【android开发】Android防止内存溢出浅析

【android开发】Android防止内存溢出浅析

      近期项目做得差点儿相同了,測试出现了一些问题,当中一个就是内存溢出问题,在三星手机上測试最easy出现内存溢出,在其它手机上,比方华为就没有发生,也是比較郁闷。这个问题在之前的公司,做项目时也遇到过,非常大一方面是自己写的代码问题,今天在网上找了一些知识,给大家分享一下:


Android的虚拟机是基于寄存器的Dalvik,它的最大堆大小通常是16M。可是Android採用的是Java语言编写,所以在非常大程度上,Android的内存机制等同于Java的内存机制,在刚開始开发的时候,内存的限制问题会给我们带来内存溢出等严重问题。在我们不使用一些内存的时候,我们要尽量在Android或者其它平台上避免在执行其它程序时,保存必要的状态,使得一些死进程所带来的内存问题,应该尽量在关闭程序或者保存状态的时候释放掉,这样能提高系统在执行方面的流畅性。

Android的内存主要表如今:

1. 在Android平台上,长期保持一些资源的引用,造成一些内存不能释放,带来的内存泄露问题非常多。比方:Context(下文中提到的Activity都是Context),在一些你须要保持你的首个类对象状态,而且把状态传入其它类对象中时,这样消除掉首个类对象之前,你必须先把接收类对象释放掉。须要注意一点的是:由于在Java或者Android内存机制中,顶点的结点释放前必须保证其它对象没有调用才干被系统GC回收释放。我们来看一段代码:

 @Override   protected void onCreate(Bundle state) {        super.onCreate(state);        TextViewlabel = new TextView(this);        label.setText("Leaksare bad");        setContentView(label);   }  

这个代码的意思就是我们把一个TextView的实例载入到了我们正在执行的Activity(Context)其中,因此,通过GC回收机制,我们知道,要释放Context,就必须先释放掉引用他的一些对象。假设没有,那在要释放Context的时候,你会发现会有大量的内存溢出。所以在你不小心的情况下内存溢出是一件很easy的事情。 保存一些对象时,同一时候也会造成内存泄露。最简单的比方说位图(Bitmap),比方说:在屏幕旋转时,会破坏当前保持的一个Activity状态,而且又一次申请生成新的Activity,直到新的Activity状态被保存。我们再看一段代码:

 

privatestatic 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);  
 }  

这个代码是非常快的同一时候也是错误的。它的内存泄露非常easy出在屏幕转移的方向上。尽管我们会发现没有显示的保存Context这个实例,可是当我们把绘制的图连接到一个视图的时候,Drawable就会将被View设置为回调,这就说明,在上述的代码中,事实上在绘制TextView到活动中的时候,我们已经引用到了这个Activity。链接情况能够表现为:Drawable->TextView->Context。

所以在想要释放Context的时候,事实上还是保存在内存中,并没有得到释放。

怎样避免这样的情况:主要在于。线程最easy出错。大家不要小看线程,在Android里面线程最easy造成内存泄露。线程产生内存泄露的主要原因在于线程生命周期的不可控。以下有一段代码:

  

publicclass MyTest extends Activity {  
     @Override  
     publicvoid onCreate(BundlesavedInstanceState) {  
         super.onCreate(savedInstanceState);  
         setContentView(R.layout.main);  
         new MyThread().start();  
     }  
    
     privateclass MyThread extends Thread{  
         @Override  
         public void run() {  
            super.run();  
            //do somthing  
         }  
     }  
}  

代码非常easy,可是在Android上又来新问题了,当我们在切换视图屏幕的时候(横竖屏),就会又一次建立横屏或者竖屏的Activity。我们形象的觉得之前建立的Activity会被回收,可是事实怎样呢?Java机制不会给你相同的感受,在我们释放Activity之前,由于run函数没有结束,这样MyThread并没有销毁,因此引用它的Activity(Mytest)也有没有被销毁,因此也带来的内存泄露问题。

有些人喜欢用Android提供的AsyncTask,但其实AsyncTask的问题更加严重,Thread仅仅有在run函数不结束时才出现这样的内存泄露问题,然而AsyncTask内部的实现机制是运用了ThreadPoolExcutor,该类产生的Thread对象的生命周期是不确定的,是应用程序无法控制的,因此假设AsyncTask作为Activity的内部类,就更easy出现内存泄露的问题。

