'memory'에 해당되는 글 1건

  1. 2013.07.08 [Android ViewPager] java.lang.OutOfMemoryError



안드로이드의 ViewPager 및 BitmapFactory를 이용해서 몇 종류의 이미지들을 모아 보여주는 기능을 구현하였는데

다음과 같이 OOM(Out Of Memory Error)를 맞닥뜨렸다.

 

07-08 00:21:48.309: E/dalvikvm-heap(28645): Out of memory on a 13498800-byte allocation.
07-08 00:21:48.309: I/dalvikvm(28645): "main" prio=5 tid=1 RUNNABLE
07-08 00:21:48.309: I/dalvikvm(28645):   | group="main" sCount=0 dsCount=0 obj=0x40cfc460 self=0x1969d88
07-08 00:21:48.309: I/dalvikvm(28645):   | sysTid=28645 nice=0 sched=0/0 cgrp=default handle=1075004776
07-08 00:21:48.309: I/dalvikvm(28645):   | schedstat=( 0 0 0 ) utm=789 stm=548 core=1
07-08 00:21:48.309: I/dalvikvm(28645):   at android.graphics.Bitmap.nativeCreate(Native Method)
07-08 00:21:48.319: I/dalvikvm(28645):   at android.graphics.Bitmap.createBitmap(Bitmap.java:605)
07-08 00:21:48.319: I/dalvikvm(28645):   at android.graphics.Bitmap.createBitmap(Bitmap.java:551)
07-08 00:21:48.319: I/dalvikvm(28645):   at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:437)
07-08 00:21:48.319: I/dalvikvm(28645):   at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:524)
07-08 00:21:48.319: I/dalvikvm(28645):   at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:499)
07-08 00:21:48.319: I/dalvikvm(28645):   at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:351)
07-08 00:21:48.319: I/dalvikvm(28645):   at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:374)
07-08 00:21:48.319: I/dalvikvm(28645):   at

...

 

 

07-08 00:21:48.749: E/AndroidRuntime(28645): FATAL EXCEPTION: main
07-08 00:21:48.749: E/AndroidRuntime(28645): java.lang.OutOfMemoryError
07-08 00:21:48.749: E/AndroidRuntime(28645):  at android.graphics.Bitmap.nativeCreate(Native Method)
07-08 00:21:48.749: E/AndroidRuntime(28645):  at android.graphics.Bitmap.createBitmap(Bitmap.java:605)
07-08 00:21:48.749: E/AndroidRuntime(28645):  at android.graphics.Bitmap.createBitmap(Bitmap.java:551)
07-08 00:21:48.749: E/AndroidRuntime(28645):  at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:437)
07-08 00:21:48.749: E/AndroidRuntime(28645):  at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:524)
07-08 00:21:48.749: E/AndroidRuntime(28645):  at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:499)
07-08 00:21:48.749: E/AndroidRuntime(28645):  at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:351)
07-08 00:21:48.749: E/AndroidRuntime(28645):  at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:374)
07-08 00:21:48.749: E/AndroidRuntime(28645):  at

...

 

이 문제를 해결하기 위해서 이틀 정도를 고생했다.

 

원인은 예상치 못했던 곳에 있었다.

 

어느 정도 감이 있는 사람이라면 다음의 로그를 보고 알아차릴 수 있을 것이다.

 

이 로그는 어플리케이션을 실행해서 이미지들을 보여주는 기능을 수 차례 반복 수행 하면서 기록된 것이다.

 

 

