Почему это утечка памяти в JavaScript? - PullRequest
5 голосов
/ 22 сентября 2011

Я читал эту статью (http://www.ibm.com/developerworks/web/library/wa-memleak/) на веб-сайте IBM об утечках памяти в JavaScript, когда натолкнулся на утечку памяти, которая не совсем выглядела как утечка:

<html>
<body>
<script type="text/javascript">
document.write("Program to illustrate memory leak via closure");
window.onload=function outerFunction(){
   var obj = document.getElementById("element");
   obj.onclick=function innerFunction(){
      alert("Hi! I will leak");
   };
   obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
   // This is used to make the leak significant
};
</script>
<button id="element">Click Me</button>
</body>
</html>

Я понял все другие утечки, но этот выделялся. Там написано, что между объектом DOM и JavaScript есть циклическая ссылка, но я этого не вижу.

Кто-нибудь может пролить свет на это?

РЕДАКТИРОВАТЬ: ссылка, кажется, была удалена (я даже обновил страницу, которую я поднял, и она была недоступна). Вот кеш Google (до тех пор, пока он длится: http://webcache.googleusercontent.com/search?q=cache:kLR-FJUeKv0J:www.ibm.com/developerworks/web/library/wa-memleak/+memory+management+in+javascript&cd=1&hl=en&ct=clnk&gl=us&client=firefox-a)

Ответы [ 2 ]

6 голосов
/ 22 сентября 2011

Присвоение onclick innerFunction создает функцию закрытия, которая сохраняет значение переменных, которые находятся в области действия innerFunction.Это позволяет коду в innerFunction ссылаться на переменные над ним и является желательной функцией javascript (чего нет в некоторых других языках).

Те переменные, которые находятся в области действия innerFunction, включают obj.Таким образом, пока есть ссылка на это замыкание, переменные в этом замыкании сохраняются.Это одноразовое использование памяти, поэтому оно не накапливается с течением времени и, следовательно, обычно не имеет значения.Но если вы поместите большие данные в одну из этих переменных, то ожидаете, что она будет освобождена, тогда «да», вы будете использовать больше памяти браузера, чем ожидали.

Где это может вызвать проблемы, так этоВ конкретном примере у вас есть циклическая ссылка между JS <==> DOM.В JS вы сохранили (в закрытии функции) ссылку на объект DOM в переменной obj.В объекте DOM вы сохранили ссылку на код JS и закрытие функции с присвоением атрибута onclick.Некоторые старые браузеры настолько глупы, что даже если вы удалите объект «элемент» из DOM, циклическая ссылка не позволит сборщику мусора когда-либо освободить память.Это не проблема в современных браузерах, поскольку они достаточно умны, чтобы видеть эту циклическую ссылку, и все же освобождают объект, если на него нет внешних ссылок.

В вашем конкретном коде вы фактически не создалиутечка, потому что элемент все еще находится в DOM.bigString - это большой кусок данных, который вы прикрепили к элементу DOM, и он будет оставаться там до тех пор, пока вы не удалите этот атрибут или не удалите объект DOM.Это не утечка, это просто хранилище.

Единственный способ, которым это может стать утечкой в ​​IE6, - это сделать следующее:

<html>
<body>
<script type="text/javascript">
document.write("Program to illustrate memory leak via closure");
window.onload=function outerFunction(){
   var obj = document.getElementById("element");
   obj.onclick=function innerFunction(){
      alert("Hi! I will leak");
   };
   obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
   // This is used to make the leak significant

};

// called later in your code
function freeObject() {
   var obj = document.getElementById("element");
   obj.parentNode.removeChild(obj);  // remove object from DOM
}

</script>
<button id="element">Click Me</button>
</body>
</html>

Теперь вы удалили объект изDOM (чуть позже в вашем коде) и, вероятно, ожидал освобождения всей памяти, связанной с ним, но циклическая ссылка предотвращает это в IE6.Вы можете обойти это, выполнив следующее:

<html>
<body>
<script type="text/javascript">
document.write("Program to illustrate memory leak via closure");
window.onload=function outerFunction(){
   var obj = document.getElementById("element");
   obj.onclick=function innerFunction(){
      alert("Hi! I will leak");
   };
   obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
   // This is used to make the leak significant

};

// called later in your code
function freeObject() {
   var obj = document.getElementById("element");
   obj.onclick=null;                 // clear handler and closure reference
   obj.parentNode.removeChild(obj);  // remove object from DOM
}
</script>
<button id="element">Click Me</button>
</body>
</html>

или, если вам не нужна ссылка obj в замыкании, вы можете полностью обнулить ссылку из замыкания следующим образом:

<html>
<body>
<script type="text/javascript">
document.write("Program to illustrate memory leak via closure");
window.onload=function outerFunction(){
   var obj = document.getElementById("element");
   obj.onclick=function innerFunction(){
      alert("Hi! I will leak");
   };
   obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
   // This is used to make the leak significant
   obj = null;   // kills the circular reference, obj will be null if you access if from innerFunction()
};
</script>
<button id="element">Click Me</button>
</body>
</html>

На практике это значимая проблема только в тех случаях, когда использование утечки памяти может быть значительным.

  1. Если у вас есть большие порции данных, которые вы храните как атрибуты DOM, то утечка одного объекта может привести к утечке большого количества данных.Обычно это решается не сохранением больших порций данных на объектах DOM.Сохраните их в JS, где вы явно управляете временем жизни.
  2. Если у вас есть долгоживущая страница с большим количеством взаимодействий javascript и операция, которая создает / уничтожает объекты DOM, может выполняться много, много раз.Например, показ слайдов, который выполняется без присмотра, может создавать / уничтожать объекты DOM снова и снова.Даже небольшая утечка памяти на элемент может в конечном итоге привести к возникновению проблемы.В написанном мной слайд-шоу я просто убедился, что не добавлял никаких пользовательских атрибутов в эти конкретные объекты DOM, которые я добавлял / удалял, и что все обработчики событий были удалены перед удалением объекта из DOM.Это должно гарантировать отсутствие циклических ссылок на них.
  3. Любая операция DOM, которую вы выполняете снова и снова в большом цикле.
0 голосов
/ 22 сентября 2011

Это приводит к утечке памяти из-за ошибки IE6.

obj ссылки innerFunction из onclick и innerFunction ссылки obj, поскольку он захватывает obj из внешней области видимости.

...