Код про утечки памяти?

Читаю статью на хабре , не понимаю 3.Утечки памяти ссылки на несуществующие объекты. Не знаю, что со мной не так, но хоть убейте, но уже долго никак не могу переварить именно текст, объясняющий этот код. Может кто объяснит по-проще, ибо уже жутко интересно, но жутко непонятно.
var theThing = null;
var replaceThing = function () {
    var priorThing = theThing;
    var unused = function () {
        // 'unused' - единственное место, где используется 'priorThing',
        // но 'unused' никогда не вызывается
        if (priorThing) {
            console.log("hi");
        }
    };
    theThing = {
        longStr: new Array(1000000).join('*'),  // создаем 1Mб объект
        someMethod: function () {
            console.log(someMessage);
        }
    };
};
setInterval(replaceThing, 1000);    // вызываем 'replaceThing' каждую секунду



Каждый объект theThing содержит свой собственный объект longStr размером 1Мб. Каждую секунду при вызове replaceThing, функция сохраняет ссылку на предыдущий объект theThing в переменной priorThing. Это не проблема, ведь каждый раз предыдущая ссылка priorThing будет перетерта (priorThing = theThing;). Так в чём же причина утечки?

Типичный способ реализации замыкания — это создание связи между каждым объектом-функцией и объектом-словарем, представляющим собой лексическую область видимости для этой функции. Если обе функции (unused и someMethod), определенные внутри replaceThing, реально используют priorThing, то важно понимать, что они получают один и тот же объект, даже если priorThing переписывается снова и снова, так как обе функции используют одну и ту же лексическую область видимости. И как только переменная используется в любом из замыканий, то она попадает в лексическую область видимости, используемую всеми замыканиями в этой области видимости. И этот маленький нюанс приводит к мощной утечке памяти.
  • Вопрос задан
  • 787 просмотров
Пригласить эксперта
Ответы на вопрос 4
@beh
Разработчик python, javascript
var theThing = null;
var replaceThing = function () {
    var priorThing = theThing;
    // сохранив функцию в переменной, мы невольно сохраняем и контекст (цепочку областей видимости) 
    // в котором она была объявлена
    // т.е. в данном случае priorThing оказался в контексте unused
    var unused = function () {
        if (priorThing) {
            console.log("hi");
        }
    };
    // здесь ссылка на старый объект заменяется новым, 
    // старый объект ожидает съедения GC, 
    // unused так же отправится в пасть GC...
    theThing = {
        longStr: new Array(1000000).join('*'), 
        // ... и все бы хорошо, если бы не один нюанс который происходит здесь
        // как там пишут: both functions share the same lexical environment
        // т.е. здесь у функции будет тот же контекст что и у unused
        // потому что контекст уже пошарен при создании unused
        // т.е. ссылка на старый объект priorThing будет сохраняться 
        // в общем контексте someMethod каждый раз.
        someMethod: function () {
            console.log(someMessage);
        }
    };
};
setInterval(replaceThing, 1000);    // вызываем 'replaceThing' каждую секунду

в итоге мы получаем следующую картину
3ba7ea237ca847a380b43f7ac331ce3d.PNG
более подробно об этой утечке info.meteor.com/blog/an-interesting-kind-of-javasc...
подебажить пример можно здесь s.codepen.io/beholderrk/debug/jWGyaY
Ответ написан
@Aksigera
Каждый раз вызов ReplaceThing создает локальную область видимости, для которой существует свой priorThing со своим значением, которое в момент каждого вызова replaceThing, начиная со второго, равно new Array(1000000).join('*').join('*'), т.к. переменная theThing находится в глобальной области видимости. Тут вопрос понимания замыканий: каждый раз при вызове функции определение var создает персональные переменные, которые и хранятся в той области видимости: И как только переменная используется в любом из замыканий, то она попадает в лексическую область видимости, используемую всеми замыканиями в этой области видимости
PS: замыкания и области видимости синонимичны
Ответ написан
@lxShaDoWxl
В памяти сохраняется priorThing потому что он используется в функции unused и при каждом запуске функции replaceThing создаётся новый priorThing и остаётся в памяти
Ответ написан
Комментировать
romy4
@romy4
Exception handler
Недавно был большой спор тут на тостере про утечки памяти в ПХП. На сколько я долго работаю с пыхом и жаваскриптом, могу сказать, что утечки памяти в пхп — это крайне редкое явление на данный момент за счёт грамотно построенного механизма внутренних ссылок и gc, в основном это корявость допмодулей.

Оставлю этот код для того юзернейма, который спорил.
<?php
$theThing = null;
$replaceThing = function () use (&$theThing) {
    $priorThing = $theThing;
    $unused = function () {
        // 'unused' - единственное место, где используется 'priorThing',
        // но 'unused' никогда не вызывается
        if ($priorThing) {
            echo "hi\n";
        }
    };
    $theThing = (object)[];
    $theThing->longStr =  join("*",(new SplFixedArray(1000000))->toArray());
    $theThing->someMethod = function () {
            echo "$someMessage\n";
        };
};
while(1)
{
sleep(1);
$replaceThing();
}
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы