安卓:内存不足异常的画廊 [英] Android: out of memory exception in Gallery

查看:124
本文介绍了安卓:内存不足异常的画廊的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用程序显示了9类列表,每个类别显示一个画廊为主的CoverFlow(由尼尔·戴维斯的这里)与所选类别的照片。照片 图像是从Web获取,每个范围从300K到500K的大小,并储存在可绘制的ArrayList。此数据绑定到使用BaseAdapter(低于code)中的CoverFlow。
每次我退出的CoverFlow,并返回到类别列表中,我明确的ArrayList(再次跌破code)。
在情景1,我ArrayList包含5可绘制。在这种情况下,我可以自由地浏览所有的类别和展示自己的形象。在我的测试中,我通过所有类别循环5次,这似乎足以确定是没有问题的。照片 在方案2中,我ArrayList包含10可绘制。

:在这种情况下,我虽然经历图像的第五或第六categeory内得到一个OutOfMemoryError异常

八月七日至13日:38:21.266:ERROR / dalvikvm堆(2133):819840字节的外部分配太大,此过程。
八月七日至13日:38:21.266:ERROR /(2133):虚拟机不会让我们分配819840字节
八月七日至13日:38:21.277:DEBUG / Skia的(2133):---德codeR->德code返回false
八月七日至13日:38:21.287:WARN / dalvikvm(2133):主题ID = 25:线程退出与未捕获的异常(组= 0x4001b188)
八月七日至13日:38:21.296:ERROR / AndroidRuntime(2133):未捕获的处理程序:螺纹螺纹-64退出,由于未捕获的异常
八月七日至13日:38:21.308:ERROR / AndroidRuntime(2133):java.lang.OutOfMemoryError:位图大小超过VM预算
八月七日至13日:38:21.308:ERROR / AndroidRuntime(2133):在android.graphics.BitmapFactory.nativeDe codeStream(本机方法)
八月七日至13日:38:21.308:ERROR / AndroidRuntime(2133):在android.graphics.BitmapFactory.de codeStream(BitmapFactory.java:459)
八月七日至13日:38:21.308:ERROR / AndroidRuntime(2133):在android.graphics.BitmapFactory.de codeResourceStream(BitmapFactory.java:323)
八月七日至13日:38:21.308:ERROR / AndroidRuntime(2133):在android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697)
八月七日至13日:38:21.308:ERROR / AndroidRuntime(2133):在android.graphics.drawable.Drawable.createFromStream(Drawable.java:657)

这是没有道理给我。如果我泄漏内存我没有料想到会崩溃在方案1中的一些观点,但我经历了所有的类别去的次数相当多,并没有崩溃。我还使用了内存分析器的Eclipse插件里面也没有present任何潜在的罪魁祸首。
如果系统无法处理的scenarion 2 10的图像一样,我本来期望崩溃第一类,但我崩溃之后才5或6大类。
有些code:

本的CoverFlow的适配功能:

 公众诠释getCount将(){
     返回DataManager.getInstance()getImageBufferInstance()getImageArraySize()。
}

公共对象的getItem(INT位置){
     返回DataManager.getInstance()getImagesBuffer()得到(位置)。
}

众长getItemId(INT位置){
     返回的位置;
}

公共查看getView(INT位置,查看convertView,ViewGroup中父){
         ImageView的我;
         如果(convertView == NULL)
             I =新ImageView的(mContext);
         其他
             I =(ImageView的)convertView;
         可绘制的BufferedImage =(抽出式)的getItem(位置);
         Log.v(getView,位置:+位置);
         i.setImageDrawable(BufferedImage的);

         i.setLayoutParams(新CoverFlow.LayoutParams(Utils.getInstance()。getScreenWidth()/ 2,
                 Utils.getInstance()getScreenHeight()/ 2))。
         i.setScaleType(ImageView.ScaleType.CENTER_INSIDE);

         尝试{
         //确保我们设置抗锯齿,否则我们得到锯齿
         BitmapDrawable绘制=(BitmapDrawable)i.getDrawable();
         drawable.setAntiAlias​​(真正的);
         }
         赶上(例外五)
         {
             Log.v(getView,异常:+ e.toString());
         }
         返回我;
     }
 

