内存泄露和内存溢出,并不是那么简单

本文详细解析了Android应用中出现的内存溢出(OOM)现象及其根本原因,并介绍了几种常见的内存泄漏场景及解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

1.内存溢出oom

 

程序发生OMM并不表示RAM不足,而是因为程序申请的java heap对象超过了dalvik vm heapgrowthlimit。也就是说,在RAM充足的情况下,也可能发生OOM

java程序发生OMM并不是表示RAM不足,如果RAM真的不足,会发生什么呢?这时Androidmemory killer会起作用,当RAM所剩不多时,memory killer会杀死一些优先级比较低的进程来释放物理内存,让高优先级程序得到更多的内存

 

这个是因为Android系统对dalvikvm heapsize作了硬性限制,当java进程申请的java空间超过阈值时,就会抛出OOM异常(这个阈值可以是48M24M16M等,视机型而定),可以通过adb shell getprop | grep dalvik.vm.heapgrowthlimit查看此值。

也就是说,程序发生OMM并不表示RAM不足,而是因为程序申请的java heap对象超过了dalvik vm heapgrowthlimit。也就是说,在RAM充足的情况下,也可能发生OOM.

即发生内存泄露,oom的主要源头

 

 

2.内存泄露


Java内存泄漏指的是进程中某些对象(垃圾对象)已经没有使用价值了,但是它们却可以直接或间接地引用到gc roots导致无法被GC回收。无用的对象占据着内存空间,使得实际可使用内存变小,形象地说法就是内存泄漏了.内存泄露的原因以及解决方法有:

 

2.1非静态内部类的静态实例容易造成内存泄漏

 对于lauchMode不是singleInstance的Activity, 应该避免在activity里面实例化其非静态内部类的静态实例。

2.2activity使用静态成员

·不要对activity的context长期引用(一个activity的引用的生存周期应该和activity的生命周期相同)

·如果可以的话,尽量使用关于application的context来替代和activity相关的context

·如果一个acitivity的非静态内部类的生命周期不受控制,那么避免使用它;正确的方法是使用一个静态的内部类,并且对它的外部类有一WeakReference,就像在ViewRootImpl中内部类W所做的那样。

2.3使用handler时的内存问题

This Handler class should be static or leaks might occur (com.example.ta.HandlerActivity.1)

可以将handler变为static ,或者添加弱引用

public class SampleActivity extends Activity {

  /**
   * 使用静态的内部类,不会持有当前对象的引用
   */
  private static class MyHandler extends Handler {
    private final WeakReference<SampleActivity> mActivity;

    public MyHandler(SampleActivity activity) {
      mActivity = new WeakReference<SampleActivity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
      SampleActivity activity = mActivity.get();
      if (activity != null) {
        // ...
      }
    }
  }

  private final MyHandler mHandler = new MyHandler(this);

  /**
   * 使用静态的内部类,不会持有当前对象的引用
   */
  private static final Runnable sRunnable = new Runnable() {
      @Override
      public void run() { }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    //  发送一个10分钟后执行的一个消息
    mHandler.postDelayed(sRunnable, 600000);

    // 结束
    finish();
  }
}




因为HandlerThread实现的run方法是一个无限循环,它不会自己结束,线程的生命周期超过了activity生命周期,当横竖屏切换,HandlerThread线程的数量会随着activity重建次数的增加而增加。

应该在onDestroy时将线程停止掉:mThread.getLooper().quit();

另外,对于不是HandlerThread的线程,也应该确保activity消耗后,线程已经终止,可以这样做:在onDestroy时调用mThread.join();

2.4注册某个对象后未取消注册

 注册广播接收器、注册观察者等等,当它不用时,及时unregister

2.5集合中对象没清理造成的内存泄露

 

2.6资源对象没关闭造成的内存泄露

 对文件,资源进行调用,cursor等,当资源对象加载完成,应该及时colse();

 

 

 

 

参考文档:http://blog.youkuaiyun.com/gemmem/article/details/13017999

 

 


### 内存泄漏与内存溢出的区别 #### 定义 内存泄漏是指程序运行过程中动态分配的内存由于某种原因未能被释放或回收,从而导致这部分内存无法再被使用的情况[^1]。而内存溢出则是指程序试图申请更多的内存空间时,系统的可用内存不足以满足该请求,进而引发错误的状态[^2]。 #### 发生场景 - **内存泄漏**通常发生在编程中的资源管理不当之处,比如对象创建后未及时销毁或者存在循环引用等问题,在垃圾收集机制无法自动处理的情况下尤为明显[^3]。 - 对于**内存溢出**而言,它可能由多种因素引起,包括但不限于单次操作所需内存过大超出物理限制;长时间累积的小规模数据存储最终突破设定界限等情形[^4]。 #### 影响范围与时效性 - 当发生**内存泄露**时,其影响可能是渐进式的,随着时间推移逐渐消耗更多本可利用的空间直到触发更严重的后果如性能下降甚至崩溃[^5]。 - 而一旦遭遇**内存溢出**,往往意味着立即面临不可用状态,应用程序可能会立刻停止工作抛出异常信息提示用户解决当前状况下的不足之处[^6]。 #### 检测方法 针对两种不同的问题有不同的检测手段: 对于发现是否存在潜在的**memory leak**(即中文所讲到得“内存泄”),可以借助专门工具分析堆栈变化趋势以及跟踪可疑变量生命周期来定位具体位置所在;至于如何预防应对可能出现 的OOM(out-of-memory),则需合理规划初始容量大小设置上限阈值监控实际用量情况以便适时调整策略优化整体架构设计[^7]. ```java // Java 中可能导致内存泄漏的例子 class Demo { private static Map<String, String> map; public void init() { map = new HashMap<>(); } @Override protected void finalize() throws Throwable { // 不可靠的清理方式 super.finalize(); map.clear(); } } ``` 以上代码片段展示了一个简单的Java类定义,其中静态成员`map`如果在实例化之后没有正确清除引用关系,则即使对象本身已经不再需要也可能因为持有外部容器而导致部分区域长期得不到GC(Garbage Collection)的机会形成所谓的Memory Leak现象[^8]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值