bestsource

자바스크립트 폐쇄가 가비지 수집 방법

bestsource 2023. 10. 31. 22:25
반응형

자바스크립트 폐쇄가 가비지 수집 방법

다음과 같은 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();

그리고 그에 상응하는 스크린샷:

screenshot of Chromebug

종결된 것으로 보입니다(이 사건의 경우,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환경 기록을 유지할 수 있습니다.적어도 파이어폭스가 폐쇄를 구현하는 방법은 이렇다고 생각합니다.

이를 통해 폐쇄형 변수에 빠르게 액세스할 수 있고 구현이 간단하다는 이점이 있습니다.관찰된 효과의 단점을 가지고 있는데, 어떤 변수에 대해 짧은 기간의 폐쇄로 인해 수명이 긴 폐쇄로 인해 수명이 유지된다는 것입니다.

실제로 폐쇄되는 내용에 따라 여러 폐쇄 환경 레코드를 생성할 수도 있지만, 이는 매우 빠르게 복잡해질 수 있으며 성능 및 메모리 문제를 야기할 수 있습니다.

  1. 함수 호출 간 상태 유지 함수 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

반응형