본문 바로가기
프로그래밍 놀이터/안드로이드, Java

[android] Bitmap 과 메모리에 대한 이야기. 끝판왕

by 돼지왕 왕돼지 2014. 1. 26.
반응형


 android, Bitmap 과 메모리에 대한 이야기, 끝판왕!

 


[android] Bitmap 과 메모리에 대한 이야기. 끝판왕


안드로이드 메모리에 대한 이야기.


한 앱은 Dalvik Heap 과 External 두가지 영역이 존재한다.

Dalvik Heap 은 Java 에서 사용하는 메모리라고 보면 되고,

External 은 native 메모리라고 보면 된다.


Dalvik Heap 영역은 메모리가 꽉 차면 늘긴 하지만 그 공간이 줄지는 않는다.

반대로 External 영역은 유동적으로 그 공간이 늘었다 줄었다 한다.

( 특히 Ginger Bread 에서는 Java Object 관리를 잘못하면 심심하면 OOM 을 만날 수 있겠다. )


HoneyComb 이전에는 bitmap 은 native memory 에서 관리했으나,

HoneyComb 이후에는 bitmap 도 dalvik heap 으로 올라왔다.




프로세스당 메모리 = 애플리케이션 메모리 = 앱 메모리


단말기별로 다양하다.

보통 단말기의 화면 크기와 밀도가 커질수록 더 크다.

그만큼 화면을 채우려면 비트맵 이미지의 크기가 더 커져야 하기 떄문.

최초 Android 폰인 G1 의 경우 16MB 였다.

하지만 요즘은 32MB, 48MB, 96MB, 128MB 등으로 다양하다.




단말의 메모리 용량 확인하기.


프로세스 메모리 limit 보기


// MB 단위로  출력된다.

((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();



Dalvik Heap 영역 메모리 보기


 // byte 단위

 Runtime.getRuntime().maxMemory() 

 Runtime.getRuntime().totalMemory()

 Runtime.getRuntime().freeMemory()


// 사용중인 메모리는

// totalMemory() - freeMemory();



cf) 필자의 단말은 Optimus G 인데 기본 Activity 띄우고 측정한 결과.

Total memory 는 96MB.

Dalvik Heap 은 약 19MB.



Native Heap 쪽 정보


Native 만 이용해서 프로그래밍 하는 경우가 아니면 꼭 필요하지 않지만 native 를 많이 이용하는 경우에는 사용할 수 있겠다.


MemoryInfo memInfo = new MemoryInfo();

am.getMemoryInfo( memInfo );


memInfo.totalMem

memInfo.threshold

memInfo.availMem


하지만 중요한 것은 Dalvik Heap 영역의 메모리이다.




Bitmap 은 recycle 을 불러주어야 하는가?


Bitmap 은 recycle 을 불러줄 필요가 있다.

사실 Bitmap 의 경우 finalize() 에서 메모리 해제 작업이 이루어지기는 하지만,

GC 에 따라서 ( 다시 말해 VM 에 따라 ) finalize() 의 호출이 언제 이루어질지 보장할 수도 없고,

운 없는 경우 아예 안 불릴수도 있다고 한다.


따라서 무조건 GC 를 믿고 있기보다는

메모리를 많이 차지하는 bitmap 일수록 유저가 recycle 을 이용해서

메모리를 관리해주는 것이 좋다.




공유하는 비트맵 해제에 대한 이야기.


RecyclingBitmapDrawable 클래스는 BitmapDrawable 클래스를 상속하며 reference count 를 가진다.

이미지 다운로더는 비트맵 대신 RecyclingBitmapDrawable 객체를 생성해서 반환한다.


RecyclingImageView 클래스는 ImageView 클래스를 상속하고, 다음 두 가지 메서드를 재정의한다.

setImageDrawable()

drawable 객체를 주입시킬 때 RecyclingBitmapDrawable 이라면 reference count 를 증가시킨다.

onDetachFromWindow()

window 에서 보이지 않게 되면 RecyclingBitmapDrawable 의 reference count 를 증가시키고, 이 때 0이면 recycle() 메서드를 호출한다.


cf) 이 RecyclingBitmapDrawable 과 RecyclingImageView 는 기본 Framework 에 들어있는 Component 는 아니나, Bitmap 관리에 필요하여 확장된 녀석들이다.




이미지 품질을 줄여서 메모리 줄이기.


비트맵 디코딩 옵션에 따라 메모리 사용량이 달라진다.

RGB_888 보다는 RGB_565 가 메모리를 아끼고,

RGBA 보다는 RGB 가 메모리를 아낀다.


JPG 같은 경우는 A 가 없으므로 RGB 를 사용하는 것이 메모리를 아낄 수 있다. 


res 폴더의 경우 aapt 가 자동으로 이미지를 분석해서 이미지를 최적화시키기 때문에 decoding 에 큰 문제가 없으나, network 에서 불러온 녀석이나, asset 폴더에 있는 녀석들은 디코딩 옵션에 따라 메모리에 큰 영향력을 행사할 수도 있다.







Dalvik Heap 을 늘리자.


둘다 honey comb 이후부터 작동한다.


@Code

((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getLargeMemoryClass();



@xml

android:largeHeap="true"




apk 로 파일로 압축되는 파일 목록 줄이기.


android 는 앱을 실행할 때 .apk 파일에 든 모든 파일의 엔트리 정보를 메모리에 로딩한다.

res 나 asset 폴더에 그만큼 많은 resource 들이 있다면, 이 리스트들만 가지고도 메모리 부담이 될 수 있다.


만약 asset 부분에 파일이 많이 들어가게 된다면, ( 압축이 안 되므로 더 많은 메모리 차지.? )

DB 의 BLOC 칼럼을 만들어서 저장하는 것이 훨씬 좋다.

덧붙여 DB 에서 파일을 가져오는 것이 File 에 접근하는 것보다 더 빠를 수 있다.




최악의 상황에서는 프로세스를 분리하자.


Process 를 분리해서 처리하게 되면, 각각의 process 에서 사용가능한 메모리를 사용할 수 있지만,

그렇게 좋은 방법은 아니다.

항상 메모리 최적화를 최우선으로 고려하고, 그래도 안 되면 process 분리를 고려해야 한다.




Native Memory 를 사용하자.


여러가지 최적화를 해도 안 된다면 마지막으로 native heap 을 사용해볼 수 있다.

다만 native heap 을 사용하기 위해서는 JNI 와 C/C++ 코드를 사용해야 하기 때문에

유지보수도 어려워지고, 더 많은 인력이 쓰일 수 있다.


주로 메모리를 많이 쓰는 게임 앱 등에서만 사용한다.






반응형

댓글