在进入该类别填充数据源:

 的for(int i = 0; I< ImageBuffer.getInstance()getImageArraySize();我++)
{
  串IMAGEURL = ImageBuffer.getInstance()getImageUrl(i)中。;
  Log.v(初始,IMAGEURL);
  可绘制fullImage = AsyncImageLoader.getInstance()loadImageByUrl(IMAGEURL)。
  ImageBuffer.getInstance()getImages()加(我,fullImage)。

}
 

退出类时清除数据源(完成()):

 的for(int i = 0; I< ImageBuffer.getInstance()getImageArraySize();我++)
{
  如果(ImageBuffer.getInstance()。images.get(ⅰ)!= NULL)
            {
                。ImageBuffer.getInstance()images.get(我).setCallback(空);
                。ImageBuffer.getInstance()images.set(我,NULL);
            }

}
 

编辑:

OK,我申请我的CoverFlow马蒂亚斯'LogHeap功能,这里有一些输出。 在此之前加载第一个画廊:

DEBUG /应用(5221):调试。 =================================
DEBUG /应用(5221):debug.heap原生:6.28MB分配6.20MB(0.07MB免费)在[com.example.Coverflow]
DEBUG /应用(5221):debug.memory:分配:24.00MB的4.00MB(0.00MB免费)
DEBUG / dalvikvm(5221):GC释放4558对象/ 638152字节84ms
DEBUG / dalvikvm(5221):GC释放17的对象/ 808字节的67ms

在进入第一个画廊后:

DEBUG /应用(5221):调试。 =================================
DEBUG /应用(5221):debug.heap原生:分配14.90MB 16.89MB(0.07MB免费)在[com.example.Coverflow]的
DEBUG /应用(5221):debug.memory:分配:24.00MB的4.00MB(1.00MB免费)
DEBUG / dalvikvm(5221):GC释放357对象/ 50080字节68ms
DEBUG / dalvikvm(5221):GC释放353对象/ 27312字节67ms

现有的第一家画廊后:

DEBUG /应用(5221):调试。 =================================
DEBUG /应用(5221):debug.heap原生:分配14.83MB 16.89MB(0.11MB免费)在[com.example.Coverflow]的
DEBUG /应用(5221):debug.memory:分配:24.00MB的4.00MB(1.00MB免费)
DEBUG / dalvikvm(5221):GC释放330对象/ 17920字节77ms
调试/ dalvikvm(5221):GC释放13的对象/ 760字节在67ms

进入第五个画廊后:

DEBUG /应用(5221):调试。 =================================
DEBUG /应用(5221):debug.heap原生:分配16.80MB 23.32MB(0.08MB免费)在[com.example.Coverflow]的
DEBUG /应用(5221):debug.memory:分配:24.00MB的4.00MB(1.00MB免费)
DEBUG / dalvikvm(5221):GC释放842对象/ 99256字节73ms
DEBUG / dalvikvm(5221):GC释放306对象/ 24896字节69ms

退出第五画廊后:

DEBUG /应用(5221):调试。 =================================
DEBUG /应用(5221):debug.heap原生:分配16.74MB 23.32MB(0.11MB免费)在[com.example.Coverlow]的
DEBUG /应用(5221):debug.memory:分配:24.00MB的4.00MB(1.00MB免费)
DEBUG / dalvikvm(5221):GC释放331对象/ 18184字节68ms
DEBUG / dalvikvm(5221):GC释放60的对象/ 3128字节的68ms

看来,进入画廊,当越来越多的内存分配,但很少有人退出后释放。我不能清除我可绘制正确?对于我可绘制的ArrayList中的每个元素我叫setCallBack(空),并设置元素设置为null。难道这还不够?
绝望的任何见解。
谢谢

解决方案
  

该图像是从网上获取,   每个从300K到500K中   大小,并储存在的ArrayList   可绘制。

您是从网络加载图像的KB的文件大小没有直接关系。由于它们转换成位图,你需要计算每幅图像宽*高* 4个字节进行定期ARGB图像。 (宽度和高度像素)。

