博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
且谈Android内存溢出
阅读量:6095 次
发布时间:2019-06-20

本文共 3402 字,大约阅读时间需要 11 分钟。

hot3.png

且谈Android内存溢出

前言

    关于android的内存溢出在创新文档库中也有不少,网络上也有很多这方面的资料.所以这遍文章不算是正真意义上的创新,仅仅只是对xxx源码中出现的一些android开发中容易犯错的代码习惯一次总结.

1.  Android的内存溢出是如何发生的

 Android的虚拟机是基于寄存器的Dalvik,它的最大堆大小一般是16M,有的机器为24M.因此我们所能利用的内存空间是有限的.如果我们的内存占用超过了一定的水平就会出现OutOfMemory的错误.原因主要有两个:

l  由于我们程序的失误,长期保持某些资源(Context)的引用,造成内存泄露,资源造成得不到释放.

l  保存了多个耗用内存过大的对象(Bitmap),造成内存超出限制.

2.  Static

staticJava中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是该类的实例.所以用static修饰的变量,它的生命周期是很长的,如果用它来引用一些资源耗费过多的实例(Context的情况最多)就要谨慎对待了.例如xxx源码:

public class IntentMapping {

    private static Contextcontext;

    //. . .

}

如果将Activity赋值到么context的话.那么即使该Activity已经onDestroy,但是由于仍有对象保存它的引用,因此该Activity依然不会被释放.所以这里就潜在内存溢出.

再看android文档中的一个例子:

    private static Drawable sBackground;

 

    @Override

    protected void onCreate(Bundle state){

       super.onCreate(state);

       TextView label = new TextView(this);

       label.setText(“Leaks are bad”);

       if(sBackground == null){

           sBackground = getDrawable(R.drawable.large_bitmap);

       }

       label.setBackgroundDrawable(sBackground);

       setContentView(label);

}

sBackground,是一个静态的变量,我们并没有显式的保存Contex的引用,但是,DrawableView连接之后,Drawable就将View设置为一个回调,由于View中是包含Context的引用的,所以,实际上我们依然保存了Context的引用.这个引用链如下:

Drawable->TextView->Context

所以,最终该Context也没有得到释放,发生了内存泄露,该部分的详细内容也可以参考Android文档中Article部分.

有效的避免内存溢出的方法:

1.应该尽量避免static成员变量引用资源耗费过多的实例,比如Context.

2.Context尽量使用Application Context,因为ApplicationContext的生命周期比较长,引用它不会出现内存泄露的问题.

3.使用WeakReference代替强引用.比如可以使用WeakReference<Context> mContextRef;

在这里要提到,xxx中其实是有在ApplicationInit中将baseContext缓存下来,但可惜的是并没有很好的利用,而是在各自的模块中又缓存了自身的Context例如上面例子中的IntentMapping.

3.  线程

程产生内存泄露的主要原因在于线程生命周期的不可控.看看下面这段代码.

public class Pandareader extends BaseActivity {

@Override

