마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

메모리누수를 해결하긴 했습니다만 뭔가 찝찝하네요..

0 추천

누수땜에 이틀 고생하다가 해결하긴 햇는데..

정확한 원인과 그에따른 정확한 논리적인 해결방법을 이해하지 못하고 해결되서 짜증납니다..

 

선배님들 잠시만 시간내서 봐주시면 정말 고개숙여 감사드릴께요.

 

현재 플립보드 형태의 라이브러리로 작업을 하고 있습니다.

이 플립보드는 내부적으로 버퍼를 3개 가지고 있고,

이전이미지 현재이미지 다음이미지 

이렇게 새개를 가지고 있습니다.. 그래야 한장한장 넘길때 부드럽거든요.

 

그리고 어댑터와 콘트롤러 다운로더 부분으로 나누어져 있습니다.

어댑터는 커스텀어댑터를 사용합니다.

 

지금까지 한장한장 이미지를 넘길대마다 메모리에서 객체가 해제가 안되고 계속 쌓여 뻗었었거든요.

참고로 한장한장 넘길때마다 new 다운로더(); 를 호출합니다.

그래서 가장 처음에는 다운로더 부분을 의심하였습니다.

Bitmap객체를 쓰고 잇었거든요.

그래서 고생고생하고 GC와 메모리에 관하여 공부하여

어댑터에서 전달되는 imageView와 다운로더 내부에서 쓰이는 Bitmap 객체에 대하여

WeakReferences 처리를 해주었습니다.

그래도 똑같더라구요..

계속 헤메고 별짓을 다하다가

어댑터부분을 자세히 보니

 

 

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
 
View layout = convertView;
 
layout = inflater.inflate(R.layout.maincontents, null);
 
이렇게 되어있었습니다.
 
위와 같은 문장을
 
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
 
View layout = convertView;
 
if(convertView == null){
layout = inflater.inflate(R.layout.maincontents, null);
}
 
이렇게 바꾸었더니..
누수가 나지 않게 되었습니다.
뷰가 null이 아니면 재사용하겠다는건데..
그런데 제가 위에 했던 방법에서는 왜 누수가 생겼는지 궁금합니다.
물론 속도는 느려진다고 알고는 잇었는데 누수까지 발생할 이유가 되나요?
제 논리로 생각할때는..
뷰를 재사용하지 않고 다시 inflater해서 중복 전개 한다고 해도,
 
처음 뷰가 생성되고 layout이라는 참조변수가 뷰 객체를 가르키고
 
두번째 뷰 전개시에는 다시 layout이라는 새로운 참조변수가 다른애를 가르키기때문에 
 
첫번째 생성 했을때의 뷰객체는 중간에 붕뜨기때문에 가비지컬렉터의 대상이 되지 않나요?
 
제 논리로는 도저히 이해가 안됩니다.
 
도와주세요 선배님들

 

갸아악 (21,260 포인트) 님이 2013년 4월 19일 질문

2개의 답변

+1 추천
 
채택된 답변
정말 메모리 누수 문제가 있었다면 생각하시는데로 단순히 위와 같이 했다고 해결되진 않겠죠. 위와 같은 해결책은 쓸데없이 새로운 메모리를 할당해서 사용하지 않아 문제 발생의 가능성을 줄여주는 것이지 근본적인 것은 해결되지 않았을 수도 있습니다. 진짜 누수가 일어난 곳을 찾아보셔야 하구요 ( http://blog.naver.com/huewu/110082062273 )

위 글만으로는 어떤 메모리에서 누수가 발생했고 왜 그렇게 판단하셨는지 모르겠는데요. 혹시나 진저브레드버전에서 테스트를 하셨을 경우에는 Garbage Collector가 제대로 처리 못하는 Bitmap 관련 Native Heap의 영향일수 있습니다.  그 경우에는  bitmap.recycle()을 호출하고 bitmap = null로 해서 사용한 놈을 바로바로 해제시켜주는 방법이 있습니다. ( http://www.androidpub.com/1282821 )
회색 (21,340 포인트) 님이 2013년 4월 19일 답변
갸아악님이 2013년 4월 25일 채택됨
답글 정말 감사 드립니다.
그런데 제가 생각하는 논리가 지금 잘못된건지 굉장히 혼란스럽습니다.
제가 생각하는 메모리의 개념은 이렇습니다.
첨에 뷰를 전개하여 메모리에 뷰 객체생성 layout = inflater.inflate(R.layout.maincontents, null);

현재 메모리상에 전개한 뷰 객체가 있고
그 뷰 객체를 레퍼런스 변수 layout이 참조하고 잇습니다.
그래서 GC는 안된다고 생각하구요.

getView가 두번째로 호출될 경우.
layout = inflater.inflate(R.layout.maincontents, null);
으로 좀전과 같이 다시 메모리상에 뷰가 전개되어 메모리에 잡힙니다.
layout이 가르키게 되지만..
첫번째 가르키던 layout 레퍼런스 변수는 존재하지 않고
지금 생성한 뷰객체는 지금 선언된 layout이 가르키게 되므로
첫번째 생성한 뷰객체는 어떤 참조변수에게도 참조되지 않아 GC에 의해수거되어 메모리로 반환됨..

만약 제 논리가 맞다면,
뷰를 재사용하지 않아도 버벅대기만 할뿐 메모리가 누적되지 않고 GC에 의해서 수거가 되긴 해야하는데 계속 누적만 되는 현상이 있어서
이렇게 추론하게 되었습니다.

제가 생각하고 있는 메모리의 개념이 틀린건가요?

저는 한장한장 플립할때마다 URL로 부터 이미지를 얻어오는 부분에서
계속적으로 뷰가 한장 넘어갈때마다 메모리가 반환되지 않고 쌓여서 그게 누수라고 생각하게 되었습니다...

가르침 부탁드리겠습니다.!
0 추천
자바의 특성 때문에 그렇습니다.

null이 아닐 때 재사용한다는 뜻은 메모리 상에 변경이 거의 없습니다.

언제나 새로 만드신다면 이론상으로는 그전에 보였던 부분이 GC가 되어서 메모리 개방되는 것은 맞습니다만,

그 시기가 언제일지... 빨리 될지... 알 수 없습니다. 화면에서 안 보여지게 된게 아직 GC되지 않은 상태에서

새로운 메모리만 쓰게 될 수도 있습니다. 단말기에 따라서 그 차이도 많이 납니다. 강제로 GC를 실행시키는 명령이

있어도 어디까지나 VM의 판단에 따라 바로 될 수도 안될수도 있기 때문에.... 재이용하는 습관을 길러야 합니다.
뚱땡이토끼 (1,440 포인트) 님이 2013년 4월 19일 답변
그렇다면 재사용 하지 않고 새로 inflater 시킨다는것은  GC에 의해서 이전것이 수거되지 않는다는한 계속 새로운 메모리영역이 할당된다는 말씀이시죠?
inflater 시킨다를 new 한다고 생각하면 될듯싶네요
내부적으로는 new 하고 있을테니까요~
...