java.lang.OutOfMemoryError:位图大小超过VM预算

时间:2011-01-06 04:44:06

标签: android memory-leaks

我正在尝试将应用程序的布局从纵向更改为横向,反之亦然。 但是如果我经常这样做或不止一次,那么我的应用程序有时会崩溃。下面是错误日志。请建议可以做些什么?

 01-06 09:52:27.787: ERROR/dalvikvm-heap(17473): 1550532-byte external allocation too large for this process. 01-06 09:52:27.787: ERROR/dalvikvm(17473): Out of memory: Heap Size=6471KB, Allocated=4075KB, Bitmap Size=9564KB 
 01-06 09:52:27.787: ERROR/(17473): VM won't let us allocate 1550532 bytes 
 01-06 09:52:27.798: DEBUG/skia(17473): --- decoder->decode returned false
 01-06 09:52:27.798: DEBUG/AndroidRuntime(17473): Shutting down VM
 01-06 09:52:27.798: WARN/dalvikvm(17473): threadid=3: thread exiting with uncaught exception (group=0x4001e390)
 01-06 09:52:27.807: ERROR/AndroidRuntime(17473): Uncaught handler: thread main exiting due to uncaught exception
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473): java.lang.RuntimeException: Unable to start activity ComponentInfo{}: android.view.InflateException: Binary XML file line #2: Error inflating class <unknown
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2596) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2621) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3812) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.app.ActivityThread.access$2300(ActivityThread.java:126) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1936) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.os.Handler.dispatchMessage(Handler.java:99) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.os.Looper.loop(Looper.java:123) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.app.ActivityThread.main(ActivityThread.java:4595) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at java.lang.reflect.Method.invokeNative(Native Method)
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at java.lang.reflect.Method.invoke(Method.java:521) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at dalvik.system.NativeStart.main(Native Method)
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473): Caused by: android.view.InflateException: Binary XML file line #2: Error inflating class <unknown
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.view.LayoutInflater.createView(LayoutInflater.java:513) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:563) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.view.LayoutInflater.inflate(LayoutInflater.java:385) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.view.LayoutInflater.inflate(LayoutInflater.java:320) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.view.LayoutInflater.inflate(LayoutInflater.java:276) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:207) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.app.Activity.setContentView(Activity.java:1629) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at onCreate(Game.java:98)
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2544) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     ...  12 more
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473): Caused by: java.lang.reflect.InvocationTargetException 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.widget.LinearLayout.<init>(LinearLayout.java:92) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at java.lang.reflect.Constructor.constructNative(Native Method)
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at java.lang.reflect.Constructor.newInstance(Constructor.java:446) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.view.LayoutInflater.createView(LayoutInflater.java:500) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     ...  22 more
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473): Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:464) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:340) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.content.res.Resources.loadDrawable(Resources.java:1705) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.content.res.TypedArray.getDrawable(TypedArray.java:548) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.view.View.<init>(View.java:1850) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.view.View.<init>(View.java:1799) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     at android.view.ViewGroup.<init>(ViewGroup.java:296) 
 01-06 09:52:27.857: ERROR/AndroidRuntime(17473):     ...  26 more 

12 个答案:

答案 0 :(得分:50)

我发现开发Android应用程序的最常见错误之一是

  

java.lang.OutOfMemoryError:位图大小超过VM预算

错误。我在更改方向后使用大量位图的活动中经常发现此错误:活动被销毁,再次创建,布局从消耗可用于位图的VM内存的XML中“膨胀”。

垃圾收集器未正确释放前一个Activity布局上的位图,因为它们已经交叉引用了它们的活动。经过多次实验,我发现了一个很好的解决方案。

首先,在XML布局的父视图上设置“id”属性:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/RootView">
        ...

然后,在您的Activity的onDestroy()方法上,调用unbindDrawables()方法传递对父视图的引用,然后执行System.gc()

    @Override
    protected void onDestroy() {
        super.onDestroy();

        unbindDrawables(findViewById(R.id.RootView));
        System.gc();
    }

    private void unbindDrawables(View view) {
        if (view.getBackground() != null) {
            view.getBackground().setCallback(null);
        }
        if (view instanceof ViewGroup) {
            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
                unbindDrawables(((ViewGroup) view).getChildAt(i));
            }
            ((ViewGroup) view).removeAllViews();
        }
    }

这个unbindDrawables()方法以递归方式探索视图树:

  1. 删除所有背景drawable上的回调
  2. 删除每个视图组中的子项

答案 1 :(得分:12)

我有java.lang.OutOfMemoryError:位图大小多次超过VM预算错误

1)稍后从应用程序切换回应用程序时(按主页按钮并浏览一些网址后)

2)经常登录/退出应用程序(10秒内)

3)水平/垂直切换设备时

