자바스크립트 폐쇄가 가비지 수집 방법
다음과 같은 Chrome 버그를 기록했습니다. 이로 인해 코드에 많은 심각하고 명확하지 않은 메모리 누수가 발생했습니다.
(이러한 결과는 Chrome Dev Tools의 메모리 프로파일러를 사용합니다. 이 프로파일러는 GC를 실행한 다음 가비지되지 않은 모든 것의 힙 스냅샷을 만듭니다.)
아래 코드에서,someClass
인스턴스(instance) 가비지 수집(good):
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() {}
개체가 동일한 컨텍스트에서 다른 개체에 의해 참조되는 경우 해당 개체 자체에 도달할 수 있는지 여부와 상관없이 모든 개체 "alive"을 유지합니다.
제 질문은 다른 브라우저(IE 9+ 및 Firefox)에서 폐쇄의 가비지 컬렉션에 대한 것입니다.저는 자바스크립트 힙 프로파일러와 같은 웹킷의 도구는 잘 알고 있지만, 다른 브라우저의 도구는 거의 알지 못해서 이것을 테스트할 수 없었습니다.
IE9+와 Firefox 가비지가 수집하는 세 가지 경우 중 어느 것이 someClass
예를 들어요?
제가 알기로는 이건 버그가 아니라 예상되는 행동입니다.
모질라의 메모리 관리 페이지에서: "2012년 현재, 모든 최신 브라우저는 마크 앤 스위프 가비지 콜렉터를 제공합니다." "제한: 객체는 명시적으로 도달할 수 없도록 만들어야 합니다."
실패한 경우의 예에서some
폐쇄 상태에서 여전히 도달할 수 있습니다.저는 두 가지 방법으로 연결이 안 되게 하고 둘 다 작동하도록 노력했습니다.둘 중 하나를 설정합니다.some=null
더 이상 필요 없을 때, 혹은 당신이 설정할 때window.f_ = null;
사라질 겁니다
갱신하다
윈도우에서 크롬 30, FF25, 오페라 12, IE10에서 시도해 보았습니다.
이 표준은 쓰레기 수거에 대해 아무 말도 하지 않고, 어떤 일이 일어나야 하는지에 대한 단서를 제공합니다.
- 섹션 13 기능 정의, 단계 4: "닫힘이 13에 명시된 새로운 기능 객체를 생성한 결과라고 가정합니다.2"
- 섹션 13.2 "범위로 명시된 어휘 환경" (범위 = 폐쇄)
- 섹션 10.2 어휘 환경:
"(내부) 렉시컬 환경의 외부 참조는 논리적으로 내부 렉시컬 환경을 둘러싸고 있는 렉시컬 환경에 대한 참조입니다.
외부 어휘 환경에는 당연히 자체적인 외부 어휘 환경이 있을 수 있습니다.어휘 환경은 여러 내부 어휘 환경의 외부 환경 역할을 할 수 있습니다.예를 들어, 함수 선언에 두 개의 중첩 함수 선언이 포함되어 있는 경우, 각 중첩 함수의 어휘 환경은 외부 어휘 환경으로서 주변 함수의 현재 실행의 어휘 환경을 갖게 됩니다."
따라서 기능은 부모의 환경에 접근할 수 있습니다.
그렇게,some
반환 기능을 종료할 때 사용할 수 있어야 합니다.
그럼 왜 항상 이용할 수 없는 겁니까?
Chrome과 FF는 경우에 따라 변수를 제거할 수 있을 정도로 똑똑하지만, Opera와 IE 모두에서some
변수는 폐쇄에서 사용할 수 있습니다(NB: 이 설정을 보려면return null
그리고 디버거를 확인합니다).
GC는 다음을 감지할 수 있도록 개선될 수 있습니다.some
함수에 사용되거나 사용되지 않지만 복잡해질 것입니다.
나쁜 예:
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);
여기 라이브 사이트.
나는 500명의 사람들로 마무리하고 싶었습니다.function() {}
최소한의 메모리를 사용합니다.
불행하게도, 그것은 사실이 아니었습니다.각 빈 함수는 백만 개 숫자의 배열을 유지합니다(영원히 도달할 수 없지만 GC'ed는 아님).
크롬은 결국 중단되어 소멸되고, 파이어폭스는 거의 4GB의 RAM을 사용한 후 전체 작업을 완료하며, IE는 "메모리 부족"을 표시할 때까지 점진적으로 느려집니다.
주석이 달린 줄 중 하나만 제거하면 모든 것이 해결됩니다.
이 세 브라우저(Chrome, Firefox, IE) 모두 폐쇄가 아닌 컨텍스트별로 환경 기록을 유지하는 것으로 보입니다.보리스는 이 결정의 배경에 있는 이유가 성능 때문이라고 가정하고 있는데, 위의 실험에 비추어 볼 때 얼마나 성능이 뛰어나다고 할 수 있을지는 잘 모르겠습니다.
폐쇄가 필요한 경우 다음을 참조합니다.some
(여기서 사용한 건 아니지만, 사용했다고 상상해보세요), 만약 대신에
function g() { some; }
사용합니다.
var g = (function(some) { return function() { some; }; )(some);
그것은 나의 다른 기능과 다른 맥락으로 닫힘으로써 기억 문제를 해결할 것입니다.
이것은 제 삶을 훨씬 더 지루하게 만들 것입니다.
추신. 호기심으로 자바에서 시도해 보았습니다. (기능 안에서 클래스를 정의하는 기능을 사용하여)GC는 원래 자바스크립트를 바라던 대로 작동합니다.
휴리스틱은 다양하지만, 이와 같은 것을 구현하는 일반적인 방법은 각 호출에 대한 환경 레코드를 생성하는 것입니다.f()
당신의 경우에는, 그리고 오직 지역 주민들만 저장해두죠.f
(일부 폐쇄에 의해) 해당 환경 기록에 실제로 폐쇄된 것입니다.다음으로 전화를 걸 때 어떤 폐쇄가 생겨났는지에 대해f
환경 기록을 유지할 수 있습니다.적어도 파이어폭스가 폐쇄를 구현하는 방법은 이렇다고 생각합니다.
이를 통해 폐쇄형 변수에 빠르게 액세스할 수 있고 구현이 간단하다는 이점이 있습니다.관찰된 효과의 단점을 가지고 있는데, 어떤 변수에 대해 짧은 기간의 폐쇄로 인해 수명이 긴 폐쇄로 인해 수명이 유지된다는 것입니다.
실제로 폐쇄되는 내용에 따라 여러 폐쇄 환경 레코드를 생성할 수도 있지만, 이는 매우 빠르게 복잡해질 수 있으며 성능 및 메모리 문제를 야기할 수 있습니다.
- 함수 호출 간 상태 유지 함수 add()가 있고 여러 호출에서 전달된 모든 값을 추가하고 합계를 반환하려고 한다고 가정합니다.
add(5)와 같이; // returns 5
add(20); // 25(5+20)을 반환합니다.
add(3); // 28(25 + 3)을 반환합니다.
먼저 두 가지 방법은 정규 변수를 정의하는 것입니다. 물론, 전체를 유지하기 위해 전역 변수를 사용할 수 있습니다.하지만 당신이 글로벌을 사용한다면 이 남자가 당신을 산 채로 잡아먹을 것임을 명심하세요.
글로벌 변수를 정의하지 않고 클로징을 사용하는 최신 방법
(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
}());
언급URL : https://stackoverflow.com/questions/19798803/how-javascript-closures-are-garbage-collected
'bestsource' 카테고리의 다른 글
AJAX, 하위 도메인 및 SSL (0) | 2023.10.31 |
---|---|
객체 배열에서 속성이 검색과 일치하는 객체의 인덱스를 찾는 가장 빠른 방법 (0) | 2023.10.31 |
환경의 현재 로케일을 가져오는 방법은 무엇입니까? (0) | 2023.10.26 |
시작은 없고, 기능은 바둑에서 끝납니까? (0) | 2023.10.26 |
Google Maps V3 - 주어진 경계에 대한 줌 레벨을 계산하는 방법 (0) | 2023.10.26 |