Требования к собеседованию: механизм управления памятью Python

Python

Что такое менеджер памяти (что)

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

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

Пул памяти Python

Зачем вводить пул памяти (почему)

При создании большого количества объектов, потребляющих мало памяти, частые вызовы new/malloc вызовут сильную фрагментацию памяти, что приведет к снижению эффективности. Функция пула памяти заключается в том, чтобы предварительно применить определенное количество блоков памяти одинакового размера в памяти и зарезервировать его в качестве резервной копии.При появлении новой потребности в памяти он сначала выделяет память из пула памяти для этой потребности, а потом подавать заявку на новую память, когда ее не хватает. . Наиболее значительным преимуществом этого является то, что он может уменьшить фрагментацию памяти и повысить эффективность.

Механизм управления памятью в питоне — Pymalloc.

Пул памяти, если он работает (как)

Во-первых, давайте посмотрим на диаграмму архитектуры памяти CPython (интерпретатор Python):

  • Управление объектом Python в основном на уровне + 1 ~ уровень + 3 слоя
  • Уровень уровня +3: существует независимый частный пул памяти для встроенных объектов python (таких как int, dict и т. д.), и пул памяти между объектами не является общим, то есть память, освобожденная int, не будет выделено для плавания.
  • Уровень + 2: когда запрошенный размер памяти меньше 256 КБ, выделение памяти в основном реализуется с помощью распределителя объектов Python.
  • Слой уровня +1: когда запрошенный размер памяти превышает 256 КБ, она выделяется собственным распределителем памяти Python, который, по сути, вызывает malloc/realloc и другие функции из стандартной библиотеки C.

Что касается освобождения памяти, когда счетчик ссылок на объект достигает 0, Python вызывает его деструктор. Вызов деструктора не означает, что в конце концов будет вызвана функция free для освобождения пространства памяти.Если это так, то частое применение и освобождение пространства памяти значительно снизит эффективность выполнения Python. Поэтому механизм пула памяти также используется при деструктуризации, и память, запрошенная из пула памяти, будет возвращена в пул памяти, чтобы избежать частых действий по применению и освобождению.

механизм сбора мусора

Механизм сборки мусора Python использует механизм подсчета ссылок в качестве основной стратегии, дополненный механизмами очистки пометок и сбора по поколениям. Среди них механизм пометки-очистки используется для решения проблемы, связанной с тем, что циклическая ссылка, вызванная подсчитанной ссылкой, не может освободить память, а механизм рециркуляции поколений предназначен для повышения эффективности сборки мусора.

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

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

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

>>> a=[1,2]
>>> import sys
>>> sys.getrefcount(a)  ## 获取对象a的引用次数
2
>>> b=a
>>> sys.getrefcount(a)
3
>>> del b  ## 删除b的引用
>>> sys.getrefcount(a)
2
>>> c=list()
>>> c.append(a) ## 加入到容器中
>>> sys.getrefcount(a)
3
>>> del c  ## 删除容器,引用-1
>>> sys.getrefcount(a)
2
>>> b=a
>>> sys.getrefcount(a)
3
>>> a=[3,4]  ## 重新赋值
>>> sys.getrefcount(a)
2

Примечание. Когда a передается в качестве параметра для getrefcount, создается временная ссылка, поэтому результат на +1 больше, чем в реальном случае.

  • Когда счетчик ссылок увеличивается:
  1. Объекту присваивается новое имя (например: a=[1,2])
  2. Поместите его в контейнер (например, список, кортеж или словарь) (например: c.append(a))
  • Когда ссылочный счетчик уменьшается:
  1. Используйте оператор del для явного уничтожения псевдонима объекта (например, del b )
  2. Контейнер, в котором находится объект, уничтожается или объект удаляется из контейнера (например: del c )
  3. Ссылка выходит за рамки или переназначается (например: a=[3,4])

Счетчик ссылок может решить большинство проблем со сборкой мусора, но когда два объекта ссылаются друг на друга, оператор del может уменьшить количество ссылок, но счетчик ссылок не вернется к 0, и объект не будет уничтожен, что приведет к проблема с утечкой памяти.В ответ на эту ситуацию Python вводит механизм маркировки-очистки..

отметка-ясно

Mark-sweep используется для решения проблемы циклических ссылок, генерируемых механизмом подсчета ссылок, что приводит к утечкам памяти. Циклические ссылки встречаются только в объектах-контейнерах, таких как словари, кортежи, списки и т. д.

Как следует из названия, этот механизм во время сборки мусора делится на два этапа, а именно:

  • На этапе маркировки обойдите все объекты.Если он достижим, то есть есть объекты, ссылающиеся на него, то пометьте объект как достижимый.
  • На этапе очистки снова пройдитесь по объекту.Если объект не отмечен как достижимый (то есть недостижимый), он будет переработан.
