Android性能提升之强引用、软引用、弱引用、虚引用使用

背景:收到公众投稿,《从面试题中看Java的Reference(引用)》,总感觉少了实际的例子和应用场景。于是结合自己工作中场景,小小补充下。小总结一下。看下Agenda如下:

  • 强引用
  • 软引用
  • 弱引用
  • 什么时候使用软引用,什么时候使用弱引用?
  • 虚引用

一、强引用

Java中的引用,类似于C++的指针。通过引用,可以对堆中的对象进行操作。在某个函数中,当创建了一个对象,该对象被分配在堆中,通过这个对象的引用才能对这个对象进行操作。

假设以上代码是在方法内运行的,那么局部变量str将被分配在栈空间上,而对象StringBuffer实例,被分配在堆空间中。局部变量str指向StringBuffer实例所在的堆空间,通过str可以操作该实例,那么str就是StringBuffer的引用。

此时,运行一个赋值语句:

那么,str所指向的对象也将被str1所指向,同时在局部栈空间上会分配空间存放str1变量。此时,该StringBuffer实例就有两个引用。对引用的”==”操作用于表示两个操作数所指向的堆空间地址是否相同,不表示两个操作数所指向的对象是否相等。

强引用特点:

  • 强引用可以直接访问目标对象。
  • 强引用所指向的对象在任何时候都不会被系统回收。JVM宁愿抛出OOM异常,也不会回收强引用所指向的对象。
  • 强引用可能导致内存泄露。

二、软引用

软引用是除了强引用外,最强的引用类型。可以通过java.lang.ref.SoftReference使用软引用。一个持有软引用的对象,不会被JVM很快回收,JVM会根据当前堆的使用情况来判断何时回收。当堆的使用率临近阈值时,才会回收软引用的对象。 看下我工作中使用到软引用的场景,加载一个1080x1920分辨率的图,约900多K, 对于我们来说,这个图已是非常大了。

  • 首先通过BitmapFactory.decodeStream构造一个大图bitmap
  • 然后把这个bitmap转成Drawble类型,构成强引用。
  • 接着使用SoftReference构造这个drawable对象的软引用drawables.
  • 最后通过软引用的get()方法,取得drawable对象实例的强引用,发现对象被未回收。在GC在内存充足的情况下,不会回收软引用对象。
  • 在实际中,一起请求很多相关图片,从网络,这时就会请求非常多的内存空间,导致内存吃紧,系统开始会GC。这次GC后,drawables.get()不再返回Drawable对象,而是返回null,这时屏幕上背景图不显示,说明在系统内存紧张的情况下,软引用被回收。
  • 使用软引用以后,在OutOfMemory异常发生之前,这些缓存的图片资源的内存空间可以被释放掉的,从而避免内存达到上限,避免Crash发生。
  • 需要注意的是,在垃圾回收器对这个Java对象回收前,SoftReference类所提供的get方法会返回Java对象的强引用,一旦垃圾线程回收该Java对象之后,get方法将返回null。所以在获取软引用对象的代码中,一定要判断是否为null,以免出现NullPointerException异常导致应用崩溃。

到底什么时候使用软引用,什么时候使用弱引用呢?

个人认为,如果只是想避免OutOfMemory异常的发生,则可以使用软引用。如果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。

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

另外,和弱引用功能类似的是WeakHashMap。WeakHashMap对于一个给定的key,其映射的存在并不阻止垃圾回收器对该键的回收,回收以后,其条目从映射中有效地移除。WeakHashMap使用ReferenceQueue实现的这种机制。

三、 弱引用

弱引用是一种比软引用较弱的引用类型。在系统GC时,只要发现弱引用,不管系统堆空间是否足够,都会将对象进行回收。但是,由于垃圾回收器的线程通常优先级很低,因此,并一不定能很快的发现持有弱引用的对象。这种情况下,弱引用对象可以存在较长的一段时间。一旦一个弱引用对象被垃圾回收器回收,便会加入到一个注册引用队列中。 看一个工作中实例:播放器的播放Panel,是一个View,就是在视频播放时,可以show、hide, 也可以拖拽进度条之类,还有上面的音量,亮度调节等。这样一个view,我们用弱引用,因为在视频播放过程中,不论硬解还是软解,都将占用大量内存。保证视频的渲染效果。 在VideoControllerView.java 有如下一段代码:

在GC之前,弱引用对象并未被垃圾回收器发现,因此通过mView.get()方法可以取得对应的强引用。但是只要进行垃圾回收,弱引用对象一旦被发现,便会立即被回收,并加入注册引用队列中。此时,再次通过mView.get()方法取得强引用就会失败。

注意:软引用,弱引用都非常适合来保存那些可有可无的缓存数据。如果这样做,当系统内存不足时,这些缓存数据会被回收,不会导致内存溢出。而当内存资源充足时,这些缓存数据又可以存在相当长的时间。

四、 虚引用

  • 虚引用是所有引用类型中最弱的一个。一个持有虚引用的对象,和没有引用几乎是一样的,随时都可能被垃圾回收器回收。当试图通过虚引用的get()方法取得强引用时,总是会失败。并且,虚引用必须和引用队列一起使用,它的作用在于跟踪垃圾回收过程。
  • 当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在垃圾回收后,销毁这个对象,奖这个虚引用加入引用队列。
  • 实际中几乎没用,暂不介绍。

原文发布于微信公众号 - 何俊林(DriodDeveloper)

原文发表时间:2016-09-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java面试通关手册

深入理解工厂模式

Java面试通关手册(Java学习指南,欢迎Star,会一直完善下去,欢迎建议和指导):https://github.com/Snailclimb/Java_G...

28813
来自专栏程序员互动联盟

java到底和C++有啥区别?

作为一名C++程序员,我们早已掌握了面向对象程序设计的基本概念,而且Java的语法无疑是非常熟悉的。事实上,Java本来就是从C++衍生出来的。 然而,C++和...

3866
来自专栏java初学

java — JVM调优

3679
来自专栏深度学习之tensorflow实战篇

Python中的__init__()方法整理中(两种解释)

解释一:看懂了就不用看第二种了 __init__()方法是Python学习当中重要的基础知识,__init__()方法意义重大的原因有两个。第一个原因是在对...

1.1K6
来自专栏编程

让你比95%的人更懂Pythonic的内置模块:collections

Python的集合(collections)模块,为很多用其他方法很难实现的场景提供了解决方案。 本文我们将会学习该模块的抽象概念是如何产生的,日后处理不同问题...

2015
来自专栏C语言及其他语言

[每日一题]字符串的连接

上一次是要反序输出字符串,而这次是要连接两个字符串,难度都不大,快来试试吧! 题目描述 写一函数,将两个字符串连接 输入 两行字符串 输出 链接后的字符串 样...

3166
来自专栏用户2442861的专栏

全面分析Java的垃圾回收机制

【简 介】 Java的堆是一个运行时数据区,类的实例(对象)从中分配空间。Java虚拟机(JVM)的堆中储存着正在运行的应用程序所建立的所有对象,这些对象通过...

2281
来自专栏数据结构笔记

python中将 \\uxxxx转换为 Unicode字符串

今天碰到一个很有意思的问题,需要将普通的 Unicode字符串转换为 Unicode编码的字符串,如下:

3803
来自专栏MelonTeam专栏

深入理解C++11(一)

导语 从最初的代号C++0x到最终的名称C++11,C++的第二个真正意义上的标准姗姗来迟。 C++11是一种新语言的开端。虽然设计C++11的目...

2009
来自专栏IT派

JavaScript 打怪升级 —— 把业务逻辑当练习题做

开发项目和出没社区有一段时间了,会遇上一些比较有印象业务需求。这些业务需求,可能是自己开发项目遇上的,可能是在社区看到的业务需求,或者其他情况接触到的需求,但是...

823

扫码关注云+社区

领取腾讯云代金券