• 在Android中解决内存溢出 – OutOfMemoryError


    原文链接:http://riggaroo.co.za/fixing-memory-leaks-in-android-outofmemoryerror/

    注:本文在原文基础上在如何判断内存是否泄露方面进行了补充

    安卓开发中经常出现内存溢出的情况,没有防备的开发者可能一天会不经意间写好几个内存溢出的漏洞。你可能不会发现这些漏洞,甚至都不知道它们存在,直到你看到这种异常:

    java.lang.OutOfMemoryError: Failed to allocate a 4308492 byte allocation with 467872 free bytes and 456KB until OOM
    at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
    at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:609)
    at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:444)
    at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:988)
    at android.content.res.Resources.loadDrawableForCookie(Resources.java:2580)
    at android.content.res.Resources.loadDrawable(Resources.java:2487)
    at android.content.res.Resources.getDrawable(Resources.java:814)
    at android.content.res.Resources.getDrawable(Resources.java:767)
    at com.nostra13.universalimageloader.core.DisplayImageOptions.getImageOnLoading(DisplayImageOptions.java:134)
    

    这是啥意思呢?难道我的Bitmap太大了?

    这种异常信息可能会让你产生误解。当你看到OutOfMemoryError的时候,十有八九是内存泄露。我第一次看到这个就以为我的Bitmap太大了,唉当时的我太年轻了。

    什么是内存泄露?

    内存泄露指程序中没有正确的释放用完的内存空间,导致运行异常或错误

    Android中内存泄露是如何发生的?

    事实上Android中内存泄露很容易发生。最大的问题就是Context对象。

    每 一个App都有一个全局的Application Context对象,可以通过getApplicationContext()方法获得。每一个Activity都是Context的子类,会存储有关当 前Activity的信息。很多情况下,内存泄露与Activity泄露有关。比如有的开发者会将Context对象在各个线程里传来传去,写一些静态的 TextView,让它们持有Activity的引用,这就是一个典型的错误。

    判断是否有内存泄露

    Memory Monitor

    1. 将设备或模拟器与Android Studio连接。

    2. 在Android Studio中运行程序。

    3. 在 屏幕下方点击Android标签,再点击Memory标签,选择设备与要监控的应用。一旦Memory Monitor开始监控设备,就会出现蓝色的图表显示内存使用情况。深蓝色部分为当前应用正在使用的内存,浅蓝色(深蓝色上面那一块)为可以使用、未被分 配的内存空间。

      演示图片

    4. 点击垃圾车图标(上图右侧那个小图标)可以触发GC(内存回收)。

    有内存溢出漏洞的应用在不断操作并触发GC后的图表不断上升的:

    有内存泄露

    内存泄露被解决后的应用不断操作并不断触发GC后图表波动但维持稳定:

    无内存泄露

    DDMS

    打 开DDMS界面,在左侧面板中选择要观察的进程,点击左上角的Update Heap按钮(红圈圈出的圆柱体按钮),再点击右侧面板中的Heap标签,然后不断操作并持续点击Cause GC按钮,观察表中data object的Total Size的数值变化。如果持续增高而不下降,那就很可能是有内存泄露发生了。

    DDMS

    有内存泄露时,应用不会重新得到用过的内存空间,当用到300MB的时候,OutOfMemoryError就发生了。从修复了内存泄露的应用的图表可以看出,应用可以回收一些内存,并处在长期稳定状态。

    如何避免内存泄露?

    • 不要将Context对象传给activity与fragment以外的对象。

    • 永远不要将Context和View存储在静态变量中。

        private static TextView textView;//不要这么干
        private static Context context;//不要这么干
      
    • 在onPause()/onDestroy()方法中解除监听器,包括在Android自己的Listener,Location Service或Display Manager Service以及自己写的Listener。

    • 不要在后台线程与AsyncTask中存储activity的强引用。不然当Activity被关闭后,由于AsyncTask仍在执行且持有Activity的强引用,导致Activity无法被回收。

    • 使用Application Context而不是Activity的Context

    • 尽量不要用非静态内部类,因为它会持有外部类的引用。在非静态内部类中存储Activity或View的引用会导致内存泄露。如需存储就使用WeakReference。

    如何解决内存泄露?

    解决内存泄露需要大量的错误积累与练习。内存泄露可能会很难追踪,但借助一些工具可以帮助你识别可能的漏洞。这里我们使用MAT进行内存泄露的检测。

    1. 打开Android Studio,点击下方的Android Monitor标签。

    2. 运行应用,并选择应用来监控。

    3. 反复进行某个可能存在内存泄露的操作,比如返回打开关闭某个Activity。

    4. 在抛出OutOfMemoryException之前点击Android Monitor中的Memory标签。此时图表开始绘制,过一会点击GC图标。(刚才说的那个红色垃圾车图标)

      hprof1

    5. 点击垃圾车下面那个有绿色箭头的图标”Dump Java Heap”,并等待几秒。然后会生成一个.hprof文件,这个文件就是用来分析内存使用的。

    6. 下载MAT

    7. 使用sdk中platform-tools路径下的hprof-conv文件将刚才生成的.hprof文件转码成MAT可以解析的文件。

       ./hprof-conv path/file.hprof exitPath/heap-converted.hprof
      
    8. 使用MAT打开转码后的文件。选择”Leak Suspects Report”然后点击”Finish”。

      hprof2

    9. 点击界面上方三条竖杠的图标。而后你会看到占用内存的对象的列表。

      hprof3

    10. 这个列表可能难以理解,你可以输入类名来过滤结果。我建议在输入框中输入package name,如下:

      hprof4

    11. 现 在我们可以看到VideoDetailActivity的9个实例,这显然不对,因为我们只应该有一个。我们可以右键单击这一条目并选择”Merge Paths to Shortest GC Root”并点击“exclude all phantom/weak/soft etc.references”来看是什么持有VideoDetailActivity的引用。

      hprof5

      而后持有引用的的线程就会显示出来。而后你就可以深入观察什么持有了Activity的引用。

    12. 通过下面的信息我们可以看到有一个DisplayListener被注册了但从未解除。

      hprof6

    所以这个漏洞就找到了,只需要解除监听器就可以了。

    不是所有的内存泄露漏洞都这么容易找到。希望这篇文章可以帮你避免内存泄露并在内存泄露发生时找到漏洞。

    推荐:

    Android 多种方式正确的加载图像,有效避免oom

  • 相关阅读:
    复位电路
    Tcl与Design Compiler (十三)——Design Compliler中常用到的命令(示例)总结
    Python Twisted系列教程1:Twisted理论基础
    Python Twisted架构英文版
    Python Twisted网络编程框架与异步编程入门教程
    windows下安装Python虚拟环境virtualenvwrapper-win
    数据库事务解析
    Python之select模块解析
    windows右键打开方式里面添加新的应用程序
    HTTP协议的状态码
  • 原文地址:https://www.cnblogs.com/android-blogs/p/5816988.html
Copyright © 2020-2023  润新知