>>> a=[1,2]
>>> b=[3,4]
>>> sys.getrefcount(a)
2
>>> sys.getrefcount(b)
2
>>> a.append(b)
>>> sys.getrefcount(b)
3
>>> b.append(a)
>>> sys.getrefcount(a)
3
>>> del a
>>> del b
  • a ссылается на b, а b ссылается на a. В это время на каждый из двух объектов ссылаются дважды (удалите временную ссылку getrefcout())

  • После выполнения del время ссылки объектов a и b равно -1.В это время все их соответствующие счетчики ссылок равны 1, и они попали в циклическую ссылку.

  • Отметить: найти один конец a, потому что он имеет ссылку на b, затем подсчитать ссылку b на -1

  • Отметить: Следуйте ссылке на b, b имеет ссылку на a, подсчитайте ссылку a -1, в это время все ссылки времени объектов a и b равны 0, которые помечены как недостижимые (Unreachable)

  • Очистить: объект, помеченный как недостижимый, — это объект, который действительно нужно освободить.

Во время фазы сборки мусора, описанной выше, все приложение будет приостановлено, и приложение будет возобновлено после ожидания очистки метки.Чтобы сократить время, когда приложение приостанавливается, Python повышает эффективность сборки мусора за счет «Коллекции поколений» (Generational Collection), которая обменивает пространство на время.

Переработка поколений

Повторение поколений основано на том статистическом факте, что для программы определенный процент блоков памяти имеет относительно короткий жизненный цикл, в то время как остальные блоки памяти имеют относительно длительный жизненный цикл, даже от начала программы до конца программы. программа. Доля короткоживущих субъектов обычно составляет от 80% до 90%. Так что просто подумайте: чем дольше объект существует, тем больше вероятность, что он не мусор и его нужно собирать реже. Таким образом, количество пройденных объектов может быть эффективно уменьшено при выполнении алгоритма маркировки-очистки, тем самым повышая скорость сборки мусора.Стратегия — это метод пространства во времени..

Python делит все объекты на три поколения: молодое поколение (поколение 0), среднее поколение (поколение 1) и старое поколение (поколение 2). Все вновь созданные объекты по умолчанию относятся к объектам поколения 0. Объекты, пережившие сканирование сборщика мусора поколения 0, будут перемещены в поколение 1, а объекты, пережившие сканирование сборщика мусора поколения 1, будут перемещены в поколение 2.

Количество сканирований gc (Поколение 0 > Поколение 1 > Поколение 2)

Когда разница между выделенными и освобожденными объектами в поколении достигает определенного порога, запускается сканирование gc текущего поколения. При сканировании поколения будет сканироваться и более молодое поколение, поэтому при сканировании GC второго поколения также будет происходить сканирование GC 0-го и 1-го поколений, то есть сканирование полного поколения.

>>> import gc 
>>> gc.get_threshold() ## 分代回收机制的参数阈值设置
(700, 10, 10)
  • 700 = количество вновь выделенных объектов - количество освобожденных объектов, запущено сканирование gc поколения 0
  • Первые 10: сканирование gc 0-го поколения выполняется 10 раз, затем запускается сканирование gc 1-го поколения.
  • Вторые 10: сканирование gc первого поколения происходит 10 раз, затем запускается сканирование gc второго поколения

считать

Что происходит в операции пометки-зачистки после операции del, если объект c также ссылается на a?

Ссылочное отношение объектов a, b и c показано на следующем рисунке:

>>> a=[1,2]
>>> b=[3,4]
>>> c=a
>>> a.append(b)
>>> b.append(a)

  • ref_count означает количество ссылок
  • Все объекты a, b, c достижимы

После выполнения del ссылочное отношение, как показано ниже:

>>> del a
>>> del b

  • a, b, c равно 1 минус ref_count

выполнить gc-сканирование

  • Пометка: a относится к b, уменьшает ref_count b на 1 до 0, b ссылается на a, уменьшает ref_count a на 1 до 1, помещает b в категорию недостижимых

  • Рециркуляция: поскольку a достижим, он будет рекурсивно помечать все узлы, достижимые из узла a, как достижимые, то есть:

  • Очистить: нет очищаемых объектов в категории недоступных, поэтому объекты a, b, c не будут очищаться.

Суммировать

В целом, Python использует пулы памяти, чтобы уменьшить фрагментацию памяти и повысить эффективность выполнения. Он в основном завершает сборку сбора мусора посредством ссылки, решает проблему, вызванную циркулярной ссылкой на объекты контейнеров через отметку, и улучшает эффективность сборки мусора через схему поколения.

Добро пожаловать всех [внимание] Я, мы учимся прогрессируем вместе.

Кроме того, если у вас есть какие-либо вопросы, пожалуйста, обсуждайте их в области комментариев и активно общайтесь.

Если эта статья была вам полезна, не забудьте [Нравится], [Подборка] и отказывайтесь связываться с вечеринкой!