位图消耗本机堆,它通常不会在HPROF显示。该HPROF应该只能说明你的对象,即BitmapDrawables或位图所留下的号码。

我用这个code。在我的应用程序的输出使用的应用程序和本机堆当前使用的内存:

 公共静态无效logHeap(类clazz所){
    双人分配=新的双(Debug.getNativeHeapAllocatedSize())/双新((1048576));
    双可用=新的双(Debug.getNativeHeapSize())/ 1048576.0);
    双人自由=新的双(Debug.getNativeHeapFreeSize())/ 1048576.0);
    DecimalFormat的DF =新的DecimalFormat();
    df.setMaximumFractionDigits(2);
    df.setMinimumFractionDigits(2);

    Log.d(APP,调试=================================。);
    Log.d(APP,debug.heap本土:拨+ df.format(分配)+MB的+ df.format(可选)+MB(+ df.format(免费)+免费MB)在[+ clazz.getName()的replaceAll。(com.myapp.android,)+]);
    Log.d(APP,debug.memory:分配:+ df.format(新双(调用Runtime.getRuntime()totalMemory()/ 1048576))+MB的+ df.format(双新(运行时。 。getRuntime()maxMemory()/ 1048576))+MB)+免费MB))(+ df.format(新双(调用Runtime.getRuntime()freeMemory()/ 1048576);
    System.gc()的;
    System.gc()的;

    //不需要加上下面几行,它只是一个应用程序的具体操作在我的应用程序
    如果(分配> =(新双(调用Runtime.getRuntime()maxMemory())/新双((1048576)) -  MEMORY_BUFFER_LIMIT_FOR_RESTART)){
        android.os.Process.killProcess(android.os.Process.myPid());
    }
}
 

这是我在开始或在开发过程中完成一个活动时调用。

  logHeap(this.getClass());
 

下面是一些参考信息 - 通常有很多关于这个话题的线程就在这里

  • 位图在Android的
  • <一个href="http://stackoverflow.com/questions/2203806/android-eclipse-mat-does-not-appear-to-show-all-my-apps-objects">Android: Eclipse的MAT似乎没有显示我的所有应用程序的对象

下面也被罗曼盖伊(Android框架工程师)关于软引用,弱引用,简单的缓存,图像处理有用的幻灯片: <一href="http://docs.huihoo.com/google/io/2009/Th_0230_TurboChargeYourUI-HowtomakeyourAndroidUIfastandefficient.pdf">http://docs.huihoo.com/google/io/2009/Th_0230_TurboChargeYourUI-HowtomakeyourAndroidUIfastandefficient.pdf

My app shows a list of 9 categories and each category displays a Gallery-based coverflow (graciously offered by Neil Davies here) with images of the selected category.
The images are fetched from the Web, each ranging from 300K to 500K in size, and stored in an arrayList of Drawables. This data is bound to the coverflow using a BaseAdapter (code below).
Every time I exit the coverflow and go back to the list of categories, I clear the arrayList (again, code below).
In scenario 1, my arrayList contains 5 Drawables. In this scenario, I can freely browse all the categories and show their images. During my test I cycled through all the categories for 5 times, which seems enough to determine that there is no problem.
In scenario 2, my arrayList contains 10 drawables. In this scenario, I get an OutOfMemoryError exception while going through images inside the 5th or 6th categeory:

07-13 08:38:21.266: ERROR/dalvikvm-heap(2133): 819840-byte external allocation too large for this process.
07-13 08:38:21.266: ERROR/(2133): VM won't let us allocate 819840 bytes
07-13 08:38:21.277: DEBUG/skia(2133): --- decoder->decode returned false
07-13 08:38:21.287: WARN/dalvikvm(2133): threadid=25: thread exiting with uncaught exception (group=0x4001b188)
07-13 08:38:21.296: ERROR/AndroidRuntime(2133): Uncaught handler: thread Thread-64 exiting due to uncaught exception
07-13 08:38:21.308: ERROR/AndroidRuntime(2133): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
07-13 08:38:21.308: ERROR/AndroidRuntime(2133):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
07-13 08:38:21.308: ERROR/AndroidRuntime(2133):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:459)
07-13 08:38:21.308: ERROR/AndroidRuntime(2133):     at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:323)
07-13 08:38:21.308: ERROR/AndroidRuntime(2133):     at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697)
07-13 08:38:21.308: ERROR/AndroidRuntime(2133):     at android.graphics.drawable.Drawable.createFromStream(Drawable.java:657)

