В этом обсуждении будет использоваться код микропифона, но, поскольку он настолько прост, я надеюсь, что он будет полезен для общего обсуждения использования mark + sweep
Micropython используетвывоз мусора, в частности разметка и подметание;давайте определим это.
Mark
Во время фазы mark , gc
следует за ссылками на память и буквально отмечает используемые блоки памяти, чтобы указать, что они достижимы из набора корневых блоков.
Sweep
После того, как фаза пометки завершена, уборщик проходит по всей куче, и если используется блок памяти, но не отмечен , помеченный , это означает, что оннедоступен кодом, поэтому он «освобожден», т. е. помечен как free .Блоки памяти, помеченные во время фазы mark , удаляют эту метку.
В текущей реализации требуется атомарный вызов для выполнения сборки мусора, также известный как gc
, но мне было интересно, если этоможно разделить его на несколько вызовов, в отличие от монолитного / атомарного вызова.
Это поможет уменьшить дрожание: вместо одного большого удара по времени у вас будет разбросана куча меньших вызовов.(Детали реализации того, как вы «разложили» вызовы gc
, здесь не обсуждаются ... если только кто-то не думает, что это добавит к обсуждению.)
Если gc
запущен "вфон "- промежуточные байтовые коды или после предварительно определенных байтовых кодов - тогда распределение (или освобождение) в неправильной точке может привести к состоянию гонки и повреждению кучи.Прежде чем мы сможем разделить выполнение gc
, мы должны определить возможные условия гонки.
Можно выполнить две операции: распределение и освобождение.
Распределение
Что может произойти, если пользователь выполняет выделение в середине фазы метки или развертки?
Давайте рассмотрим конкретный пример кода
>> var1 = SomeAllocation()
Распределение во время пометки
В приведенном выше примере оператор выполняется в REPL, поэтому любые добавления в словарь будут вноситься в глобальный словарь, который является записью в GC Roots
.Если запись добавляется в глобальные объекты до ее сканирования, ничего «плохого» не происходит: новый блок памяти будет помечен , как и должно быть.
Проблема в том, что глобальные переменные изменены1064 * после оно было отсканировано.В этом случае блок памяти не будет помечен 1067 *, поэтому на этапе развертки он будет считаться «недоступным» и освобожденным.,,даже если это не должно быть.
Распределение во время развертки
Если блок выделяется до того, как уборщик пройдет эту точку в памяти, он освободит его, потому что неиметь специальную отметку из отметки фазы.Если блок выделен после того, как уборщик прошел этот блок, ничего плохого не произойдет.
Решение
Если gc
находится в середине выполнения, пометьте выделенныйблок как помеченный .Единственным недостатком является то, что если вы выделите в фазе развертки и после того, как уборщик проверит вновь выделенный блок, вы закончите gc
блоком, помеченным mark .Если пользователь явно не освободит его, вам придется пройти дополнительный цикл gc
, чтобы освободить его, если он станет недоступным.
Но есть простое решение: если вы выделяете во время фазовой развертки, вы проверяете положение уборщика: если за ним стоит новый блок, который будет выделен, не помечайте его mark и в противном случае пометьте его mark , так как уборщик удалил бы mark .Таким образом, вы не выйдете из gc
с блоками, помеченными mark .
Deallocation
Что может произойти, если пользователь выполнит выделение в середине любой меткиили фаза развертки?
Распределение во время отметки
Если блок освобожден до сканирования ссылочного (родительского) блока, ничего не происходит.
Если блок освобожден и у него есть дочерние элементы, которые имеютуже были помечены , у нас есть несоответствие, потому что блоки должны быть помечены , только если у них есть родительский элемент, который также помечен (или родительский * GC root
),В результате эти недоступные, но помеченные блоки не будут освобождены до дополнительного цикла gc
, поскольку, будучи отмеченными помеченными , эти блоки без родителей, но помеченные не будут освобождаться по фазеSweep.
Однако я не думаю, что это проблема, потому что это не будет отличаться от случая монолитного gc
.В монолитном gc
вам необходимо завершить текущий цикл gc
, затем пользователь вызовет free(ptr)
, и тогда дочерние элементы этого блока будут освобождены в течение следующего gc
.Время, в течение которого куча находится в «правильном» состоянии, не изменится.
Распределение во время развертки
Если блок освобожден прежде чем уборщик это проверит, ничего особенного не происходит.Операция free изменяет состояние целевого блока с , отмеченного , на free и затем, когда уборщик достигает его.,,здесь ничего не видно, только свободный блок.
Если блок освобожден после того, как уборщик проверит его, операция free изменяет состояние целевого блока с используется до бесплатно .
ВОПРОС
Правильн ли мой анализ: можно ли разделить отметку + убрать сборку мусора?