Понимание алгоритма разметки и развертки в JavaScript - PullRequest
0 голосов
/ 22 февраля 2020

Согласно MDN , Mark and Sweep является лучшим алгоритмом для сбора мусора, чем подсчет ссылок, так как он предотвращает утечки памяти, которые могут возникать при циклических c ссылках. Тем не менее, я не понимаю, почему это так? Может кто-нибудь объяснить (с помощью кода), как Mark и Sweep избегают таких утечек памяти? Не стесняйтесь использовать приведенный ниже код из MDN, который объясняет, как работает подсчет ссылок:

var x = { 
  a: {
    b: 2
  }
}; 

// 2 objects are created. One is referenced by the other as one of its properties.
// The other is referenced by virtue of being assigned to the 'x' variable.
// Obviously, none can be garbage-collected.

var y = x;      // The 'y' variable is the second thing that has a reference to the object.

x = 1;          // Now, the object that was originally in 'x' has a unique reference
                //   embodied by the 'y' variable.

var z = y.a;    // Reference to 'a' property of the object.
                //   This object now has 2 references: one as a property, 
                //   the other as the 'z' variable.

y = 'mozilla';  // The object that was originally in 'x' has now zero
                //   references to it. It can be garbage-collected.
                //   However its 'a' property is still referenced by 
                //   the 'z' variable, so it cannot be freed.

z = null;       // The 'a' property of the object originally in x 
                //   has zero references to it. It can be garbage collected.

Кроме того, глобальные переменные никогда не собираются мусором?

1 Ответ

1 голос
/ 22 февраля 2020

Разметка работает, помечая корни, такие как временные переменные (в текущем методе) и глобальные (stati c) переменные, а затем сметая кучу, удаляя любые неотмеченные ссылки

Например, первая Строка в вашем примере создает вложенный объект и присваивает его переменной x. Для MS, чтобы знать, является ли ее поле b живым или нет, он сначала отмечает корни.

Это работает, проверяя все глобальные переменные и локальные переменные в текущем методе (фрейме) и затем маркируя ссылки, на которые они указывают. Например, одним из них может быть внешний объект x.

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

var x = { a: {b: 2}}; 

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

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

Теперь проблема циклических ссылок c состоит в том, что объект a ссылается на другой объект b и наоборот. При подсчете ссылок это тот случай, когда объект имеет ссылку на себя, его поле счетчика ссылок будет по крайней мере 1. Проблема в том, что ни у root объекта, ни у объекта, на который косвенно ссылается root (он же есть объект) относится к объекту cycli c, но согласно алгоритму счетчик ссылок, равный или превышающий 1, означает, что он жив.

Определение живого объекта у Mark-sweep отличается, а именно то, что на него прямо или косвенно ссылается root объект.

...