Сборка мусора

Введение

Примеры

  • 2

    Подсчет ссылок

    Подавляющее большинство управления памятью Python выполняется с подсчетом ссылок.

    Каждый раз, когда на объект ссылаются (например, назначают на переменную), его счетчик ссылок автоматически увеличивается. При разыменовании (например, переменная выходит из области видимости) ее счетчик ссылок автоматически уменьшается.

    Когда счетчик ссылок достигает нуля, объект немедленно уничтожается и память сразу же освобождается. Таким образом, в большинстве случаев сборщик мусора даже не нужен.

     >>> import gc; gc.disable()  # disable garbage collector
    >>> class Track:
            def __init__(self):
                print("Initialized")
            def __del__(self):
                print("Destructed")
    >>> def foo():
            Track()
            # destructed immediately since no longer has any references
            print("---")
            t = Track()
            # variable is referenced, so it's not destructed yet
            print("---")
            # variable is destructed when function exits
    >>> foo()
    Initialized
    Destructed
    ---
    Initialized
    ---
    Destructed 

    Для дальнейшей демонстрации концепции ссылок:

     >>> def bar():
            return Track()
    >>> t = bar()
    Initialized
    >>> another_t = t  # assign another reference
    >>> print("...")
    ...
    >>> t = None          # not destructed yet - another_t still refers to it
    >>> another_t = None  # final reference gone, object is destructed
    Destructed 
  • 2

    Сборщик мусора для эталонных циклов

    Единственный раз , когда требуется сборщик мусора , если у вас есть ссылочный цикл. Простейшим примером эталонного цикла является тот, в котором A относится к B, а B относится к A, в то время как ничто иное не относится ни к A, ни к B. Ни A, ни B не доступны из любой точки программы, поэтому их можно безопасно уничтожить, все же их счетчики ссылок равны 1, и поэтому они не могут быть освобождены одним алгоритмом подсчета ссылок.

     >>> import gc; gc.disable()  # disable garbage collector
    >>> class Track:
            def __init__(self):
                print("Initialized")
            def __del__(self):
                print("Destructed")
    >>> A = Track()
    Initialized
    >>> B = Track()
    Initialized
    >>> A.other = B
    >>> B.other = A
    >>> del A; del B  # objects are not destructed due to reference cycle
    >>> gc.collect()  # trigger collection
    Destructed
    Destructed
    4 

    Референтный цикл может быть произвольно длинным. Если A указывает на B указывает на C указывает на ... указывает на Z, который указывает на A, то ни один от A до Z не будет собираться до фазы сбора мусора:

     >>> objs = [Track() for _ in range(10)]
    Initialized
    Initialized
    Initialized
    Initialized
    Initialized
    Initialized
    Initialized
    Initialized
    Initialized
    Initialized
    >>> for i in range(len(objs)-1):
    ...     objs[i].other = objs[i + 1]
    ...
    >>> objs[-1].other = objs[0]  # complete the cycle
    >>> del objs                  # no one can refer to objs now - still not destructed
    >>> gc.collect()
    Destructed
    Destructed
    Destructed
    Destructed
    Destructed
    Destructed
    Destructed
    Destructed
    Destructed
    Destructed
    20 
  • 6

    Эффекты команды del

    Удаление имени переменной из сферы , используя del v , или удаление объекта из коллекции с помощью del v[item] или del[i:j] , или удаление атрибута с помощью del v.name , или любой другой способ удаления ссылок на объект, не вызывает какую - либо деструктор вызовы или любая память освобождения и сам по себе. Объекты уничтожаются только тогда, когда их счетчик ссылок достигает нуля.

     >>> import gc
    >>> gc.disable()  # disable garbage collector
    >>> class Track:
            def __init__(self):
                print("Initialized")
            def __del__(self):
                print("Destructed")
    >>> def bar():
        return Track()
    >>> t = bar()
    Initialized
    >>> another_t = t  # assign another reference
    >>> print("...")
    ...
    >>> del t          # not destructed yet - another_t still refers to it
    >>> del another_t  # final reference gone, object is destructed
    Destructed 
  • 7

    Повторное использование примитивных объектов

    Интересная вещь, которая может помочь оптимизировать ваши приложения, заключается в том, что примитивы на самом деле также пересчитываются под капотом. Давайте посмотрим на цифры; для всех целых чисел от -5 до 256 Python всегда использует один и тот же объект:

     >>> import sys
    >>> sys.getrefcount(1)
    797
    >>> a = 1
    >>> b = 1
    >>> sys.getrefcount(1)
    799
    
     

    Обратите внимание , что RefCount возрастает, а это означает , что и a b ссылаться на тот же объект , лежащим в основе , когда они относятся к 1 примитиву. Однако для больших чисел Python фактически не использует базовый объект:

     >>> a = 999999999
    >>> sys.getrefcount(999999999)
    3
    >>> b = 999999999
    >>> sys.getrefcount(999999999)
    3
    
     

    Поскольку RefCount для 999999999 не изменяется при назначении его a и b мы можем сделать вывод , что они относятся к двум различным базовым объектам, несмотря на то, что они оба назначены тому же примитивно.

  • 1

    Просмотр отсчета объекта

     >>> import sys
    >>> a = object()
    >>> sys.getrefcount(a)
    2
    >>> b = a
    >>> sys.getrefcount(a)
    3
    >>> del b
    >>> sys.getrefcount(a)
    2 
  • 1

    Насильственное освобождение объектов

    Вы можете принудительно освободить объекты, даже если их refcount не равен 0 в Python 2 и 3.

    Обе версии используют ctypes модуль , чтобы сделать это.

    ВНИМАНИЕ: делать это оставит вашу среду Python неустойчивы и склонны к врезаться без TRACEBACK! Использование этого метода также может создать проблемы с безопасностью (весьма маловероятно). Только освобождать объекты, которые, вы уверены, вы никогда больше не будете ссылаться. Когда-либо.

    import ctypes deallocated = 12345 ctypes.pythonapi._Py_Dealloc (ctypes.py_object (deallocated)) импорт ctypes, sys deallocated = 12345 (ctypes.c_char * sys.getsizeof (deallocated)). from_address (id (deallocated)) [: 4] = '\ x00' * 4

    После запуска любая ссылка на теперь освобожденный объект приведет к тому, что Python вызовет неопределенное поведение или произойдет сбой - без обратной трассировки. Вероятно, была причина, почему сборщик мусора не удалил этот объект ...

    Если вы не освободить None , Вы получаете специальное сообщение - Fatal Python error: deallocating None , прежде чем врезаться.

  • 0

    Управление сборкой мусора

    Есть два подхода к влиянию, когда выполняется очистка памяти. Они влияют на то, как часто выполняется автоматический процесс, а другой вручную запускает очистку.

    Сборщиком мусора можно управлять, настраивая пороги сбора, которые влияют на частоту, с которой работает сборщик. Python использует систему управления памятью на основе поколения. Новые объекты сохраняются в новейшей генерации - generation0 и с каждыми выжили коллекции, объекты продвинут на старшие поколения. Не Достигнув последнего поколения - generation2, они больше не способствовали.

    Пороговые значения можно изменить с помощью следующего фрагмента:

     import gc
    gc.set_threshold(1000, 100, 10) # Values are just for demonstration purpose
    
     

    Первый аргумент представляет собой пороговое значение для сбора generation0. Каждый раз , когда число отчислений превышает число deallocations на 1000 сборщик мусора будет называться.

    Старшие поколения не очищаются при каждом запуске, чтобы оптимизировать процесс. Вторые и третьи аргументы не являются обязательными и контролировать , как часто моют старшие поколения. Если generation0 был обработан 100 раз без очистки generation1, то generation1 будет обработано. Аналогичным образом , объекты в generation2 будут обрабатываться только тогда , когда те , в generation1 были очищены в 10 раз , не касаясь generation2.

    Один экземпляр , в котором установка вручную пороги полезно, когда программа выделяет много мелких объектов без deallocating их , что приводит к сборщик мусора работает слишком часто (каждый generation0_threshold объект распределения). Несмотря на то, что сборщик работает довольно быстро, при работе с огромным количеством объектов возникает проблема с производительностью. Во всяком случае, нет единого размера, подходящего для любой стратегии выбора пороговых значений, и это зависит от варианта использования.

    Ручной запуск коллекции можно выполнить, как показано в следующем фрагменте:

     import gc
    gc.collect()
    
     

    Сборка мусора запускается автоматически в зависимости от количества выделений и освобождений, а не от используемой или доступной памяти. Следовательно, при работе с большими объектами память может быть исчерпана до запуска автоматической очистки. Это хороший пример использования для вызова сборщика мусора вручную.

    Хотя это возможно, это не поощряемая практика. Избегать утечек памяти - лучший вариант. В любом случае, в больших проектах обнаружение утечки памяти может быть сложной задачей, и запуск сборки мусора вручную можно использовать как быстрое решение до дальнейшей отладки.

    Для долго выполняющихся программ сборка мусора может запускаться по времени или по событию. Примером первого является веб-сервер, который запускает коллекцию после фиксированного числа запросов. В последующем - веб-сервер, который запускает сборку мусора при получении запроса определенного типа.

  • 0

    Не ждите, чтобы очистить сборку мусора

    Тот факт, что сборщик мусора будет очищен, не означает, что вы должны ждать очистки цикла сбора мусора.

    В частности, вам не следует ждать, пока сборщик мусора закроет дескрипторы файлов, соединения с базой данных и откроет сетевые соединения.

    например:

    В следующем коде предполагается, что файл будет закрыт в следующем цикле сборки мусора, если f была последней ссылкой на файл.

     >>> f = open("test.txt")
    >>> del f
    
    
     

    Более явный способ очистки заключается в вызове f.close() . Вы можете сделать это еще более элегантным, то есть, используя with заявлением, также известный как менеджер контекста :

     >>> with open("test.txt") as f:
    ...     pass
    ...     # do something with f
    >>> #now the f object still exists, but it is closed
    
     

    with утверждением позволяет отступа кода под открытым файлом. Это делает явным и легче видеть, как долго файл остается открытым. Он также всегда закрывает файл, даже если исключение возникает в while блок.

Синтаксис

Параметры

Примечания