IE8과 9에서 발생하는 javascript memory leak 이슈

금년도 들어서 가장 해결하기 어려웠고.. 왜 그렇게 동작하는지 이해가 안갔던 이슈다.

상황은 다음과 같다.
1.parent.html 에서 child.html 을 오픈 함
2.child.html 은 특정 동작을 취하면 parent.html에 저장된 window.setAction()호출
3.window.setAction( jSonData ) 와 같이 인자로 오브젝트 데이터를 전달하게 된다.
4.전달된 데이터는 전역 변수( 해당 스코프 안에서 ) parent.html 에서 저장하고 있다가. 특정 동작이 실행되면 ajax로 백엔드에 전달

언뜻보면 간단한 흐름을 가진 구조다. 하지만. 3번과 4번에서 IE8&9에서 문제가 발생한다. 즉 jsonData가 전달 당시에는 제대로 전달이 되는것 같지만, ( 디버그로 확인해보면 전달 당시에는 정상적인 값이 출력된다. ) 사용하려고 하면 null 도 아닌 null같은 쓰레기 값이 들어가 있어서 그 동작은 정상적으로 수행되지 않는다.

문제를 좀 객관화 하자면 IE 8&9에서는 부모페이지와 자식 페이지 상의 전역함수로 호출할경우 파라미터에 복잡한 JSON형태의 데이터가 들어가면 해당 데이터를 직접 저장하는게 아닌. 데이터가 생성되었을때 기준으로 shallow copy를 해오는것 같다. ( 이부분이 맞는지는 사실 정확히 모르겠다.. 자료를 찾아봐도 없다. )

이상황에서 자식창이 종료가 되면 shallow copy된 데이터도 같이 날아가버리는 현상이 발생한다.

문제에서 사용했던 data는 아래와 같다.

"data":[{
  "key1":"value",
  "key2":"value",
  "keyList":[{
    "key3":"value",
    "key4":"value"
   }],
  "key5":"value"
  },
  {
  "key1":"value",
  "key2":"value",
  "keyList":[{
    "key3":"value",
    "key4":"value"
  }],
  "key5":"value"
 }];

아무런 문제가 없는 일반적으로 사용하는 이중 배열 형태의 데이터다. 단지 data[0].keyList 가 한번더 배열인형태로 좀 복잡할 수가 있지만 (?) 트리 형태의 구조를 표현할때는 저렇게 구조를 잡는게 맞다.

당연히 해당 데이터는 circular-reference 로 만들어진 것도 아니였다. 그래서 해당 문제를 해결 하기 위해 일단 문제를 단순화 했다.

위의 과정중에
3. window.setAction( jSonData ) 와 같이 인자로 오브젝트 데이터를 전달하게 된다.
jsonData 부분에 위에 명시했던 데이터를 객체 형식으로 저장해서 과정4번 과정까지 진행해봤다. ( 즉 아래와 같이..)

var jsonData =[{  "key1":"value",
  "key2":"value",
  "keyList":[{
    "key3":"value",
    "key4":"value"
   }],
  "key5":"value"
  },
  {
  "key1":"value",
  "key2":"value",
  "keyList":[{
    "key3":"value",
    "key4":"value"
  }],
  "key5":"value"
 }];
window.setAction( jSonData );

정말로 신기한건 위와 같이 하면 문제가 해결 된다는 것이다.

하지만 실제로 웹서비스에서 저런 정적인 데이터는 불가능하므로, 다시 변수로 저장을 시도해봤다. 4번의 페이지에서 전달받았을 당시에는 IE8에서 디버깅을 해보면 문제없이 전달이 되므로.. 해당 변수 데이터를 클로저로 저장도 해보고. localStorage 에 저장도해보는 작업을 해본다.

하지만 결론은 모두 다 null도 아닌 쓰레기 값(?) 이 저장되며 여전히 4번 순서에서 오류가 났다..

그러다가 4번의 전달받은 데이터를 JSON.stringify( jsonData)로 해서 스트링으로 저장도 시도 해봤지만. 해당 데이터를 JSON.parse( )로 다시 JSON으로 만들때 circular reference 오류가 발생했다.
( circular reference가 나는 이유는.. 해당 데이터가 정상적이지 않기 때문에..)