This doesn't make sense to me. If I am leaking memory I would have expected to crash at some point in scenario 1, but I went through all the categories a substantial number of times and didn't crash. I also used the Memory Analyzer plugin for Eclipse which didn't present any potential culprits.
If the system couldn't handle 10 images, like in scenarion 2, I would have expected to crash in the first category, but I crash only after 5 or 6 categories.
Some code:

The coverflow's adapter functions:

public int getCount() {
     return DataManager.getInstance().getImageBufferInstance().getImageArraySize(); 
}

public Object getItem(int position) {    
     return DataManager.getInstance().getImagesBuffer().get(position);
}

public long getItemId(int position) {
     return position;
}

public View getView(int position, View convertView, ViewGroup parent) {      
         ImageView i;
         if (convertView == null)
             i = new ImageView(mContext);
         else
             i = (ImageView)convertView;
         Drawable bufferedImage = (Drawable)getItem(position);
         Log.v("getView", "position: " + position);
         i.setImageDrawable(bufferedImage);

         i.setLayoutParams(new CoverFlow.LayoutParams(Utils.getInstance().getScreenWidth() / 2,
                 Utils.getInstance().getScreenHeight() / 2));
         i.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 

         try{
         //Make sure we set anti-aliasing otherwise we get jaggies
         BitmapDrawable drawable = (BitmapDrawable) i.getDrawable();
         drawable.setAntiAlias(true);
         }
         catch (Exception e)
         {
             Log.v("getView", "Exception: " + e.toString());
         }
         return i;      
     }

populating the data source upon entry to the category:

for (int i = 0; i < ImageBuffer.getInstance().getImageArraySize(); i++)  
{  
  String imageUrl = ImageBuffer.getInstance().getImageUrl(i);  
  Log.v("Initial", imageUrl);  
  Drawable fullImage = AsyncImageLoader.getInstance().loadImageByUrl(imageUrl);  
  ImageBuffer.getInstance().getImages().add(i, fullImage);  

}

clearing the data source when exiting the category (in finish()):

for (int i = 0; i < ImageBuffer.getInstance().getImageArraySize(); i++)  
{  
  if (ImageBuffer.getInstance().images.get(i) != null)  
            {  
                ImageBuffer.getInstance().images.get(i).setCallback(null);  
                ImageBuffer.getInstance().images.set(i, null);  
            }    

}

EDIT:

OK, I applied Mathias' LogHeap function on my coverflow and here are some outputs. Prior to loading the first gallery:

DEBUG/Application(5221): debug. =================================
DEBUG/Application(5221): debug.heap native: allocated 6.20MB of 6.28MB (0.07MB free) in [com.example.Coverflow]
DEBUG/Application(5221): debug.memory: allocated: 4.00MB of 24.00MB (0.00MB free)
DEBUG/dalvikvm(5221): GC freed 4558 objects / 638152 bytes in 84ms
DEBUG/dalvikvm(5221): GC freed 17 objects / 808 bytes in 67ms

After entering the first gallery:

DEBUG/Application(5221): debug. =================================
DEBUG/Application(5221): debug.heap native: allocated 14.90MB of 16.89MB (0.07MB free) in [com.example.Coverflow]
DEBUG/Application(5221): debug.memory: allocated: 4.00MB of 24.00MB (1.00MB free)
DEBUG/dalvikvm(5221): GC freed 357 objects / 50080 bytes in 68ms
DEBUG/dalvikvm(5221): GC freed 353 objects / 27312 bytes in 67ms

After existing the first gallery:

