Що таке збирання сміття в JavaScript?


297

Що таке збирання сміття в JavaScript? Що важливо для веб-програміста зрозуміти про збирання сміття в JavaScript, щоб написати кращий код?



Відповіді:


192

Ерік Ліпперт трохи пізніше написав докладну публікацію в блозі на цю тему (додатково порівнявши її з VBScript ). Точніше, він писав про JScript , який є власною реалізацією Microsoft ECMAScript, хоча він дуже схожий на JavaScript. Я б міг уявити, що ви можете припустити, що переважна більшість поведінки буде однаковою для JavaScript-механізму Internet Explorer. Звичайно, реалізація буде відрізнятися від браузера до браузера, хоча я підозрюю, що ви можете взяти ряд загальних принципів і застосувати їх до інших браузерів.

Цитується на цій сторінці:

JScript використовує непромисловий збірник сміття. Це працює так:

  • Кожна змінна, яка є "за обсягом", називається "поглиначем". Скріпач може позначати число, предмет, рядок і все, що завгодно. Ми підтримуємо список відлякувачів - змінні переміщуються до списку scav, коли вони потрапляють у сферу дії, і виключаються зі списку scav, коли вони виходять із сфери застосування.

  • Раз у раз працює сміттєзбірник. Спочатку він ставить "позначку" на кожен об'єкт, змінну, рядок тощо - всю пам'ять, відстежувану GC. (JScript використовує внутрішньо структуру даних VARIANT, і в цій структурі є багато зайвих невикористаних бітів, тому ми просто встановимо один з них.)

  • По-друге, вона знімає позначку на відлякувачів та перехідне закриття посилань на смітників. Отже, якщо об'єкт "очищувач" посилається на об'єкт, що не містить котла, тоді ми очищаємо біти на нескавангері та на все, на що він посилається. (Я вживаю слово "закриття" в іншому значенні, ніж у попередній публікації.)

  • На даний момент ми знаємо, що всій пам'яті, яка все ще позначена, виділяється пам'ять, до якої неможливо досягти жодного шляху з будь-якої змінній в межах області. Усі ці об'єкти доручені зруйнувати, що знищує будь-які кругові посилання.

Основна мета збирання сміття - дозволити програмісту не турбуватися про управління пам'яттю об'єктів, які вони створюють та використовують, хоча, звичайно, цього не уникати іноді - завжди корисно мати хоча б приблизне уявлення про те, як працює сміття .

Історична примітка: попередня редакція відповіді мала неправильне посилання на deleteоператора. В JavaScript оператор видаляє властивість з об'єкта , і повністю відрізняється в C / C ++.deletedelete


27
Посібник Apple недосконалий: автор використовує deleteнеправильно; наприклад, у першому прикладі замість цього delete fooслід спочатку видалити слухач події через, window.removeEventListener()а потім використати foo = nullдля заміни змінної; в IE, delete window.foo(але ні delete foo) також працював би, якби fooбув глобальним, але навіть тоді це не було б у FF або Opera
Крістоф

3
Майте на увазі, що статтю Еріка слід розглядати "лише для історичних цілей". Але це все одно інформативно.
Петро Іван

2
Також зверніть увагу - IE 6 і 7 НЕ використовують непромисловий збірник сміття. Вони використовують простий підрахунок сміттєзбірника, який більш вразливий до кругових довідкових проблем зі збиранням сміття.
Дуг

1
ECMAScript's delete- це унарний оператор (вираз), а не оператор (тобто:) delete 0, delete 0, delete 3. Це виглядає як твердження, коли воно виражається висловом вираз.
Hydroper

Так, відповідь на даний момент застаріла, станом на 2012 рік сучасні браузери використовують алгоритм позначення / розгортання .. тому це вже не залежить від обсягу. Довідкова інформація: developer.mozilla.org/en-US/docs/Web/JavaScript/…
sksallaj

52

Остерігайтеся кругових посилань, коли задіяні об'єкти DOM:

Шаблони витоку пам'яті в JavaScript

Майте на увазі, що пам'ять може бути відтворена лише тоді, коли немає активних посилань на об'єкт. Це звичайна помилка із закриттями та обробниками подій, оскільки деякі двигуни JS не перевірять, на які змінні насправді посилаються внутрішні функції, а просто зберігатимуть усі локальні змінні функцій, що додаються.

Ось простий приклад:

function init() {
    var bigString = new Array(1000).join('xxx');
    var foo = document.getElementById('foo');
    foo.onclick = function() {
        // this might create a closure over `bigString`,
        // even if `bigString` isn't referenced anywhere!
    };
}

Наївна реалізація JS не може збиратись до bigStringтих пір, поки обробник подій є навколо. Існує кілька способів вирішити цю проблему, наприклад встановлення bigString = nullв кінці init()( deleteне працюватиме для локальних змінних та аргументів функції: deleteвидаляє властивості з об'єктів, а об'єкт змінної недоступний - ES5 у суворому режимі навіть викине значення a, ReferenceErrorякщо ви спробуєте видалити локальну змінну!).

Я рекомендую максимально уникати зайвих закриттів, якщо ви дбаєте про споживання пам’яті.


20
Помилка кругової посилання DOM характерна лише для JScript - жоден інший браузер не страждає, крім IE. Насправді я цілком впевнений, що специфікація ECMAScript прямо зазначає, що GC повинна вміти обробляти такі цикли: - /
olliej

@olliej: Я не бачу жодної згадки про GC у специфікації ECMAScript .
Янус Трольсен


16

Гарна цитата, взята з блогу

Компонент DOM є "зібраним сміттям", як і компонент JScript, а це означає, що якщо ви створите об'єкт в будь-якому з компонентів, а потім втратите слід від цього об'єкта, він з часом буде очищений.

Наприклад:

function makeABigObject() {
var bigArray = new Array(20000);
}

Коли ви викликаєте цю функцію, компонент JScript створює об'єкт (званий bigArray), доступний у межах функції. Щойно функція повертається, однак, ви «втрачаєте слід» від bigArray, тому що більше не можна посилатися на нього. Добре, що компонент JScript розуміє, що ви втратили це, і тому bigArray очищений - його пам'ять відтворена. Такий самий варіант працює в компоненті DOM. Якщо ви говорите document.createElement('div'), чи щось подібне, то компонент DOM створює для вас об’єкт. Після того, як ви якось загубите цей об’єкт, компонент DOM очистить пов'язані.


13

Наскільки мені відомо, об’єкти JavaScript періодично збирають сміття, коли на нього немає жодних посилань. Це щось відбувається автоматично, але якщо ви хочете дізнатися більше про те, як це працює, на рівні C ++, є сенс поглянути на вихідний код WebKit або V8

Як правило, не потрібно думати про це, проте в старих браузерах, як IE 5.5 та ранніх версіях IE 6, а можливо, і поточних версіях, закриття створюватиме кругові посилання, які, якщо не поставлено прапорці, в кінцевому підсумку з'їде пам'ять. У конкретному випадку, що я маю на увазі про закриття, це було тоді, коли ви додавали посилання JavaScript на об’єкт dom та об'єкт на DOM-об’єкт, який звертався до об’єкта JavaScript. В основному, його ніколи не можна збирати, і в кінцевому підсумку це призведе до того, що ОС стане нестабільною у тестових програмах, які циклично створюють збої. На практиці цих витоків зазвичай мало, але щоб зберегти чистий код, слід видалити посилання JavaScript на об’єкт DOM.

Зазвичай корисно використовувати ключове слово для видалення негайно великих посилань, таких як дані JSON, які ви отримали назад, і зробите все, що вам потрібно зробити, особливо в мобільній веб-розробці. Це призводить до наступного розгортання GC, щоб видалити цей об'єкт і звільнити його пам'ять.


Чи вирішена проблема JavaScript -> DOM -> кругової посилання JavaScript у нових версіях IE? Якщо так, то коли? Я думав, що це архітектурно дуже глибоко вниз і навряд чи коли-небудь виправиться. У вас є джерела?
erikkallen

Просто анекдотично. Я не помітив шалених витоків в IE 8, що працює в стандартному режимі, а не порушеному режимі. Я скорегую свою відповідь.
Тепло Мізер

1
@erikkallen: так, помилка GC була виправлена ​​у версіях IE 8+, оскільки старші використовували дуже наївний алгоритм збору сміття, що унеможливлювало GC пару об'єктів, що посилаються один на одного. Про це піклуються новіші mark-and-sweepалгоритми стилів .
кумархарш

6

збирання сміття (GC) - це форма автоматичного управління пам’яттю шляхом видалення об’єктів, які більше не потрібні.

будь-який процес, що займається пам'яттю, виконайте наступні дії:

1 - виділіть потрібний простір пам'яті

2 - зробити деяку обробку

3 - звільнити цей простір пам'яті

є два основних алгоритму, які використовуються для виявлення, які об'єкти більше не потрібні.

Колекція сміття з відліку посилань : цей алгоритм зменшує визначення поняття "об'єкт більше не потрібен", щоб "об'єкт не мав іншого об'єкта, на який посилається", об'єкт буде видалений, якщо на нього немає опорної точки

Алгоритм розмітки і підмітки : підключіть кожен об'єкт до джерела кореня. будь-який об’єкт не підключається до кореневого чи іншого об’єкта. цей об’єкт буде видалено.

В даний час більшість сучасних браузерів використовують другий алгоритм.


1
І щоб додати джерело цього, дивіться MDN: developer.mozilla.org/en-US/docs/Web/JavaScript/…
Xenos

4

"В інформатиці збирання сміття (GC) - це форма автоматичного управління пам'яттю. Збирач сміття, або просто колекціонер, намагається повернути сміття або пам'ять, яку використовують об'єкти, до яких програма ніколи не отримає доступ або мутує."

Усі двигуни JavaScript мають власні сміттєзбірники, і вони можуть відрізнятися. Більшість часу вам не доводиться мати справу з ними, тому що вони просто роблять те, що повинні робити.

Написання кращого коду в основному залежить від того, наскільки добре ви знаєте принципи програмування, мову та особливості реалізації.


1

Що таке збирання сміття в JavaScript?

перевірити це

Що важливо для веб-програміста зрозуміти про збирання сміття в JavaScript, щоб написати кращий код?

У Javascript ви не дбаєте про розподіл пам’яті та розсилку. Вся проблема вимагається у інтерпретатора Javascript. У Javascript все ще можливі витоки, але вони є помилками перекладача. Якщо вас цікавить ця тема, ви можете прочитати більше на www.memorymanagement.org


Яка з різних систем управління пам’яттю у статті, на яку ви посилаєтесь, є тією, яку використовує JavaScript? "У Javascript все ще можливі витоки, але вони є помилками перекладача." - Це не означає, що програмісти JS можуть просто ігнорувати всю проблему, наприклад, є досить відома кругла посилання JS <-> DOM у старіших версіях IE, яку ви могли б обійти у своєму JS-коді. Крім того, те, як працюють закриття JS, є особливістю дизайну, а не помилкою, але ви можете пов’язати більші шматки пам'яті, ніж передбачалося, якщо ви використовуєте закриття "неналежним чином" (я не кажу, що не використовуйте їх).
nnnnnn

3
Витоки пам'яті є звіром у JavaScript. Якщо ви пишете просту заявку на проект «коледж», то не хвилюйтесь. Але коли ви починаєте писати високоефективні програми для корпоративного рівня, управління пам'яттю в JavaScript є обов'язковим.
Дуг

1

У Windows ви можете використовувати Drip.exe, щоб знайти витоки пам'яті або перевірити, чи працює ваша безкоштовна пам'ять.

Це дуже просто, просто введіть URL-адресу веб-сайту, і ви побачите споживання пам'яті інтегрованого IE-рендерінга. Потім натисніть оновити, якщо пам’ять збільшується, ви виявили, що десь на веб-сторінці витік пам'яті. Але це також дуже корисно, щоб перевірити, чи працюють підпрограми для звільнення пам'яті для IE.


1

Типи посилань не зберігають об'єкт безпосередньо в змінній, якій він призначений, тому змінна об'єкта в цьому прикладі насправді не містить примірника об'єкта. Натомість він містить покажчик (або посилання) на місце в пам'яті, де існує об'єкт

var object = new Object();

якщо призначити одну змінну іншій, кожна змінна отримує копію вказівника, і обидві досі посилаються на один і той же об'єкт у пам'яті.

var object1 = new Object();
var object2 = object1;

Дві змінні, що вказують на один об’єкт

JavaScript - мова, зібрана сміттям , тому вам не потрібно турбуватися про розподіл пам’яті під час використання посилальних типів. Однак найкраще знімати об'єкти, які вам більше не потрібні, щоб сміттєзбірник міг звільнити цю пам'ять. Найкращий спосіб зробити це - встановити змінну об'єкта на null.

var object1 = new Object();
// do something
object1 = null; // dereference

Розмежування об'єктів особливо важливо у дуже великих програмах, які використовують мільйони об'єктів.

від Принципів об'єктно-орієнтованого JavaScript - NICHOLAS C. ZAKAS

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.