欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

关于Android内存泄漏的种种总结第二弹

程序员文章站 2022-05-06 11:36:13
...

衔接上篇:
新年过后献上关于Android内存泄漏的种种总结
顺手留下GitHub链接,需要获取相关面试等内容的可以自己去找
https://github.com/xiangjiana/Android-MS

更多完整项目下载。未完待续。源码。图文知识后续上传github。
可以点击关于我联系我获取

在Android应用的开发中,为了防止内存溢出,在处理一些占用内存大而且声明周 期较长的对象时候,可以尽量应用软引用和弱引用技术。

软/弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用 的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队 列中。利用这个队列可以得知被回收的软/弱引用的对象列表,从而为缓冲器清除已 失效的软/弱引用。

假设我们的应用会用到大量的默认图片,比如应用中有默认的头像,默认游戏图标 等等,这些图片很多地方会用到。如果每次都去读取图片,由于读取文件需要硬件 操作,速度较慢,会导致性能较低。所以我们考虑将图片缓存起来,需要的时候直 接从内存中读取。但是,由于图片占用内存空间比较大,缓存很多图片需要很多的 内存,就可能比较容易发生OutOfMemory异常。这时,我们可以考虑使用软/弱引 用技术来避免这个问题发生。

以下就是高速缓冲器的雏形: 首先定义一个HashMap,保存软引用对象。

  private Map <String, SoftReference<Bitmap>> imageCache = new Has 
  hMap <String, SoftReference<Bitmap>> ();

再来定义一个方法,保存Bitmap的软引用到HashMap
关于Android内存泄漏的种种总结第二弹
使用软引用以后,在OutOfMemory异常发生之前,这些缓存的图片资源的内存空间 可以被释放掉的,从而避免内存达到上限,避免Crash发生。 如果只是想避免OutOfMemory异常的发生,则可以使用软引用。

如果对于应用的性 能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。

另外可以根据对象是否经常使用来判断选择软引用还是弱引用。如果该对象可能会 经常使用的,就尽量用软引用。如果该对象不被使用的可能性更大些,就可以用弱 引用

ok,继续回到主题。前面所说的,创建一个静态Handler内部类,然后对 Handler 持有的对象使用弱引用,这样在回收时也可以回收 Handler 持有的对象,但是这样 做虽然避免了 Activity 泄漏,不过 Looper 线程的消息队列中还是可能会有待处理的 消息,所以我们在 Activity 的 Destroy 时或者 Stop 时应该移除消息队列 MessageQueue中的消息。

下面几个方法都可以移除 Message:

  public final void removeCallbacks(Runnable r); 
  public final void removeCallbacks(Runnable r, Object token); 
  public final void removeCallbacksAndMessages(Object token); 
  public final void removeMessages(int what); 
  public final void removeMessages(int what, Object object);
  • 尽量避免使用 static 成员变量
    如果成员变量被声明为 static,那我们都知道其生命周期将与整个app进程生命 周期一样。

    这会导致一系列问题,如果你的app进程设计上是长驻内存的,那即使app切到 后台,这部分内存也不会被释放。按照现在手机app内存管理机制,占内存较 大的后台进程将优先回收,因为如果此app做过进程互保保活,那会造成app在 后台频繁重启。当手机安装了你参与开发的app以后一夜时间手机被消耗空了 电量、流量,你的app不得不被用户卸载或者静默。 这里修复的方法是:

不要在类初始时初始化静态成员。可以考虑lazy初始化。 架构设计上要思考是否真 的有必要这样做,尽量避免。如果架构需要这么设计,那么此对象的生命周期你有 责任管理起来。

  • 避免 override finalize()
    1、finalize 方法被执行的时间不确定,不能依赖与它来释放紧缺的资源。时间 不确定的原因是:
  • 虚拟机调用GC的时间不确定
  • Finalize daemon线程被调度到的时间不确定

2、finalize 方法只会被执行一次,即使对象被复活,如果已经执行过了 finalize 方法,再次被 GC 时也不会再执行了,原因是:
含有 finalize 方法的 object 是在 new 的时候由虚拟机生成了一个 finalize reference 在来引用到该Object的,而在 finalize 方法执行的时候,该 object 所 对应的 finalize Reference 会被释放掉,即使在这个时候把该 object 复活(即用 强引用引用住该 object ),再第二次被 GC 的时候由于没有了 finalize reference 与之对应,所以 finalize 方法不会再执行。

3、含有Finalize方法的object需要至少经过两轮GC才有可能被释放。

  • 资源未关闭造成的内存泄漏
    对于使用了BraodcastReceiverContentObserver,File,游标 Cursor, Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否 则这些资源将不会被回收,造成内存泄漏。
  • 一些不良代码造成的内存压力
    有些代码并不造成内存泄露,但是它们,或是对没使用的内存没进行有效及时 的释放,或是没有有效的利用已有的对象而是频繁的申请新内存。

比如:
构造 Adapter 时,没有使用缓存的 convertView ,每次都在创建新的 converView。这里推荐使用 ViewHolder

总结:

  • 对 Activity 等组件的引用应该控制在 Activity 的生命周期之内; 如果不能就考 虑使用 getApplicationContext 或者 getApplication,以避免 Activity 被外部长 生命周期的对象引用而泄露。
  • 尽量不要在静态变量或者静态内部类中使用非静态外部成员变量(包括context ),即使要使用,也要考虑适时把外部成员变量置空;也可以在内部类中使用弱 引用来引用外部类的变量。
  • 对于生命周期比Activity长的内部类对象,并且内部类中使用了外部类的成员变 量,可以这样做避免内存泄漏:
    • 将内部类改为静态内部类
    • 静态内部类中使用弱引用来引用外部类的成员变量
  • Handler 的持有的引用对象最好使用弱引用,资源释放时也可以清空 Handler 里面的消息。比如在 Activity onStop 或者 onDestroy 的时候,取消掉该 Handler对象的 MessageRunnable.
  • 在 Java 的实现过程中,也要考虑其对象释放,最好的方法是在不使用某对象 时,显式地将此对象赋值为 null,比如使用完Bitmap 后先调用 recycle(),再赋 为null,清空对图片等资源有直接引用或者间接引用的数组(使用 array.clear() ; array = null)等,最好遵循谁创建谁释放的原则。
  • 正确关闭资源,对于使用了BraodcastReceiverContentObserver,File,游 标 Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或 者注销。
  • 保持对对象生命周期的敏感,特别注意单例、静态对象、全局性集合等的生命 周期。

删减了一部分,见谅_
顺手留下GitHub链接,需要获取相关面试等内容的可以自己去找
https://github.com/xiangjiana/Android-MS

更多完整项目下载。未完待续。源码。图文知识后续上传github。
可以点击关于我联系我获取