Ответ 1
Неверно, что существуют ограничения, запрещающие вам вызывать eval, которые будут упущены статическим анализом: это просто, что такие ссылки на eval выполняются в глобальной области. Обратите внимание, что это изменение ES5 от ES3, где косвенные и прямые ссылки на eval выполнялись в локальной области, и поэтому я не уверен, что на самом деле какие-либо оптимизации основываются на этом факте.
Очевидный способ проверить это - сделать BigObject действительно большим объектом и заставить gc после запуска f0-f2. (Потому что, эй, насколько мне кажется, я знаю ответ, тестирование всегда лучше!)
Итак...
Тест
var closure;
function BigObject() {
var a = '';
for (var i = 0; i <= 0xFFFF; i++) a += String.fromCharCode(i);
return new String(a); // Turn this into an actual object
}
function f0() {
var x = new BigObject();
var y = 0;
closure = function(){ return 7; };
}
function f1() {
var x = new BigObject();
closure = (function(y) { return function(){return y++;}; })(0);
}
function f2() {
var x = new BigObject();
var y = 0;
closure = function(){ return y++; };
}
function f3() {
var x = new BigObject();
var y = 0;
closure = eval("(function(){ return 7; })"); // direct eval
}
function f4() {
var x = new BigObject();
var y = 0;
closure = (1,eval)("(function(){ return 7; })"); // indirect eval (evaluates in global scope)
}
function f5() {
var x = new BigObject();
var y = 0;
closure = (function(){ return eval("(function(){ return 7; })"); })();
}
function f6() {
var x = new BigObject();
var y = 0;
closure = function(){ return eval("(function(){ return 7; })"); };
}
function f7() {
var x = new BigObject();
var y = 0;
closure = (function(){ return (1,eval)("(function(){ return 7; })"); })();
}
function f8() {
var x = new BigObject();
var y = 0;
closure = function(){ return (1,eval)("(function(){ return 7; })"); };
}
function f9() {
var x = new BigObject();
var y = 0;
closure = new Function("return 7;"); // creates function in global scope
}
Я добавил тесты для eval/Function, кажется, что это также интересные случаи. Разница между f5/f6 интересна, потому что f5 действительно просто идентична f3, учитывая то, что действительно является идентичной функцией для закрытия; f6 просто возвращает то, что когда-то оценивалось, дает это, и поскольку eval еще не был оценен, компилятор не может знать, что в нем нет ссылки на x.
SpiderMonkey
js> gc();
"before 73728, after 69632, break 01d91000\n"
js> f0();
js> gc();
"before 6455296, after 73728, break 01d91000\n"
js> f1();
js> gc();
"before 6455296, after 77824, break 01d91000\n"
js> f2();
js> gc();
"before 6455296, after 77824, break 01d91000\n"
js> f3();
js> gc();
"before 6455296, after 6455296, break 01db1000\n"
js> f4();
js> gc();
"before 12828672, after 73728, break 01da2000\n"
js> f5();
js> gc();
"before 6455296, after 6455296, break 01da2000\n"
js> f6();
js> gc();
"before 12828672, after 6467584, break 01da2000\n"
js> f7();
js> gc();
"before 12828672, after 73728, break 01da2000\n"
js> f8();
js> gc();
"before 6455296, after 73728, break 01da2000\n"
js> f9();
js> gc();
"before 6455296, after 73728, break 01da2000\n"
SpiderMonkey появляется на GC "x" на всех, кроме f3, f5 и f6.
Похоже, что это возможно (то есть, когда это возможно, y, а также x), если в цепочке областей любой функции, которая все еще существует, есть прямой вызов eval. (Даже если этот объект функции был GC'd и больше не существует, как в случае f5, что теоретически означает, что он может GC x/y.)
V8
[email protected]:~$ v8 --expose-gc --trace_gc --shell foo.js
V8 version 3.0.7
> gc();
Mark-sweep 0.8 -> 0.7 MB, 1 ms.
> f0();
Scavenge 1.7 -> 1.7 MB, 2 ms.
Scavenge 2.4 -> 2.4 MB, 2 ms.
Scavenge 3.9 -> 3.9 MB, 4 ms.
> gc();
Mark-sweep 5.2 -> 0.7 MB, 3 ms.
> f1();
Scavenge 4.7 -> 4.7 MB, 9 ms.
> gc();
Mark-sweep 5.2 -> 0.7 MB, 3 ms.
> f2();
Scavenge 4.8 -> 4.8 MB, 6 ms.
> gc();
Mark-sweep 5.3 -> 0.8 MB, 3 ms.
> f3();
> gc();
Mark-sweep 5.3 -> 5.2 MB, 17 ms.
> f4();
> gc();
Mark-sweep 9.7 -> 0.7 MB, 5 ms.
> f5();
> gc();
Mark-sweep 5.3 -> 5.2 MB, 12 ms.
> f6();
> gc();
Mark-sweep 9.7 -> 5.2 MB, 14 ms.
> f7();
> gc();
Mark-sweep 9.7 -> 0.7 MB, 5 ms.
> f8();
> gc();
Mark-sweep 5.2 -> 0.7 MB, 2 ms.
> f9();
> gc();
Mark-sweep 5.2 -> 0.7 MB, 2 ms.
V8 появляется на GC x на всем, кроме f3, f5 и f6. Это идентично SpiderMonkey, см. Анализ выше. (Обратите внимание, однако, что числа недостаточно подробны, чтобы определить, является ли y GC'd, когда x не является, я не потрудился исследовать это.)
Carakan
Я не собираюсь работать с этим снова, но, разумеется, поведение идентично SpiderMonkey и V8. Сложнее тестировать без оболочки JS, но выполнимо со временем.
JSC (Nitro) и Chakra
Building JSC - это боль в Linux, а Chakra не работает в Linux. Я считаю, что такое же поведение у таких же двигателей есть, и я был бы удивлен, если бы у Чакры тоже не было. (Делать что-нибудь лучшее быстро становится очень сложным, делая что-то еще хуже, ну, вы почти никогда не будете делать GC и иметь серьезные проблемы с памятью...)