DEBUG/Application(5221): debug. =================================
DEBUG/Application(5221): debug.heap native: allocated 14.83MB of 16.89MB (0.11MB free) in [com.example.Coverflow]
DEBUG/Application(5221): debug.memory: allocated: 4.00MB of 24.00MB (1.00MB free)
DEBUG/dalvikvm(5221): GC freed 330 objects / 17920 bytes in 77ms
DEBUG/dalvikvm(5221): GC freed 13 objects / 760 bytes in 67ms

After entering the fifth gallery:

DEBUG/Application(5221): debug. =================================
DEBUG/Application(5221): debug.heap native: allocated 16.80MB of 23.32MB (0.08MB free) in [com.example.Coverflow]
DEBUG/Application(5221): debug.memory: allocated: 4.00MB of 24.00MB (1.00MB free)
DEBUG/dalvikvm(5221): GC freed 842 objects / 99256 bytes in 73ms
DEBUG/dalvikvm(5221): GC freed 306 objects / 24896 bytes in 69ms

After exiting the fifth gallery:

DEBUG/Application(5221): debug. =================================
DEBUG/Application(5221): debug.heap native: allocated 16.74MB of 23.32MB (0.11MB free) in [com.example.Coverlow]
DEBUG/Application(5221): debug.memory: allocated: 4.00MB of 24.00MB (1.00MB free)
DEBUG/dalvikvm(5221): GC freed 331 objects / 18184 bytes in 68ms
DEBUG/dalvikvm(5221): GC freed 60 objects / 3128 bytes in 68ms

It seems that more and more memory is allocated when entering a gallery, but very little is released after exiting. Am I not clearing my drawables properly? For each element in my arrayList of drawables I call setCallBack(null) and set the element to null. Is that not enough?
Desperate for any insight.
Thanks

解决方案

The images are fetched from the Web, each ranging from 300K to 500K in size, and stored in an arrayList of Drawables.

The kb file size of the image you're loading from the web isn't directly relevant. Since they're converted into bitmaps you need to calculate width * height * 4 bytes per image for regular ARGB images. (width and height in px).

The bitmaps consume native heap, which usually doesn't show in a hprof. The hprof should only show you the number of objects, i.e. BitmapDrawables or Bitmaps that are left.

I use this code in my app to output the current used memory used by the app and native heap:

public static void logHeap(Class clazz) {
    Double allocated = new Double(Debug.getNativeHeapAllocatedSize())/new Double((1048576));
    Double available = new Double(Debug.getNativeHeapSize())/1048576.0);
    Double free = new Double(Debug.getNativeHeapFreeSize())/1048576.0);
    DecimalFormat df = new DecimalFormat();
    df.setMaximumFractionDigits(2);
    df.setMinimumFractionDigits(2);

    Log.d(APP, "debug. =================================");
    Log.d(APP, "debug.heap native: allocated " + df.format(allocated) + "MB of " + df.format(available) + "MB (" + df.format(free) + "MB free) in [" + clazz.getName().replaceAll("com.myapp.android.","") + "]");
    Log.d(APP, "debug.memory: allocated: " + df.format(new Double(Runtime.getRuntime().totalMemory()/1048576)) + "MB of " + df.format(new Double(Runtime.getRuntime().maxMemory()/1048576))+ "MB (" + df.format(new Double(Runtime.getRuntime().freeMemory()/1048576)) +"MB free)");
    System.gc();
    System.gc();

    // don't need to add the following lines, it's just an app specific handling in my app        
    if (allocated>=(new Double(Runtime.getRuntime().maxMemory())/new Double((1048576))-MEMORY_BUFFER_LIMIT_FOR_RESTART)) {
        android.os.Process.killProcess(android.os.Process.myPid());
    }
}

which I call when starting or finishing an activity during development.

logHeap(this.getClass());

Here are some informative links - generally there are lots of threads about this topic on here.

Here's also a useful slide by Romain Guy (Android Framework engineer) about soft references, weak references, simple caches, image handling: http://docs.huihoo.com/google/io/2009/Th_0230_TurboChargeYourUI-HowtomakeyourAndroidUIfastandefficient.pdf

这篇关于安卓:内存不足异常的画廊的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