Android 内存分析工具MAT(中)

来源:互联网 发布:linux下重启服务 编辑:程序博客网 时间:2024/04/29 16:35
1.资源对象没关闭造成的内存泄露
    资源性文件对象(如Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。它们的缓冲不仅存在于Java虚拟机内,还存在于虚拟机外,如SQLiteCursor(在析构函数finalize(),如果我们没有关闭它


,它自己会调用close()关闭),如果我们没有关闭它,系统会在回收它时也会关闭它,但是这样的效率太低。因此对于资源性对象不使用的时候,应该调用它的close()函数,将其关闭掉,然后才置为null。在我们退出程序时一定要确保我们的资源性对象已经关闭





2.一些不良代码造成的内存压力
    有些代码并不造成内存泄露,但是它们,或是对没使用的内存进行有效及时的释放,或是没有有效的利用已有的对象而是频繁的申请新的内存,对内存的回收和分配造成很大的影响,容易迫使虚拟机不得不给应用程序分配更多的内存,造成不必要的内存开支。


3.调用用recycle()释放Bitmap占用的内存
  Bitmap对象在不使用时,我们应该先调用recycle()释放内存,然后才将它设置为null。recycle()并不能确定立即会将Bitmap释放掉,但是会给虚拟机一个暗示:“该图片可以释放了”。为了使图片占用较少的内存,可以给图片设置一定的采样率,有时候,我们要显


示的区域很小,没有必要将整个图片都加载出来,而只需要加载一个缩小过的图片,这时候可以设置一定的采样率,那么就可以大大的减少占用的内存。如下面的代码:
  private ImageView preview;
  BitmapFactory.Options options = new BitmapFactory.Options();
  options.inSampleSize = 2;//图片宽高都为原来的二分之一,即图片为原来的四分之一
  Bitmap bitmap = BitmapFactory.decodeStream(cr.openInputStream(uri), null, options); 
  preview.setImageBitmap(bitmap);


4.Context的描述
  如果Activity的context不被释放,那么这个Activity就不会被销毁


5.尽量不要使用static关键字来定义变量和方法
  因为static定义的变量的生命周期,从它定义开始,直到程序的消亡。static定义的方法,在程序一开始的时候就分配了内存地址,这样会消耗内存,但是这种方法的执行效率比实例方法快。


6.线程问题
  线程也是造成内存泄露的一个重要的源头。线程产生内存泄露的主要原因在于线程生命周期的不可控。我们来考虑下面一段代码。
  public class MyActivity extends Activity {  
  
        @Override  
  
        public void onCreate(Bundle savedInstanceState) {  
  
           super.onCreate(savedInstanceState);  
  
           setContentView(R.layout.main);  
 
          new MyThread().start();  
 
      }  
 
 
      private class MyThread extends Thread{  
 
          @Override  
 
          public void run() {  
 
              super.run();  
 
              //do somthing  
 
          }  
 
      }  
 
 }
    这段代码很平常也很简单,是我们经常使用的形式,我们思考一个问题:假设MyThread的run函数是一个很费时的操作,当我们开启线程后,将设备的横屏变为了竖屏,一般情况下当屏幕转换时会重新创建Activity,按照我们的想法,老的Activity应该会被销毁才对


,然而事实上并非如此。
    由于我们的线程是Activity的内部类,所以NyThread中保存了Activity的一个引用,当MyThread的run函数没有结束时,MyThread是不会被销毁的,因此它所引用的老的Activity也不会被销毁,因此就出现了内存泄露的问题。
    有些人喜欢用Android提供的AsyncTask,单但事实上AsyncTask的问题更加的严重,Thread只有在run函数不结束时才出现这种内存泄露的问题,然而AsyncTask内部的实现机制是运用了ThreadPoolExcutor,该类产生的Thread对象的生命周期是不确定的,是应用程序


无法控制的,因此如果AsyncTask作为Activity内部类,就更容易出现内存的泄露问题。


7.行踪诡异的Cursor
    Cursor是Android查询数据后得到的一个管理数据的集合的类,正常情况下,如果查询到的数据量较小时不会有内存的问题,而且虚拟机能够保证Cursor最终会被释放掉。
    然而如果Cursor的数据量特别大,特别是如果里面有Biob信息时,应该保证Cursor占用的内存被及时的释放掉,而不是等待GC来处理。并且Android明显是倾向与编程者手动的将Cursor关掉,因为在源代码中我们发现,如果等到垃圾回收器来回收时,会给用户以错误


提示。所以我们使用Cursor的方式一般如下:
   Cursor cursor = null;  
      try {  
          cursor = mContext.getContentResolver().query(uri,null, null,null,null);  
           if(cursor != null) {  
              cursor.moveToFirst();  
              //do something  
          }  
       } catch (Exception e) {  
           e.printStackTrace();   
      } finally {  
          if (cursor != null) {  
             cursor.close();  
          }  
 }
    有一种情况下,我们不能直接将Cursor关闭掉,这就是在CursorAdapter中应用的情况,但是注意,CursorAdapter在Activity结束时并没有自动的将Cursor关闭掉,因此,你需要在OnDestroy函数中,手动关闭。
    CursorAdapter中的changeCursor函数,会将原来的Cursor释放掉,并替换为新的Cursor,所以你不用担心原来的Cursor没有被关闭。
    你可能会想到使用Activity的managedQuery来生成Cursor,这样Cursor就会与Activity的生命周期一致了,事实上managedQuery也有很大的局限性。
    managedQuery生产的Cursor必须确保不会被替换,因为可能很多程序事实上查询条件都是不确定的,因此我们经常会用新的Cursor来替换掉原先的Cursor。因此这种方法的使用范围是很小。
0 0
原创粉丝点击