정말 이리저리 삽질을 진행하다가 결국에는 문제를 해결했다.
아래는 해결한 방법이다.

var jsonData =[{  "key1":"value",
  "key2":"value",
  "keyList":[{
    "key3":"value",
    "key4":"value"
   }],
  "key5":"value"
  },
  {
  "key1":"value",
  "key2":"value",
  "keyList":[{
    "key3":"value",
    "key4":"value"
  }],
  "key5":"value"
 }];
jsonData = JSON.stringify( jsonData );
window.setAction( jSonData );

즉 자식 페이지에서 부모 페이지로 전달할때 JSON.stringify()로  스트링화 해서 전달한후 다시 JSON.parse() 해서 사용했더니 정보도 저장이 잘되었고 4번순서의 ajax 요청에도 정상적으로 활용할 수 있었다.

* 참고로 JSON관련 라이브러리는 jquery-json 을 사용했습니다. ( 그래서 문제없이 IE에서 JSON라이브러리를 사용가능)

뭔가 해결은 냈지만.. 원인을 정확하게 파악을 못해서 여전히 찝찝하다.

그 이후에 여러가지 방법으로 찾아봤는데.
https://support.microsoft.com/en-us/kb/2711084
위와 같은 내용이 존재한다. 요약하자면

IE 8에서는 윈도우창을 열었다 닫을때 참조하는 순환참조형태의 배열이 있다면 메모리 누수현상이 발생함” 이다.

물론 내가 만든 저 배열의 순환참조는 없었지만. IE 8에서 메모리 누수 현상이 있기때문에 내가 만든 패턴도 저 이슈에 걸렸다고 생각이되는데. 정확한 이해가 되지 않는데 다시 말하자면 나는 순환참조 배열이 아니다..

결국 의문점이 더 해소가 되지 않아서 더 찾아봤다. IE 에서 메모리 누수현상은 꾸준히 지적되어왔으니 내가 겪은 이슈도 분명히 있을것이라 생각하면서..
여러 검색 끝에 아래와 같이 결과물이 나왔다. (  MSDN..)

https://msdn.microsoft.com/en-us/library/bb250448(v=vs.85).aspx
해당 글에는 공식적으로 메모리 누수 패턴에 대해 설명하고 있다.

1.Circular References : 순환참조
2.Closures: 클로져
3.Cross-Page Leaks: 크로스 페이지 누수
4.Pseudo-Leaks : 슈도 누수(?)

드디어 찾았다. 3번째 크로스 페이지 누수다.

원문은 다음과 같다.

Cross-page leaks are often very small leaks of internal book-keeping objects as you move from site to site.

즉 사이트에서 사이트간의 이동 할때 내부적으로 저장된 객체해서 메모리 누수가 종종 발생한다고 한다.  이것을 스트링으로 저장하고 활용했기때문에 해결 가능했던 것이고 IE 8&9에서는 해당 이슈가 지적되며 개발자가 이런 객체로 저장하고 활용하는것을 어느정도 방어적으로 코드를 작성해야 한다고 나와있다.

많이 돌아와서.. 결국 이슈를 해결하고 원인도 알았다. ( 사실 이 포스팅을 준비하면서 더 찾아보다가여기까지 결론에 왔다.. 원래는 “~~해서 해결했다. 그래도 모르겠다. ” 였는데..)

한줄로 요약하자

IE 8&9 에서 사이트간 이동할때 객체형태의 데이터를 전달할때는 신중해야한다. (메모리누수 현상 발생할수도 있음 )

추가 : 포스팅 하고 해당 페이지의 3번째 이슈에 대한 상세 설명을 보면 DOM과 객체간의 이슈를 중점적으로 다루고 있다. 다만 내  이슈의 경우에는 자식창이 종료되면서 자식창이 가지고 있던 객체가 가비지 콜렉터에 의해 지워지면서 발생하는 이슈인것 같다.

1 댓글

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.

다음의 HTML 태그와 속성을 사용할 수 있습니다: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>