2009-12-23 2 views
30

나는 많은 검색을 해왔으며 은 BitmapFactory과 동일한 OOM 메모리 문제를 겪고 있습니다. 내 앱은 Runtime.getRuntime ().totalMemory()을 사용하여 4MB의 총 메모리 만 표시합니다. 한계가 16MB라면 비트 맵 공간을 확보하기 위해 총 메모리가 늘어나지 않는 이유는 무엇입니까? 대신 오류가 발생합니다.BitmapFactory 바보짓을하는 OOM

나는 또한 내가 말하는 오류가 왜 나는 Runtime.getRuntime().freeMemory()에 에 따라 사용 가능한 메모리 1.6MB이있는 경우 "VM 우리가 614,400 바이트를 할당 할 수 없습니다"이해하지

? 나는 충분히 많다고 생각한다. 사용 가능한 메모리.

내 앱만 실행되도록 전화를 재부팅하면 사라지는이 문제를 제외하고 내 앱이 완료되었습니다. 장치 테스트 용으로 HTC Hero 을 사용하고 있습니다 (Android 1.5).

이 시점에서 나는 이것에 대한 유일한 방법은 어쨌든 무엇이든이라고 생각하고 있습니다 BitmapFactory을 피하십시오.

누구든지 VM에없는 이유에 대한 설명이나 설명이 있습니다. 은 1.6MB의 사용 가능한 메모리가있을 때 614KB를 할당합니까?

+0

[this] (http://stackoverflow.com/questions/477572/android-strange-out-of-memory-issue-while-loading-an-image-to-a-bitmap-object/14731953#14731953) 도움이 될 수 있습니다! –

답변

2

1.6MB의 메모리가 많은 것처럼 보일 수 있지만 메모리가 너무 심하게 조각화되어 한 번에 큰 메모리 블록을 할당 할 수없는 경우가 있습니다 (여전히 매우 이상하게 들립니다).

이미지 리소스를 사용하는 동안 OOM의 한 가지 일반적인 원인은 JPG, PNG, GIF 이미지의 압축률이 매우 높기 때문입니다. 이 모든 포맷은 꽤 잘 압축되어있어 공간을 거의 차지하지 않지만 이미지를 휴대 전화에로드하면 사용하려는 메모리는 width * height * 4 bytes과 같은 것입니다. 또한 압축 해제가 시작될 때 몇 가지 다른 보조 데이터 구조가 디코딩 단계를 위해로드되어야합니다.

45

[이 (CommonsWare가 지적한대로)이 답변의 전체 접근 방식은 2.3.x (진저 브레드)까지만 적용됩니다. Honeycomb 현재 비트 맵 데이터는 VM 힙에 할당됩니다.]

비트 맵 데이터는 VM 힙에 할당되지 않습니다. VM 힙 (작은 크기)에 대한 참조가 있지만 실제 데이터는 기본 Skia 그래픽 라이브러리에 의해 기본 힙에 할당됩니다.

불행히도 BitmapFactory.decode ...()의 정의에 따르면 이미지 데이터를 디코딩 할 수없는 경우 null을 반환하지만 Skia 구현 (Java 코드와 Skia 간의 JNI 아교)은 메시지 ("VM에서 xxxx 바이트를 할당 할 수 없습니다.")와 잘못된 메시지 (비트 맵 크기가 VM 예산 초과)로 OutOfMemory 예외가 발생합니다.

문제는 VM 힙에 없으며 오히려 원시 힙에 있습니다. Natïve 힙은 실행중인 응용 프로그램간에 공유되므로 사용 가능한 공간의 양은 실행중인 다른 응용 프로그램과 해당 비트 맵 사용에 따라 다릅니다. 그러나 BitmapFactory가 반환되지 않는다면 호출하기 전에 호출이 성공하는지 알아낼 방법이 필요합니다.

네이티브 힙의 크기를 모니터하는 루틴이 있습니다 (Debug 클래스 getNative 메소드 참조). 그러나 getNativeHeapFreeSize() 및 getNativeHeapSize()가 신뢰할 수 없다는 것을 발견했습니다. 따라서 많은 수의 비트 맵을 동적으로 만드는 내 응용 프로그램 중 하나에서 다음을 수행합니다.

네이티브 힙 크기는 플랫폼에 따라 다릅니다.따라서 시작시 허용되는 최대 VM 힙 크기를 확인하여 최대 허용 된 기본 힙 크기를 결정합니다. [마법의 숫자는 2.1 및 2.2에서 테스트에 의해 결정되었고, 다른 API 수준에 따라 달라질 수 있습니다.]

long mMaxVmHeap  = Runtime.getRuntime().maxMemory()/1024; 
long mMaxNativeHeap = 16*1024; 
if (mMaxVmHeap == 16*1024) 
    mMaxNativeHeap = 16*1024; 
else if (mMaxVmHeap == 24*1024) 
    mMaxNativeHeap = 24*1024; 
else 
    Log.w(TAG, "Unrecognized VM heap size = " + mMaxVmHeap); 

그런 다음 우리가 BitmapFactory를 호출해야 할 때마다 우리는 형식의 수표로 전화를 앞에. heapPad가) 기본 힙 크기의 보고서는 "소프트"사실 수 있도록하는 마법의 수이고 b)는 우리가 다른 응용 프로그램의 기본 힙에 약간의 공간을 남겨하려는