07-08 00:18:30.756: I/_PracticePagerAdapterClass(28645): parent: onPageSelected()=4
07-08 00:18:31.357: I/_PracticePagerAdapterClass(28645): destroyItem: position=1/13, bitmapSize=8191152
07-08 00:18:31.527: I/_PracticePagerAdapterClass(28645): instantiateItem: position=6/13, bitmapSize=8191152
07-08 00:18:43.208: I/_PracticePagerAdapterClass(28645): parent: onPageSelected()=5
07-08 00:18:43.839: I/_PracticePagerAdapterClass(28645): destroyItem: position=2/13, bitmapSize=8992608
07-08 00:18:44.109: I/_PracticePagerAdapterClass(28645): instantiateItem: position=7/13, bitmapSize=18348016
07-08 00:19:15.260: I/_PracticePagerAdapterClass(28645): parent: onPageSelected()=4
07-08 00:19:16.191: I/_PracticePagerAdapterClass(28645): instantiateItem: position=2/13, bitmapSize=8992608
07-08 00:19:16.191: I/_PracticePagerAdapterClass(28645): destroyItem: position=7/13, bitmapSize=18348016
07-08 00:19:32.987: I/_PracticePagerAdapterClass(28645): parent: onDestroy()
07-08 00:19:40.224: I/_PracticePagerAdapterClass(28645): parent: onCreate()
07-08 00:19:41.075: I/_PracticePagerAdapterClass(28645): instantiateItem: position=0/13, bitmapSize=13498784
07-08 00:19:42.716: I/_PracticePagerAdapterClass(28645): instantiateItem: position=1/13, bitmapSize=8191152
07-08 00:19:42.897: I/_PracticePagerAdapterClass(28645): instantiateItem: position=2/13, bitmapSize=8992608
07-08 00:19:54.408: I/_PracticePagerAdapterClass(28645): parent: onDestroy()
07-08 00:20:00.544: I/_PracticePagerAdapterClass(28645): parent: onCreate()
07-08 00:20:01.435: I/_PracticePagerAdapterClass(28645): instantiateItem: position=0/13, bitmapSize=13498784
07-08 00:20:01.585: I/_PracticePagerAdapterClass(28645): instantiateItem: position=1/13, bitmapSize=8191152
07-08 00:20:01.825: I/_PracticePagerAdapterClass(28645): instantiateItem: position=2/13, bitmapSize=8992608
07-08 00:20:06.319: I/_PracticePagerAdapterClass(28645): parent: onDestroy()
07-08 00:21:47.618: I/_PracticePagerAdapterClass(28645): parent: onCreate()

...

 

다음의 이미지에서 확인할 수 있듯이, 페이지는 항상 Instantiate 되었지만 정작 Destroy 되는 순간은

 

현재 보고 있는 페이지가 이동하면서 준비된 페이지의 수가 설정된 최대 준비페이지 수(여기서는 5개)보다 많아졌을 때 뿐이었다.

 

따라서 각각의 Page에 연결되었던 Bitmap Drawable들은 해제되지 못한 채 가비지 컬렉터(GC)에서 처리되지 못 하고 마는 것이다.

 

 

 

 

 

그래서 나는 ViewPager를 포함한 parent에서 onDestory()가 호출될 때 pagerAdapter에 따로 만들어 둔 비트맵 리사이클 메서드를 호출하도록 하였다.

 

결과는 만족스러웠다. 몇 번 액티비티를 들락날락거리면 죽어버리던 앱이 몇 십번을 반복해도 이상이 없었다.

 

참고로, 어떤 식으로 해결했는지에 대해 말하자면

 

adapter 클래스에 전체 페이지 갯수만큼의 크기의 boolean 배열과 ImageView 배열을 만들어서

 

각각의 위치의 페이지들이 instantiate 되었는지 아니면 destory 되었는지 체크해 두었다가

 

parent Activity의 onDestroy()에서 해당 배열을 처리하는 메서드를 호출하는 방법을 사용하였다.

 

예를 들면 다음과 같은 소스코드이다.

 

 

 

 

 

덧붙여 말하자면, ViewPager를 좌, 우로 넘기기만 헀을 뿐인데도 메모리 에러가 생기는 경우는

 

destroyItem()에서 리소스의 뒷처리를 하지 않았기 때문일 가능성이 크다.

 

Posted by Kugi
,