    public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    `   new Thread() {

           public void run() {

           InitDataBase.initDB(Pandareader.this.getBaseContext());

           //. . .

        }

    }

}

这段代码很平常也很简单,也是我们经常使用的形式.但存在一个问题:假设Threadrun函数是一个很费时的操作,当我们开启该线程后,将设备的横屏变为了竖屏,这时会重新创建Activity,按照我们的想法,老的Activity应该会被销毁才对,然而事实上并非如此.

由于我们的线程是Activity的内部类,所以Thread中保存了Activity的一个引用,Threadrun函数没有结束时,Thread是不会被销毁的,因此它所引用的老的Activity也不会被销毁,因此潜在内存泄露.

Android提供的AsyncTask同样也会潜在内存溢出的风险.因为Thread只有在run函数不结束时才出现这种内存泄露问题,然而AsyncTask内部的实现机制是运用了ThreadPoolExcutor,该类产生的Thread对象的生命周期是不确定的,是应用程序无法控制的,因此如果AsyncTask作为Activity的内部类,就更容易出现内存泄露.

要避免线程的内存溢出要到2:

1.  将线程的内部类,改为静态内部类.

2.  在线程内部采用弱引用保存Context引用.

4.  Bitmap

可以说出现OutOfMemory问题的绝大多数人,都是因为Bitmap的问题.因为Bitmap占用的内存实在是太多了,特别是分辨率大的图片,如果要显示或创建多张那OutOfMemory问题就更显著了.而恰恰xxx的文本阅读就是依赖于PageBitmap. PageBitmap是创建屏幕分辨率大小的Bitmap,虽然有封装了一个池BitmapPool来管理PageBitmap的创建和销毁,以及引用.但是还是没发完全避免由于PageBitmap所带来的内存溢出.并且TextViewerActivity, TextDraw, TXTParagraph, ParagraphLineData, ParagraghData, TXTReader之间错综复杂的调用关系以及引用,决定了在TextViewerActivity中创建的对象并没有完全的释放,容易发生内存溢出.

有效的解决内存溢出:

1.及时的销毁.

虽然,系统能够确认Bitmap分配的内存最终会被销毁,但是由于它占用的内存过多,所以很可能会超过java堆的限制.因此,在用完Bitmap,要及时的recycle.recycle并不能确定立即就会将Bitmap释放掉,但是会给虚拟机一个暗示:”该图片可以释放了”.

2.设置一定的采样率.

有时候,我们要显示的区域很小,没有必要将整个图片都加载出来,而只需要记载一个缩小过的图片,这时候可以设置一定的采样率,那么就可以大大减小占用的内存.

但像上面xxx中出现的问题,并不能简单的应用采样率来解决.那就要更深一步分析BitmapTextDraw中使用的情况,包括BitmapPool中缓存PageBitmap数量以及对象的引用情况.

5.  Cursor

CursorAndroid查询数据后得到的一个管理数据集合的类,正常情况下,如果查询得到的数据量较小时不会有内存问题,而且虚拟机能够保证Cusor最终会被释放掉.然而如果Cursor的数据量特表大,特别是如果里面有Blob信息时,应该保证Cursor占用的内存被及时的释放掉,而不是等待GC来处理.并且Android明显是倾向于手动的将Cursor close. 有一种情况下,我们不能直接将Cursor关闭掉,这就是在CursorAdapter中应用的情况,但是注意,CursorAdapterAcivity结束时并没有自动的将Cursor关闭掉,因此,你需要在onDestroy函数中,手动关闭.

转载于:https://my.oschina.net/HandyWorkGroup/blog/74344

你可能感兴趣的文章
IIS7下使用urlrewriter.dll配置
查看>>
12.通过微信小程序端访问企查查(采集工商信息)
查看>>
WinXp 开机登录密码
查看>>
POJ 1001 Exponentiation
查看>>
HDU 4377 Sub Sequence[串构造]
查看>>
云时代架构阅读笔记之四
查看>>
WEB请求处理一:浏览器请求发起处理
查看>>
Lua学习笔记(8): 元表
查看>>
PHP经典算法题
查看>>
LeetCode 404 Sum of Left Leaves
查看>>
醋泡大蒜有什么功效
查看>>
hdu 5115(2014北京—dp)
查看>>
数据结构中常见的树(BST二叉搜索树、AVL平衡二叉树、RBT红黑树、B-树、B+树、B*树)...
查看>>
PHP读取日志里数据方法理解
查看>>
第五十七篇、AVAssetReader和AVAssetWrite 对视频进行编码
查看>>
Vivado增量式编译
查看>>
一个很好的幻灯片效果的jquery插件--kinMaxShow
查看>>
微信支付签名配置正确,但返回-1,调不出支付界面(有的手机能调起,有的不能)...
查看>>
第二周例行报告
查看>>
Spring学习(16)--- 基于Java类的配置Bean 之 基于泛型的自动装配(spring4新增)...
查看>>