long sizeReqd  = bitmapWidth * bitmapHeight * targetBpp/8; 
long allocNativeHeap = Debug.getNativeHeapAllocatedSize(); 
if ((sizeReqd + allocNativeHeap + heapPad) >= mMaxNativeHeap) 
{ 
    // Do not call BitmapFactory… 
} 

참고. 우리는 현재 3 * 1024 * 1024 (즉, 3Mbytes) 패드로 실행 중입니다.

+0

매우 흥미롭고 실제적인 접근법. – BonanzaDriver

+0

흥미로운 게시물. 이 파일에 대한 증거가 있습니까? : "Natïve 힙은 실행중인 응용 프로그램간에 공유되므로 사용 가능한 공간의 양은 다른 응용 프로그램이 실행중인 내용과 비트 맵 사용에 따라 다릅니다." – mibollma

+0

이것은 Skia 소스를 읽고, 특히 bitmap.recycle()을 어떻게 처리 하는지를 기반으로했습니다. 그러나 되돌아 보면, 해당 코드에서 응용 프로그램 별 참조/처리가 표시되지 않지만 (이는 해당 성명서로 안내하게 된 것입니다.) 이제는 이것이 너무 강하고/잘못되었다고 생각합니다.네이티브 힙은 VM의 일반적인 프로세스 별 메모리 처리에 의존하는 애플리케이션별로 할당되므로 Skia 코드는 애플리케이션/프로세스 당 문제에 대해 걱정할 필요가 없습니다. – Torid

0

대개는 VM에서만 발생하기 때문에 오류를 잡아내는 것이 좋지 않지만이 경우에는 jni 글루 코드에 의해 오류가 발생하므로로드 할 수없는 경우를 처리하는 것이 매우 간단합니다. 이미지 : 그냥 OutOfMemoryError를 잡아라.

0

이것은 상당히 높은 수준의 대답이지만, 저의 문제는 모든 관점에서 하드웨어 가속을 사용하는 것으로 나타났습니다. 대부분의 내 견해에는 커다란 기본 힙 크기의 원본으로 생각한 사용자 지정 비트 맵 조작이 있지만 사실 하드웨어 가속을 사용하지 않으면 네이티브 힙 사용량이 4 배로 줄어 듭니다.

하드웨어 가속은 모든 종류의 캐싱을 사용자의 관점에서 수행하고 비트 맵을 자체적으로 생성하며 모든 비트 맵이 원시 힙을 공유하기 때문에 할당 크기가 상당히 커질 수 있습니다.

+0

그리고 우리가 ... –

+1

@Henrique 소우 어떻게? 추가 안드로이드 : 놀랍게도이 .. 내가 구글 문서화하는 방법을 제대로 믿을 수 없어 문제를 해결 AndroidManifest.xml에 – samgak

+0

에서 사용자의 작업에 hardwareAccelerated = "false"로이 메모리의 사용량을 배로 ! –

2

Torid's answer에 제공된 문제가 최신 Android 버전에서 해결 된 것 같습니다.

그러나 이미지 캐시 (특수 HashMap 또는 심지어 일반 HashMap)를 사용하는 경우 메모리 누수를 생성하여이 오류를 쉽게 얻을 수 있습니다. 실수로 메모리 누수가 당신의 Bitmap 참조에 잡고 만드는 경우 내 경험에

, 영업 이익의 오류 (AN BitmapFactory 및 기본 방법 참조)가 ICS까지 (앱이 충돌 것입니다 - 14 +?)

이 문제를 방지하려면 비트 맵을 "놓아 버려"만드십시오. 즉, 캐시의 마지막 계층에서 SoftReferences를 사용하면 비트 맵에서 가비지 수집이 가능해집니다. 이 작업은 가능하지만 여전히 크래시가 발생하는 경우 bitmap.recycle()을 사용하여 컬렉션의 특정 비트 맵을 명시 적으로 표시하려고 시도 할 수 있습니다. bitmap.isRecycled() 인 경우 앱에 사용할 비트 맵을 반환하지 마십시오. 여담으로

, LinkedHashMaps 가 쉽게 this example (starting line 308)처럼 하드 및 소프트 참조를 결합 특히, 꽤 좋은 캐시 구조를 구현 ...하지만 하드 참조를 사용을위한 훌륭한 도구입니다 당신이 메모리에 자신을 얻을 수있는 방법도 네가 엉망이라면 누출 상황.

관련 문제