• 造成OOM(内存溢出)的几种情况


    1. 数据库Cursor没关。
      当我们操作完数据库后,一定要调用close()释放资源。

    2. 构造Adapter没有使用缓存ContentView

      @Override  
      public View getView(int position, View convertView, ViewGroup parent) {  
          ViewHolder vHolder = null;  
          //如果convertView对象为空则创建新对象,不为空则复用  
          if (convertView == null) {  
              convertView = inflater.inflate(..., null);  
              // 创建 ViewHodler 对象  
              vHolder = new ViewHolder();  
              vHolder.img= (ImageView) convertView.findViewById(...);  
              vHolder.tv= (TextView) convertView  
                      .findViewById(...);  
              // 将ViewHodler保存到Tag中  
              convertView.setTag(vHolder);  
          } else {  
              //当convertView不为空时,通过getTag()得到View  
              vHolder = (ViewHolder) convertView.getTag();  
          }  
          // 给对象赋值,修改显示的值  
          vHolder.img.setImageBitmap(...);  
          vHolder.tv.setText(...);  
          return convertView;  
      }  
      
      static class ViewHolder {  
          TextView tv;  
          ImageView img;  
      }  
    3. 未取消注册广播接收者
      registerReceiver()unregisterReceiver()要成对出现,通常需要在ActivityonDestory()方法去取消注册广播接收者。

    4. IO流未关闭 注意用完后及时关闭

    5. Bitmap使用后未调用recycle()

    6. Context泄漏 这是一个很隐晦的OutOfMemoryError的情况。先看一个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);  
      }  

      这段代码效率很快,但同时又是极其错误的:
      我们看一下setBackgroundDrawable(Drawable background)的源码:

      public void setBackgroundDrawable(Drawable background) {
          ...
          background.setCallback(this);
       }

      background.setCallback(this);方法,也就是说Drawable拥有TextView的引用,而TextView又拥有Activity(Context类型)的引用, 因为sBackgroundstatic的,即使Activity被销毁,但是sBackground的生命周期还没走完,所以内存仍然不会被释放。这样就会有内存泄露了。 对,这样想是对的,但是我们看一下setCallback的源码:

      public final void setCallback(Callback cb) {
          mCallback = new WeakReference<Callback>(cb);
      }

      我们会发现里面使用了WeakReference,所以不会存在内存泄露了,但是官网当时提供的例子明明说有泄露,这是因为在3.0之后, 修复了这个内存泄露的问题,在3.0之前setCallback,方法是没有使用WeakReference的,所以这种泄露的情况在3.0之前会发生,3.0之后已经被修复。

    7. 线程 线程也是造成内存泄露的一个重要的源头。线程产生内存泄露的主要原因在于线程生命周期的不可控。我们来考虑下面一段代码。

      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();             
              //耗时的操作       
              }     
          } 
      }  

      假设MyThreadrun函数是一个很费时的操作,当调用finish的时候Activity会销毁掉吗?
      事实上由于我们的线程是Activity的内部类,所以MyThread中保存了Activity的一个引用,当MyThreadrun函数没有结束时,MyThread是不会被销毁的, 因此它所引用的Activity也不会被销毁,因此就出现了内存泄露的问题。

    8. 尽量使用ApplicationContext Context引用的生命周期超过它本身的生命周期,也会导致Context泄漏。 所以如果打算保存一个长时间的对象时尽量使用Application这种Context类型。 例如:

      mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
      改成:
      mStorageManager = (StorageManager) getApplicationContext().getSystemService(Context.STORAGE_SERVICE);

      按道理来说这种系统服务是不会有问题,但是有些厂商在修改的时候,可能会导致Context无法被及时释放。

    9. Handler的使用,在Activity退出的时候注意移除(尤其是循环的时候)

      public class ThreadDemo extends Activity {  
          private static final String TAG = "ThreadDemo";  
          private int count = 0;  
          private Handler mHandler =  new Handler();  
      
          private Runnable mRunnable = new Runnable() {  
      
              public void run() {  
                  //为了方便 查看,我们用Log打印出来   
                  Log.e(TAG, Thread.currentThread().getName() + " " +count);    
                  //每2秒执行一次   
                  mHandler.postDelayed(mRunnable, 2000);  
              }   
          };  
          @Override  
          public void onCreate(Bundle savedInstanceState) {  
              super.onCreate(savedInstanceState);  
              setContentView(R.layout.main);   
              //通过Handler启动线程   
              mHandler.post(mRunnable);  
          } 
      } 

      这样在也会引发内存泄露。我们应该在onDestory方法中移除Handler,代码如下:

      @Override  
      protected void onDestroy() {  
          super.onDestroy();  
          mHandler.removeCallbacks(mRunnable);  
      } 
  • 相关阅读:
    BaseServlet的编写
    两个线程交替打印1-100
    java集合中的HashMap源码分析
    mybatis一级缓存与二级缓存的原理
    mybatis的执行流程
    spring IOC的理解
    一文彻底搞懂面试中常问的各种“锁”
    Dubbo相关的基础
    Servlet基础
    文件传输基础
  • 原文地址:https://www.cnblogs.com/l2rf/p/4730032.html
Copyright © 2020-2023  润新知