다음 Chrome 버그를 기록했습니다 . 이로 인해 코드에서 심각하고 분명하지 않은 메모리 누수가 많이 발생했습니다.
(이 결과 는 GC를 실행하고 가비지 수집되지 않은 모든 것의 힙 스냅 샷을 생성하는 Chrome Dev Tools의 메모리 프로파일 러 를 사용합니다.)
아래 코드에서 someClass
인스턴스는 가비지 수집됩니다 (양호).
var someClass = function() {};
function f() {
var some = new someClass();
return function() {};
}
window.f_ = f();
그러나이 경우 가비지 수집되지 않습니다 (나쁜).
var someClass = function() {};
function f() {
var some = new someClass();
function unreachable() { some; }
return function() {};
}
window.f_ = f();
그리고 해당 스크린 샷 :
클로저 (이 경우 function() {}
)는 객체가 동일한 컨텍스트에서 다른 클로저에 의해 참조되는 경우 클로저 자체에 도달 할 수 있는지 여부에 관계없이 모든 객체를 “생존”상태로 유지 하는 것으로 보입니다 .
내 질문은 다른 브라우저 (IE 9 이상 및 Firefox)의 가비지 수집에 관한 것입니다. JavaScript 힙 프로파일 러와 같은 웹킷 도구에 익숙하지만 다른 브라우저 도구는 거의 알지 못하므로 테스트 할 수 없습니다.
이 세 가지 경우 중 IE9 +와 Firefox 가비지가 인스턴스를 수집 someClass
합니까?
답변
내가 알 수있는 한, 이것은 버그가 아니라 예상되는 동작입니다.
Mozilla의 메모리 관리 페이지에서 : “2012 년 현재 모든 최신 브라우저는 마크 앤 스윕 가비지 수집기를 제공합니다.” “제한 : 객체는 명시 적으로 도달 할 수 없어야 합니다. “
귀하의 예에서 실패한 부분 some
은 여전히 폐쇄에 도달 할 수 있습니다. 나는 그것을 도달 할 수 없도록 만들고 두 가지 방법을 시도했습니다. some=null
더 이상 필요하지 않을 때 설정 하거나 설정하면 window.f_ = null;
사라집니다.
최신 정보
Windows의 Chrome 30, FF25, Opera 12 및 IE10에서 시도했습니다.
표준은 가비지 컬렉션에 대해 아무 말도 있지만, 어떻게해야하는지의 몇 가지 단서를 제공하지 않습니다.
- 섹션 13 함수 정의, 4 단계 : “13.2에 지정된 새 Function 객체를 만든 결과를 닫습니다.”
- 섹션 13.2 “범위에 의해 지정된 어휘 환경”(범위 = 폐쇄)
- 섹션 10.2 어휘 환경 :
“(내부) 어휘 환경의 외부 참조는 내부 어휘 환경을 논리적으로 둘러싼 어휘 환경에 대한 참조입니다.
물론 외부 어휘 환경에는 자체 외부 어휘 환경이있을 수 있습니다. 어휘 환경은 여러 내부 어휘 환경의 외부 환경으로 사용될 수 있습니다. 예를 들어, 함수 선언이 두 중첩 포함 함수 선언 후 중첩 된 각 기능의 사전 환경은 외부 환경 어휘 주변 기능의 현재의 사전 실행 환경으로 할 것이다. “
따라서 함수는 부모의 환경에 액세스 할 수 있습니다.
따라서 some
반환 기능을 닫을 때 사용할 수 있어야합니다.
그렇다면 왜 항상 사용할 수 없습니까?
Chrome과 FF는 경우에 따라 변수를 제거하기에 충분히 똑똑한 것으로 보이지만 Opera와 IE 모두 some
변수를 클로저에서 사용할 수 있습니다 (NB :이 세트를 중단 점을 return null
보고 디버거를 확인하십시오).
some
기능에 사용 되는지 여부를 감지하도록 GC를 개선 할 수 는 있지만 복잡합니다.
나쁜 예 :
var someClass = function() {};
function f() {
var some = new someClass();
return function(code) {
console.log(eval(code));
};
}
window.f_ = f();
window.f_('some');
위의 예에서 GC는 변수의 사용 여부를 알 수있는 방법이 없습니다 (코드 테스트 및 Chrome30, FF25, Opera 12 및 IE10에서 작동).
에 다른 값을 할당하여 객체에 대한 참조가 손상되면 메모리가 해제됩니다 window.f_
.
제 생각에는 이것은 버그가 아닙니다.
답변
IE9 +와 Firefox에서 이것을 테스트했습니다.
function f() {
var some = [];
while(some.length < 1e6) {
some.push(some.length);
}
function g() { some; } //removing this fixes a massive memory leak
return function() {}; //or removing this
}
var a = [];
var interval = setInterval(function() {
var len = a.push(f());
if(len >= 500) {
clearInterval(interval);
}
}, 10);
나는 function() {}
최소한의 메모리를 사용하여 500의 배열로 마무리하고 싶었습니다 .
불행히도, 그렇지 않았습니다. 각 빈 함수는 백만 개의 숫자로 구성된 (영원히 도달 할 수 없지만 GC가 아닌) 배열을 유지합니다.
Chrome은 결국 중단되고 죽고 Firefox는 거의 4GB의 RAM을 사용한 후 모든 것을 끝내고 IE는 “메모리 부족”을 표시 할 때까지 점진적으로 느려집니다.
주석 처리 된 행 중 하나를 제거하면 모든 것이 수정됩니다.
이 세 가지 브라우저 (Chrome, Firefox 및 IE)는 모두 폐쇄가 아니라 컨텍스트별로 환경 레코드를 유지하는 것으로 보입니다. Boris는이 결정의 원인이 성능이라는 가설을 세웠으며, 위 실험에 비추어 얼마나 성능이 좋은지 잘 모르겠습니다.
대신 클로저 참조가 필요한 경우 some
(여기서 사용하지 않았지만 상상해보십시오)
function g() { some; }
나는 사용한다
var g = (function(some) { return function() { some; }; )(some);
클로저를 다른 함수와 다른 컨텍스트로 이동하여 메모리 문제를 해결합니다.
이것은 내 인생을 훨씬 더 지루하게 만들 것입니다.
추신 : 호기심으로, Java에서 이것을 시도했습니다 (함수 내부의 클래스를 정의하는 기능 사용). GC는 원래 Javascript를 원했던대로 작동합니다.
답변
휴리스틱은 다양하지만, 이런 종류의 물건을 구현하는 일반적인 방법은 각 통화에 대한 환경 레코드를 생성하는 것입니다 f()
귀하의 경우, 단지의 주민들 저장 f
실제로 (에 의해 이상 닫혀 일부 해당 환경 레코드에 폐쇄). 그런 다음 호출에서 생성 된 폐쇄 f
는 환경 레코드 를 유지합니다. 나는 이것이 Firefox가 최소한 클로저를 구현하는 방법이라고 생각합니다.
이는 닫힌 변수에 빠르게 액세스 할 수 있고 구현이 간편하다는 이점이 있습니다. 일부 변수에 대한 단기 폐쇄 폐쇄로 인해 장기 폐쇄에 의해 생존이 유지되는 관측 된 효과의 단점이 있습니다.
실제로 닫은 내용에 따라 다른 클로저에 대해 여러 환경 레코드를 만들려고 시도 할 수 있지만 매우 복잡해져 자체 성능 및 메모리 문제를 일으킬 수 있습니다.
답변
- 함수 호출 사이의 상태 유지 add () 함수가 있고 여러 호출에서 전달 된 모든 값을 추가하고 합계를 반환한다고 가정합니다.
add (5)처럼; // 5를 반환
추가 (20); // 25를 반환합니다 (5 + 20)
추가 (3); // 28을 반환합니다 (25 + 3)
전역 변수를 정의하는 것이 일반적입니다.
물론 총계를 유지하기 위해 전역 변수를 사용할 수 있습니다. 그러나 만약 당신이 (ab) 글로벌을 사용한다면이 친구는 당신을 살아있게 먹을 것입니다.
전역 변수를 정의하지 않고 클로저 를 사용하는 최신 방법
(function(){
var addFn = function addFn(){
var total = 0;
return function(val){
total += val;
return total;
}
};
var add = addFn();
console.log(add(5));
console.log(add(20));
console.log(add(3));
}());
답변
function Country(){
console.log("makesure country call");
return function State(){
var totalstate = 0;
if(totalstate==0){
console.log("makesure statecall");
return function(val){
totalstate += val;
console.log("hello:"+totalstate);
return totalstate;
}
}else{
console.log("hey:"+totalstate);
}
};
};
var CA=Country();
var ST=CA();
ST(5); //we have add 5 state
ST(6); //after few year we requare have add new 6 state so total now 11
ST(4); // 15
var CB=Country();
var STB=CB();
STB(5); //5
STB(8); //13
STB(3); //16
var CX=Country;
var d=Country();
console.log(CX); //store as copy of country in CA
console.log(d); //store as return in country function in d
답변
(function(){
function addFn(){
var total = 0;
if(total==0){
return function(val){
total += val;
console.log("hello:"+total);
return total+9;
}
}else{
console.log("hey:"+total);
}
};
var add = addFn();
console.log(add);
var r= add(5); //5
console.log("r:"+r); //14
var r= add(20); //25
console.log("r:"+r); //34
var r= add(10); //35
console.log("r:"+r); //44
var addB = addFn();
var r= addB(6); //6
var r= addB(4); //10
var r= addB(19); //29
}());
답변