线程问题的改进方式主要有:

l  将线程的内部类,改为静态内部类。

l  在程序中尽量採用弱引用保存Context。

 

2. 万恶的bitmap。。。

Bitmap是一个非常万恶的对象,对于一个内存对象,假设该对象所占内存过大,在超出了系统的内存限制时候,内存泄露问题就非常明显了。。

解决bitmap主要是要解决在内存尽量不保存它或者使得採样率变小。在非常多场合下,由于我们的图片像素非常高,而对于手机屏幕尺寸来说我们并不用那么高像素比例的图片来载入时,我们就能够先把图片的採样率减少在做原来的UI操作。

假设在我们不须要保存bitmap对象的引用时候,我们还能够用软引用来做替换。详细的实例代码google上面也有非常多。

 

综上所述,要避免内存泄露,主要要遵循下面几点:

第一:不要为Context长期保存引用(要引用Context就要使得引用对象和它本身的生命周期保持一致)。

第二:假设要使用到Context,尽量使用ApplicationContext去取代Context,由于ApplicationContext的生命周期较长,引用情况下不会造成内存泄露问题

第三:在你不控制对象的生命周期的情况下避免在你的Activity中使用static变量。尽量使用WeakReference去取代一个static。

第四:垃圾回收器并不保证能准确回收内存,这样在使用自己须要的内容时,主要生命周期和及时释放掉不须要的对象。尽量在Activity的生命周期结束时,在onDestroy中把我们做引用的其它对象做释放,比方:cursor.close()。

 

事实上我们能够在非常多方面使用更少的代码去完毕程序。比方:我们能够多的使用9patch图片等。有非常多细节地方都能够值得我们去发现、挖掘很多其它的内存问题。我们要是能做到C/C++对于程序的“谁创建,谁释放”原则,那我们对于内存的把握,并不比Java或Android本身的GC机制差,并且更好的控制内存,能使我们的手机执行得更流畅。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/119110.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • OpenSSL、OpenSSL-FIPS、OpenSSL-FIPS-ECP的区别

    OpenSSL、OpenSSL-FIPS、OpenSSL-FIPS-ECP的区别在OpenSSL的官网上可以看到三个分支,分别是openssl-、openssl-fips-、openssl-fips-ecp-。这三者的区别如下。 分支 内容差异 openssl- 完整版的OpenSSL openssl-fips- 把密码函数库单独抽出来,做成一个满足FIPS1…

  • docker搭建kafka集群

    docker搭建kafka集群docker搭建kafka集群我在M1mbp上使用的以下镜像新建文件zk-kafka-docker-compose.ymlversion:”2″services:zookeeper:user:rootimage:docker.io/zookeeperports:-“12181:2181″environment:-ALLOW_ANONYMOUS_LOGIN=yesvolumes:-zoo

  • C# SplitContainer 控件详细用法

    C# SplitContainer 控件详细用法1.可以将Windows窗体SplitContainer控件看作是一个复合体,它是由一个可移动的拆分条分隔的两个面板。当鼠标指针悬停在该拆分条上时,指针将相应地改变形状以显示该拆分条是可移动的。使用SplitContainer控件,可以创建复合的用户界面(通常,在一个面板中的选择决定了在另一个面板中显示哪些对象)。这种排列对于显示和浏览信息非常有用。拥有两个面板使您可以聚合不同区域中的信息,并且用户可以轻松地使用拆分条(也称为“拆分器”)调整面板的大小。另外,还可以嵌套多个SplitC…

  • 二进制、八进制、十进制、十六进制关系及转换[通俗易懂]

    二进制、八进制、十进制、十六进制关系及转换[通俗易懂]二进制,八进制,十进制,十六进制之间的关系是什么?浮点数是什么回事?本文内容参考自王达老师的《深入理解计算机网络》一书<中国水利水电出版社>一、数制解释:1、编程中经常使用的数制分类(“你编程时能使用的数制全部在这里了”):⑴、十进制十进制是我们生活中使用得最频繁的进制了。十进制的基数是10,也就是说,十进制有10个数字符

    2022年10月17日
  • Spring整合Hibernate的步骤

    Spring整合Hibernate的步骤

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号