最后我按照以下方式解决了错误

    public void clearAllResources() {

    // Set related variables null

    System.gc();
    Runtime.getRuntime().gc();
   }

答案 2 :(得分:8)

这是一个实用的答案,我试图在运行时避免这个问题

Runtime.getRuntime().gc();

调用垃圾收集器是一个好主意。

答案 3 :(得分:4)

将这行代码添加到“app”标签内的Android Manifest File中: - android:largeHeap =“true”

<application
         android:theme="@android:style/Theme.NoTitleBar"
        android:allowBackup="true"
        android:largeHeap="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
       >

答案 4 :(得分:3)

您正在泄漏资源(很可能是Drawable对象)。试试What Android tools and methods work best to find memory/resource leaks?

中的工具

答案 5 :(得分:3)

图像源来自哪里?你的drawables文件夹中有9MB的图像吗?

关于Android的本机位图内存管理问题,有很多讨论,例如: http://code.google.com/p/android/issues/detail?id=10821http://code.google.com/p/android/issues/detail?id=8488http://groups.google.com/group/android-developers/browse_thread/thread/ed57849ef705d421/11af8362d77a8cf4?lnk=raot。每次我完成一个位图时,我都很好地明确地调用了recycle()和System.gc(),例如:

private void changeImage() {
     if (mCurrentImage != null) {
          mCurrentImage.recycle();
          mCurrentImage = null;
          System.gc();
     }
     mCurrentImage = getNewImage();
}

答案 6 :(得分:3)

首先通过捕获您认为可能产生此错误的地方的OutOfMemory错误来阻止您的应用程序在运行时崩溃,如下所示:

try {

...

}
catch(OutOfMemoryError error)  {
    //decide what to do when there is not more memory available
}

答案 7 :(得分:3)

您可以使用opts.inSampleSize=2; or opts.inSampleSize=4

解决问题
BitmapFactory.Options opts = new BitmapFactory.Options();
// opts.inJustDecodeBounds = true; 
opts.inSampleSize=2;    
Bitmap myBitmap = BitmapFactory.decodeFile(st_imagepath,opts);

答案 8 :(得分:2)

Angel,StackOverflow上发布了类似的问题@ OutOfMemory exception when loading bitmap from external storage

从那里我推断SoftReference也可以作为解决方案的替代方案。请查看它可能证明有用的帖子。

答案 9 :(得分:2)

我对@ hp.android的优秀答案进行了一些扩展:

  1. 我没有在每个XML类中定义RootView ID,而是使用android.R.id.content动态查找根视图。
  2. 正如@Mario Lenci所说,AdapterView不支持removeAllViews,因此我在调用removeAllViews()之前检查了视图的类型。
  3. IMO在每个onDestroy调用中调用System.gc()都不是一个好主意(例如this answer),所以我删除了它。
  4. 我创建了一个静态onDestroy方法,我在所有自定义Activity类中都从onDestroy调用。
  5. 我添加了一些额外的错误保护和日志记录。
  6. 以下是代码:

        public static void onDestroy(Activity activity) {
            View rootView = null;
    
            try {            
                rootView = ((ViewGroup) activity.findViewById(android.R.id.content))
                        .getChildAt(0);
            } catch (Exception e) {
                Log.w("Cannot find root view to call unbindDrawables on",
                      activity);
            }
    
            if (rootView != null) {
                Log.d("unbindDrawables", activity, rootView);
                unbindDrawables(rootView);
            }
        }
    
        /**
          * Utility method to unbind drawables when an activity is destroyed.  This
          * ensures the drawables can be garbage collected.
          */
         public static void unbindDrawables(View view) {
             if (view.getBackground() != null) {
                 view.getBackground().setCallback(null);
             }
    
             if (view instanceof ViewGroup) {
                 for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
                     unbindDrawables(((ViewGroup) view).getChildAt(i));
                 }
    
                 try {                
                     // AdapterView objects do not support the removeAllViews method
                     if (!(view instanceof AdapterView)) {             
                         ((ViewGroup) view).removeAllViews();
                     }
                 } catch (Exception e) {
                     Log.w("Ignore Exception in unbindDrawables", e);
                 }
             }
         }
    

答案 10 :(得分:0)

1550532字节分配听起来很大。我猜你的布局有一个某种位图图像的参考,也许是PNG?这个PNG的尺寸是多少?如果它真的很大并且你有多个它们可能没有足够的内存来一次加载它们。

此外,如果旋转触发此操作,请记住您的Activity已被销毁并重新创建,因此您可能需要注意静态引用。看一下这个Google blog post。您需要注意保留代码中可能导致泄漏的静态引用

答案 11 :(得分:0)

http://yekmer.posterous.com/android-memory-management

可能对此网站有所帮助。您应该释放所有参考资料。