Comments 48
UFO just landed and posted this here
Ох уж это Vanilla JS!
+17
Насколько я понимаю, первый вариант удаляет объект, а не элемент массива. И, если я не ошибаюсь, я даже где-то видел предостережение от такого использования delete.
+7
Не совсем, если внимательно посмотреть, то там даже не массив, а объект, так же можно заменить
Первый и второй вариант различаются лишь структурой хранения данных.
Вариант первый:
Вариант второй:
testedObject[ i ] = ...
на такое testedObject[ 'item_' + i ]
но это ничего не даст.Первый и второй вариант различаются лишь структурой хранения данных.
Вариант первый:
testedObject = {
1 : {
obj : {....}
},
....
.....
}
Вариант второй:
testedObject = {
obj : {
1 : {
.....
},
....
.....
}
}
0
Ну, я про это и пишу. В варианте с утечкой, delete применяется НЕ к элементу массива, а к объекту.
0
Точно, не правильно понял вас, но замечу что и в варианте без утечки delete так же применяется к объекту.
+1
Теоритически, да. Но, вероятно, при таком синтаксисе уже работает «магия JavaScript» и delete делает именно то, что нужно.
0
Тоже возможно. Но как тогда объяснить, что если в качестве индетефикатора свойства, использовать не число, а строку 'item_' + i, и это ничего не меняет. Думаете тоже магия?
0
Ничего не меняет в том смысле что при таком варианте утечки продолжаются?
0
Именно!
0
Вот это уже интересно. Т.к. удаление «элемента хэша» (или «свойства») это достаточно стандартная ситуация.
+1
Вот и я о том же!!! Из-за этого потерял кучу времени пока понял в чем дело. У меня куча мест которые используют оператор delete, и везде все нормально, но в одной функции был именно такой вариант хранения данных, и уж никак я не мог грешить на delete.
0
Я смутно припоминаю, что я в таком случае сначала присваивал свойству пустое значение.
0
Пробовал, тоже не помогает. Я вообще кучу всего пробовал чтоб в варианте с утечкой не меняя структуры данных избавиться от утечки: переприсваивал свойства, делал удаление в отдельной функции и еще куча бредовых штук, но ничего не помогало…
0
Нет, в варианте без утечки вы удаляете элемент массива, а в варианте с утечкой — только одно его свойство.
Представьте, что у вас более сложная структура данных в варианте с утечкой:
А удаляете вы только
тогда как
и
и тд остаются на месте.
Все что надо — просто удалять
— то есть сам элемент массива, а не одно из его свойств.
При удалении свойства сам элемент никуда не девается.
Вот и вся разгадка.
Нет там утечки и не было.
Представьте, что у вас более сложная структура данных в варианте с утечкой:
testedObject[ i ] = {
obj : getObject(),
fld1 : val1,
fld2 : val2
// и тд
};
А удаляете вы только
delete testedObject[ i ].obj;
тогда как
testedObject[ i ].fld1;
и
testedObject[ i ].fld2;
и тд остаются на месте.
Все что надо — просто удалять
testedObject[ i ];
— то есть сам элемент массива, а не одно из его свойств.
При удалении свойства сам элемент никуда не девается.
Вот и вся разгадка.
Нет там утечки и не было.
+6
Интересно, а
testedObject[ i ] = null;
не поможет избавиться от утечки?0
А я правильно понимаю, что сборщик мусора потом все же вычистил память?
0
Учитывая, что delete для элементов массива не меняет его длинну, это может привести к всяческим ошибкам при дальнейших операциях с этим массивом. Предпочитаю использовать splice.
+1
Не успел написать до вашего комента, читайте чуть выше...
0
Кстати, вопрос не в тему))
Как сделать чтоб мой пост был во вкладке посты?
Как сделать чтоб мой пост был во вкладке посты?
0
Скажите, а для отлова утечек вы используете только стандартные средства хрома или что-то ещё?
И я правильно понимаю что вы скрупулёзно, в силу требований проекта, проверяете на утечки новые куски кода?
И я правильно понимаю что вы скрупулёзно, в силу требований проекта, проверяете на утечки новые куски кода?
+1
Прикольно, что судя по всему, если сделать вместо
delete testedObject[ i ].obj;
testedObject[ i ].obj = null;
сборщик мусора собирает эти объекты0
Как Вы пользуетесь гуглом?
У меня первым результатом ссылка на StackOverflow, смотрите второй ответ по популярности в качестве разъясняющего примера.
будет в данном случае правильным решением, так как однозначно говорит удалить все ссылки на объект.
У меня первым результатом ссылка на StackOverflow, смотрите второй ответ по популярности в качестве разъясняющего примера.
testedObject[ i ].obj = null;
будет в данном случае правильным решением, так как однозначно говорит удалить все ссылки на объект.
0
С чего это зануление однозначно говорит удалить _все_ ссылки на объект? Оно просто зануляет эту ссылку и все.
Когда вызывается delete в примере автора, других ссылок на него все-равно нет больше, по идее объект должен собираться со временем, поэтому разумным выглядит объяснение habrahabr.ru/post/150723/#comment_5107501
Когда вызывается delete в примере автора, других ссылок на него все-равно нет больше, по идее объект должен собираться со временем, поэтому разумным выглядит объяснение habrahabr.ru/post/150723/#comment_5107501
+1
Прежде всего совет: не пытайтесь понять, что удаляется, а что не удаляется по графику потребления памяти — это гадание на кофейной гуще. Просто посмотрите на снапшот кучи.
Объяснение (по крайней мере для Хрома) очень простое: когда вы делаете
Объяснение (по крайней мере для Хрома) очень простое: когда вы делаете
delete testedObject[i].obj
, V8 нормализует объект testedObject[i]
— трансформирует его из быстрого компактного представления в медленное и раздутое представление на основе словаря, который еще и выделяется с запасом по размеру. При этом V8 не замечает, что после удаления в словаре будет пусто — и словарь (800 байтов) остается болтаться в воздухе. И так для каждого из ваших объектов. +8
Дополню mraleph:
После создания объектов:
После удаления филдов obj
Как видно — obj прекрасно подчистился и никаких «first»-«second»… не осталось. Но остался массив в 10 000 элементов (сам по себе занимающиий 470КБ), в каждой ячейке из которого пустой объект весом 424 Байта (почему пустой объект весит больше заполненного объяснил mraleph — полагаю потому что сказав
После создания объектов:
После удаления филдов obj
Как видно — obj прекрасно подчистился и никаких «first»-«second»… не осталось. Но остался массив в 10 000 элементов (сам по себе занимающиий 470КБ), в каждой ячейке из которого пустой объект весом 424 Байта (почему пустой объект весит больше заполненного объяснил mraleph — полагаю потому что сказав
delete testedObject[i].obj
вы фактически сказали что testedObject[i]
— изменяемый и вы можете захотеть впихнуть в него новых данных например а не просто обратиться к его полям в «read-only» режиме)+4
Все логично. В первом варианте у Вас:
что дает count объектов внутри testedObject, внутри каждого из которых по 1 объекту obj, то есть суммарное кол-во «значимых» объектов 2*count.
Во втором варианте у Вас:
что дает внутри testedObject 1 объект obj, в котором count объектов с индексами, то есть суммарное кол-во «значимых» объектов count + 1.
То есть второй вариант изначально потребляет меньше памяти, что также можно видеть на графиках, достаточно выключить Record, а потом опять включить, чтоб увидеть конечный результат после нажатия «Заполнить массив».
С учетом комментария mraleph и комментария seriyPS можно принять, что при операции delete родительский объект «нормализуется» и в конечном счете становится не меньше N байт (как видно, зависит от системы).
Для первого варианта родительским объектом является
Во втором варианте Вы получаете:
Как-бы логично, что
testedObject = {
1 : {
obj : {....}
},
....
.....
}
что дает count объектов внутри testedObject, внутри каждого из которых по 1 объекту obj, то есть суммарное кол-во «значимых» объектов 2*count.
Во втором варианте у Вас:
testedObject = {
obj : {
1 : {
.....
},
....
.....
}
}
что дает внутри testedObject 1 объект obj, в котором count объектов с индексами, то есть суммарное кол-во «значимых» объектов count + 1.
То есть второй вариант изначально потребляет меньше памяти, что также можно видеть на графиках, достаточно выключить Record, а потом опять включить, чтоб увидеть конечный результат после нажатия «Заполнить массив».
С учетом комментария mraleph и комментария seriyPS можно принять, что при операции delete родительский объект «нормализуется» и в конечном счете становится не меньше N байт (как видно, зависит от системы).
Для первого варианта родительским объектом является
testedObject[i]
(который Вы не удаляете), для второго варианта родительским элементом является testedObject.obj
, то есть в первом варианте (с «утечкой») Вы получаете в остатке:(2*count - count)*N => count*N
байт лишнимиВо втором варианте Вы получаете:
(count + 1 - count)*N => N
байт лишнимиКак-бы логично, что
count*N > N
, «примерно» в count раз ;)+1
выше уже написали что удаляются разные вещи, но они удаляются — delete возвращает также результат операции, в обоих случаях true.
кроме того, странно видеть профайлер хрома, если баг открывают на мозилловцев.
если смотреть about:memory, то память возвращается спустя какое-то время или запуском сборщика мусора.
кроме того, странно видеть профайлер хрома, если баг открывают на мозилловцев.
если смотреть about:memory, то память возвращается спустя какое-то время или запуском сборщика мусора.
+1
>> На все вопросы получены четкие ответы, это не магия JavaScript, не утечка и не браузерный баг, это особенность реализации.
Не совсем, все-таки, на мой взгляд утечка здесь есть, потому как был {}, потом {obj: {...}}, потом удалили obj через delete и получили, что размер полученного {} > начального {}, или теперь memory leak == «особенность реализации»? )
Просто у Вас некорректный пример, так как сравниваете изначально разные вещи, если уж отписывать баг, то скрипт должен быть другим, в котором будет видна утечка от «особенностей реализации».
В приведенном выше рассуждении была некая переменная N, вместо которой нужно (N + M), где:
— M — это размер пустого объекта {};
— N — размер «особенностей нормализации».
Тогда
Не совсем, все-таки, на мой взгляд утечка здесь есть, потому как был {}, потом {obj: {...}}, потом удалили obj через delete и получили, что размер полученного {} > начального {}, или теперь memory leak == «особенность реализации»? )
Просто у Вас некорректный пример, так как сравниваете изначально разные вещи, если уж отписывать баг, то скрипт должен быть другим, в котором будет видна утечка от «особенностей реализации».
В приведенном выше рассуждении была некая переменная N, вместо которой нужно (N + M), где:
— M — это размер пустого объекта {};
— N — размер «особенностей нормализации».
Тогда
testedObject[i]
должен после delete testedObject[i].obj
стать размером M, а становится размером (N + M), то есть в первом варианте после «очистки» в идеале должны получить M*count, а получили (N + M)*count, а еще в куче видно, что M много больше N, что немного неправильно.0
Простите первый раз неправильно прочитал ваш комент.
Вы все же считаете что утечка ест, я правильно понял? Я изначально думал тоже об утечке, но сильно сомневался изза того что не может быть такого о всех браузерах одновременно, и вариант описывающий реализацию delete, более реалистичен.
Вы все же считаете что утечка ест, я правильно понял? Я изначально думал тоже об утечке, но сильно сомневался изза того что не может быть такого о всех браузерах одновременно, и вариант описывающий реализацию delete, более реалистичен.
0
утечка — это когда память болтается непонятно где, не используется и не может быть переиспользована. а здесь не эффективное использование памяти, потому что это память никуда не пропала и занята объектом, на который есть валидные ссылки из другого объекта.
+2
Не совсем, все-таки, на мой взгляд утечка здесь есть
Да, я согласен, это не утечка, как таковой казалась на первый взгляд, и я написал об этом. Под особенность реализации, о которых я не знал, думаю и многие другие, я имел в виду:
когда вы делаете delete testedObject[i].obj, V8 нормализует объект testedObject[i] — трансформирует его из быстрого компактного представления в медленное и раздутое представление на основе словаря, который еще и выделяется с запасом по размеру. При этом V8 не замечает, что после удаления в словаре будет пусто — и словарь (800 байтов) остается болтаться в воздухе.
зная о такой реализации оператора delete, я бы не наткнулся на такие грабли, и поста бы вообще небыло. Но пост есть, и думаю заслужиает существования, т.к. вполне вероятно многие незнаю как работает delete.
0
Sign up to leave a comment.
JavaScript: оператор delete создает утечку!?