diff options
Diffstat (limited to 'files/uk/web/javascript/memory_management')
| -rw-r--r-- | files/uk/web/javascript/memory_management/index.html | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/files/uk/web/javascript/memory_management/index.html b/files/uk/web/javascript/memory_management/index.html new file mode 100644 index 0000000000..0022a81129 --- /dev/null +++ b/files/uk/web/javascript/memory_management/index.html @@ -0,0 +1,204 @@ +--- +title: Керування пам'яттю +slug: Web/JavaScript/Memory_Management +tags: + - JavaScript + - Збирання сміття + - пам'ять + - продуктивність +translation_of: Web/JavaScript/Memory_Management +--- +<div>{{JsSidebar("Advanced")}}</div> + +<p>У низькорівневих мовах, наприклад C, існують примітивні функції для ручного керування пам'яттю, такі як <a href="https://pubs.opengroup.org/onlinepubs/009695399/functions/malloc.html">malloc()</a> та <a href="https://en.wikipedia.org/wiki/C_dynamic_memory_allocation#Overview_of_functions">free()</a>. В той же час JavaScript автоматично виділяє пам'ять при створенні об'єктів та звільняє її, коли вони більше не використовуються (<em>збирання сміття</em>). Ця автоматичність є потенційним джерелом плутанини: вона може дати розробникам хибне враження, що їм не потрібно хвилюватись щодо керування пам'яттю.</p> + +<h2 id="Життєвий_цикл_памяті">Життєвий цикл пам'яті</h2> + +<p>Незалежно від мови програмування, життєвий цикл пам'яті завжди приблизно той самий:</p> + +<ol> + <li>Виділення потрібної пам'яті</li> + <li>Використання виділеної пам'яті (читання, запис)</li> + <li>Вивільнення виділеної пам'яті, коли вона більше не потрібна</li> +</ol> + +<p>Друга частина в усіх мовах є явною. Перша та остання частини є явними у низькорівневих мовах, але, як правило, неявні у мовах високого рівня, таких як JavaScript.</p> + +<h3 id="Виділення_памяті_у_JavaScript">Виділення пам'яті у JavaScript</h3> + +<h4 id="Ініціалізація_значень">Ініціалізація значень</h4> + +<p>Щоб не турбувати розробника виділеннями пам'яті, JavaScript автоматично виділяє пам'ять, коли оголошуються початкові значення.</p> + +<pre class="brush: js">var n = 123; // виділяє пам'ять для числа +var s = 'блабла'; // виділяє пам'ять для рядка + +var o = { + a: 1, + b: null +}; // виділяє пам'ять для об'єкта та значень, що він містить + +// (як з об'єктом) виділяє пам'ять для масиву та +// значень, що він містить +var a = [1, null, 'аовл']; + +function f(a) { + return a + 2; +} // виділяє пам'ять для функції (об'єкт, який можна викликати) + +// функціональні вирази також виділяють пам'ять для об'єкта +someElement.addEventListener('click', function() { + someElement.style.backgroundColor = 'blue'; +}, false); +</pre> + +<h4 id="Виділення_памяті_через_виклики_функцій">Виділення пам'яті через виклики функцій</h4> + +<p>Виклики деяких функцій виділяють пам'ять під об'єкт.</p> + +<pre class="brush: js">var d = new Date(); // виділяє пам'ять під об'єкт Date + +var e = document.createElement('div'); // видділяє пам'ять під елемент DOM</pre> + +<p>Деякі методи виділяють пам'ять для нових значень чи об'єктів:</p> + +<pre class="brush: js">var s = 'йцуке'; +var s2 = s.substr(0, 3); // s2 - новий рядок +// Оскільки рядки є незмінними, +// JavaScript може вирішити не виділяти пам'ять, +// а лише зберегти діапазон [0, 3]. + +var a = ['ouais ouais', 'nan nan']; +var a2 = ['generation', 'nan nan']; +var a3 = a.concat(a2); +// новий масив з 4 елементів, що є +// поєданням елементів a та a2. +</pre> + +<h3 id="Використання_значень">Використання значень</h3> + +<p>Використання значень, по суті, означає читання та запис у виділеній пам'яті. Це можна зробити зчитуванням та записом значення змінної чи властивості об'єкта, чи навіть передачею аргументу у функцію.</p> + +<h3 id="Звільнення_памяті_коли_вона_більше_не_потрібна">Звільнення пам'яті, коли вона більше не потрібна</h3> + +<p>Більшість проблем керування пам'яттю виникають на цій стадії. Найскладніший аспект цього етапу полягає у визначенні, коли виділена пам'ять більше не потрібна. </p> + +<p>Низькорівневі мови вимагають, щоб розробник вручну визначав, у якій точці програми виділена пам'ять більше не потрібна, та звільняв її.</p> + +<p>Деякі високорівневі мови, такі як JavaScript, використовують форму автоматичного керування пам'яттю, відому як <a href="https://uk.wikipedia.org/wiki/%D0%97%D0%B1%D0%B8%D1%80%D0%B0%D0%BD%D0%BD%D1%8F_%D1%81%D0%BC%D1%96%D1%82%D1%82%D1%8F">збирання сміття</a> (ЗС). Збирач сміття відслідковує виділення пам'яті та визначає момент, коли блок виділеної пам'яті більше не потрібний, і повертає його. Цей автоматичний процес є неточним, оскільки загальна проблема визначення, чи потрібна наразі та чи інша ділянка пам'яті, є <a href="https://uk.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%96%D1%87%D0%BD%D0%B0_%D1%80%D0%BE%D0%B7%D0%B2%27%D1%8F%D0%B7%D0%BD%D1%96%D1%81%D1%82%D1%8C">нерозв'язною</a>.</p> + +<h2 id="Збирання_сміття">Збирання сміття</h2> + +<p>Як було зазначено вище, загальна проблема автоматичного визначення того, що якась ділянка пам'яті "більше не потрібна", є нерозв'язною. Як наслідок, збирачі сміття впроваджують обмеження вирішення загальної проблеми. Цей розділ пояснює концепції, необхідні для розуміння головних алгоритмів збирання сміття та відповідних обмежень.</p> + +<h3 id="Посилання">Посилання</h3> + +<p>Головна концепція, на яку покладаються алгоритми збирачів сміття - це концепція <em>посилань</em>. У контексті керування пам'яттю об'єкт посилається на інший об'єкт, якщо звертається до нього (явно чи неявно). Наприклад, об'єкт JavaScript має посилання на свій <a href="/uk/docs/Web/JavaScript/Inheritance_and_the_prototype_chain">прототип</a> (неявне посилання) та на значення своїх властивостей (явне посилання).</p> + +<p>У цьому контексті поняття "об'єкта" є ширшим, ніж звичайні об'єкти JavaScript, і містить також області видимості функцій (або глобальну область видимості).</p> + +<h3 id="Збирання_сміття_через_підрахунок_посилань">Збирання сміття через підрахунок посилань</h3> + +<p>Це найпримітивніший алгоритм збирання сміття. Цей алгоритм звужує проблему з визначення того, чи об'єкт досі є потрібним, до визначення того, чи мають інші об'єкти досі посилання на даний об'єкт. Об'єкт називається "сміттям", або підлягає збиранню, якщо на нього вказує нуль посилань.</p> + +<h4 id="Приклад">Приклад</h4> + +<pre class="brush: js">var x = { + a: { + b: 2 + } +}; +// Створено 2 об'єкта. Один має посилання у іншому як одна з його властивостей. +// Інший має посилання через присвоєння його змінній 'x'. +// Зрозуміло, що жоден з них не може бути прибраний. + + +var y = x; // Змінна 'y' - друга сутність, що має посилання на об'єкт. + +x = 1; // Тепер об'єкт, що початково був присвоєний 'x', має унікальне посилання, + // втілене у змінній 'y'. + +var z = y.a; // Посилання на властивість 'a' об'єкта. + // Цей об'єкт тепер має 2 посилання: одне у вигляді властивості, + // інше у вигляді змінної 'z'. + +y = 'mozilla'; // Об'єкт, що початково був присвоєний 'x', тепер має нуль посилань. + // Він може бути прибраний. + // Однак, на його властивість 'a' досі посилається + // змінна 'z', тому її не можна вивільнити. + +z = null; // Властивість 'a' об'єкта, що початково був присвоєний x, + // має нуль посилань. Об'єкт тепер може бути прибраний. +</pre> + +<h4 id="Обмеження_Циклічні_посилання">Обмеження: Циклічні посилання</h4> + +<p>Існує обмеження у випадку циклічних посилань. У наступному прикладі два об'єкти створені з властивостями, в яких вони посилаются один на одного, таким чином створюючи цикл. Вони вийдуть за межі області видимості, коли завершиться виклик функції. В цей момент вони стають непотрібними, і їхня виділена пам'ять має бути повернена. Однак, алгоритм, що підраховує посилання, не вважатиме їх готовими для повернення, оскільки кожен з двох об'єктів має принаймні одне посилання, що вказує на нього, в результаті жоден з них не позначається для збирання сміття. Циклічні посилання є типовою причиною витоків пам'яті.</p> + +<pre class="brush: js">function f() { + var x = {}; + var y = {}; + x.a = y; // x посилається на y + y.a = x; // y посилається на x + + return 'йцуке'; +} + +f(); +</pre> + +<h4 id="Приклад_з_реального_життя">Приклад з реального життя</h4> + +<p>Internet Explorer 6 та 7 відомі тим, що їхні збирачі сміття для об'єктів DOM працюють на основі підрахунку посилань. Цикли є типовою помилкою, що призводить до витоків пам'яті:</p> + +<pre class="brush: js">var div; +window.onload = function() { + div = document.getElementById('myDivElement'); + div.circularReference = div; + div.lotsOfData = new Array(10000).join('*'); +}; +</pre> + +<p>У наведеному прикладі DOM-елемент "myDivElement" має циклічне посилання на самого себе у властивості "circularReference". Якщо ця властивість не буде явно видалена чи обнулена, збирач сміття, що підраховує посилання, завжди матиме принаймні одне наявне посилання і триматиме DOM-елемент у пам'яті, навіть якщо він був видалений з DOM-дерева. Якщо DOM-елемент містить великий об'єм даних (проілюстровано у наведеному прикладі властивістю "lotsOfData"), пам'ять, зайнята цими даними, ніколи не буде вивільнена, що може призвести до проблем, пов'язаних з пам'яттю, наприклад, переглядач ставатиме все повільнішим.</p> + +<h3 id="Алгоритм_маркування_та_прибирання_mark-and-sweep">Алгоритм маркування та прибирання (mark-and-sweep)</h3> + +<p>Цей алгоритм звужує визначення "об'єкта, який більше не потрібен" до "недосяжного об'єкта".</p> + +<p>Даний алгоритм використовує набір об'єктів, що називаються <em>коренями. </em>У JavaScript корінь є глобальним об'єктом. Періодично збирач сміття починатиме від цих коренів, знаходитиме усі об'єкти, що мають посилання у коренях, далі усі об'єкти, що мають посилання у них, і т. д. Таким чином, починаючи від коренів, збирач сміття знайде усі <em>досяжні</em> об'єкти та збере усі недосяжні об'єкти.</p> + +<p>Цей алгоритм є покращенням попереднього, оскільки об'єкт, що має нуль посилань, є об'єктивно недосяжним. Протилежне не є істиною, як ми бачили на прикладі циклічних посилань.</p> + +<p>Станом на 2012 рік усі сучасні переглядачі використовують збирачі сміття з алгоритмом маркування та прибирання. Усі покращення в галузі збирання сміття у JavaScript (генераційне/інкрементне/конкурентне/паралельне збирання сміття) за останні кілька років є покращеннями реалізації даного алгоритму, а не покращеннями самого алгоритму збирання сміття чи скороченням його визначення моменту, коли "об'єкт більше не потрібен".</p> + +<h4 id="Циклічні_посилання_більше_не_є_проблемою">Циклічні посилання більше не є проблемою</h4> + +<p>У наведеному вище прикладі, коли завершується виклик функції, два об'єкти більше не мають посилань з жодного ресурсу, досяжного з глобального об'єкта. Як наслідок, збирач сміття визнає їх недосяжними та поверне виділену під них пам'ять.</p> + +<h4 id="Обмеження_Ручне_вивільнення_памяті">Обмеження: Ручне вивільнення пам'яті</h4> + +<p>Бувають випадки, коли було б зручно власноруч визначити, коли й яка пам'ять буде звільнена. Щоб звільнити пам'ять об'єкта, його треба зробити явно недосяжним.</p> + +<p>Станом на 2019 рік немає можливості явно чи програмно запустити збирання сміття у JavaScript.</p> + +<h2 id="Node.js">Node.js</h2> + +<p>Node.js пропонує додаткові можливості та інструменти для конфігурування та відлагодження проблем з пам'яттю, які можуть бути недоступні для JavaScript, що виконується у середовищі переглядача.</p> + +<h4 id="Прапори_рушія_V8">Прапори рушія V8</h4> + +<p>Максимальний розмір доступної пам'яті купи може бути збільшений за допомогою прапору:</p> + +<p><code>node --<em>max-old-space-size=6000</em> index.js</code></p> + +<p>Ми також можемо викликати збирач сміття для відлагодження проблем з пам'яттю, використовуючи прапор та <a href="https://nodejs.org/uk/docs/guides/debugging-getting-started/">Chrome Debugger</a>:</p> + +<pre class="brush: bash">node --expose-gc --inspect index.js</pre> + +<h4 id="Див._також">Див. також</h4> + +<ul> + <li><a class="external" href="http://www.ibm.com/developerworks/web/library/wa-memleak/">Стаття IBM про "Шаблони витоків пам'яті у JavaScript" (2007)</a></li> + <li><a class="external" href="http://msdn.microsoft.com/en-us/magazine/ff728624.aspx">Стаття Kangax про те, як зареєструвати обробник подій та запобігти витокам пам'яті (2010)</a></li> + <li><a href="/uk/docs/Mozilla/Performance" title="https://developer.mozilla.org/en-US/docs/Mozilla/Performance">Продуктивність</a></li> +</ul> |
