java语言中为对象的引用分为了四个级别,分别为 强引用 、软引用、弱引用、虚引用。
WeakReference与SoftReference、HardReference、PhantomReference的区别
1.WeakReference与SoftReference都可以用来保存对象的实例引用,这两个类与垃圾回收有关。
WeakReference是弱引用,其中保存的对象实例可以被GC回收掉。这个类通常用于在某处保存对象引用,而又不干扰该对象被GC回收,通常用于Debug、内存监视工具等程序中。因为这类程序一般要求即要观察到对象,又不能影响该对象正常的GC过程。最近在JDK的Proxy类的实现代码中也发现了Weakrefrence的应用,Proxy会把动态生成的Class实例暂存于一个由Weakrefrence构成的Map中作为Cache。
2.SoftReference是软引用,它保存的对象实例,除非JVM即将OutOfMemory,否则不会被GC回收。这个特性使得它特别适合设计对象Cache。对于Cache,我们希望被缓存的对象最好始终常驻内存,但是如果JVM内存吃紧,为了不发生OutOfMemoryError导致系统崩溃,必要的时候也允许JVM回收Cache的内存,待后续合适的时机再把数据重新Load到Cache中。这样可以系统设计得更具弹性。
二、很多时候我们需要考虑Android平台上的内存管理问题,Dalvik VM给每个进程都分配了一定量的可用堆内存,当我们处理一些耗费资源的操作时可能会产生OOM错误(OutOfMemoryError)这样的异常,Android123观察了下国内的类似Market客户端设计,基本上都没有采用很好的内存管理机制和缓存处理如果细心的网友可能发现Android Market客户端载入时,每个列表项的图标是异步刷新显示的,但当我们快速的往下滚动到一定数量比如50个,再往回滚动时可能我们看到了部分App的图标又重新开始加载,当然这一过程可能是从SQLite数据库中缓存的,但是在内存中已经通过类似SoftReference的方式管理内存。
在Java中内存管理,引用分为四大类,强引用HardReference、弱引用WeakReference、软引用SoftReference和虚引用PhantomReference。它们的区别也很明显,HardReference对象是即使虚拟机内存吃紧抛出OOM也不会导致这一引用的对象被回收,而WeakReference等更适合于一些数量不多,但体积稍微庞大的对象,在这四个引用中,它是最容易被垃圾回收的,而我们对于显示类似Android Market中每个应用的App Icon时可以考虑使用SoftReference来解决内存不至于快速回收,同时当内存短缺面临Java VM崩溃抛出OOM前时,软引用将会强制回收内存,最后的虚引用一般没有实际意义,仅仅观察GC的活动状态,对于测试比较实用同时必须和ReferenceQueue一起使用。对于一组数据,我们可以通过HashMap的方式来添加一组SoftReference对象来临时保留一些数据,同时对于需要反复通过网络获取的不经常改变的内容,可以通过本地的文件系统或数据库来存储缓存,希望给国内做App Store这样的客户端一些改进建议。
WeakReference的一个测试程序:
- import java.lang.ref.WeakReference;
- public class WeakReferenceTest {
- /**
- * @param args
- */
- public static void main(String[] args) {
- A a = new A();
- a.str = "Hello, reference";
- WeakReference<A> weak = new WeakReference<A>(a);
- a = null;
- int i = 0;
- while (weak.get() != null) {
- System.out.println(String.format("Get str from object of WeakReference: %s, count: %d", weak.get().str, ++i));
- if (i % 10 == 0) {
- System.gc();
- System.out.println("System.gc() was invoked!");
- }
- try {
- Thread.sleep(500);
- } catch (InterruptedException e) {
- }
- }
- System.out.println("object a was cleared by JVM!");
- }
- }
程序运行结果:
- Get str from object of WeakReference: Hello, reference, count: 1
- Get str from object of WeakReference: Hello, reference, count: 2
- Get str from object of WeakReference: Hello, reference, count: 3
- Get str from object of WeakReference: Hello, reference, count: 4
- Get str from object of WeakReference: Hello, reference, count: 5
- Get str from object of WeakReference: Hello, reference, count: 6
- Get str from object of WeakReference: Hello, reference, count: 7
- Get str from object of WeakReference: Hello, reference, count: 8
- Get str from object of WeakReference: Hello, reference, count: 9
- Get str from object of WeakReference: Hello, reference, count: 10
- System.gc() was invoked!
- object a was cleared by JVM!
SoftReference的一个测试程序:
- import java.lang.ref.SoftReference;
- public class SoftReferenceTest {
- /**
- * @param args
- */
- public static void main(String[] args) {
- A a = new A();
- a.str = "Hello, reference";
- SoftReference<A> sr = new SoftReference<A>(a);
- a = null;
- int i = 0;
- while (sr.get() != null) {
- System.out.println(String.format("Get str from object of SoftReference: %s, count: %d", sr.get().str, ++i));
- if (i % 10 == 0) {
- System.gc();
- System.out.println("System.gc() was invoked!");
- }
- try {
- Thread.sleep(500);
- } catch (InterruptedException e) {
- }
- }
- System.out.println("object a was cleared by JVM!");
- }
- }
程序运行结果:
- Get str from object of SoftReference: Hello, reference, count: 1
- Get str from object of SoftReference: Hello, reference, count: 2
- Get str from object of SoftReference: Hello, reference, count: 3
- Get str from object of SoftReference: Hello, reference, count: 4
- Get str from object of SoftReference: Hello, reference, count: 5
- Get str from object of SoftReference: Hello, reference, count: 6
- Get str from object of SoftReference: Hello, reference, count: 7
- Get str from object of SoftReference: Hello, reference, count: 8
- Get str from object of SoftReference: Hello, reference, count: 9
- Get str from object of SoftReference: Hello, reference, count: 10
- System.gc() was invoked!
- Get str from object of SoftReference: Hello, reference, count: 11
- Get str from object of SoftReference: Hello, reference, count: 12
- Get str from object of SoftReference: Hello, reference, count: 13
- Get str from object of SoftReference: Hello, reference, count: 14
- Get str from object of SoftReference: Hello, reference, count: 15
- Get str from object of SoftReference: Hello, reference, count: 16
- Get str from object of SoftReference: Hello, reference, count: 17
- Get str from object of SoftReference: Hello, reference, count: 18
- Get str from object of SoftReference: Hello, reference, count: 19
- Get str from object of SoftReference: Hello, reference, count: 20
- System.gc() was invoked!
- Get str from object of SoftReference: Hello, reference, count: 21
- Get str from object of SoftReference: Hello, reference, count: 22
- Get str from object of SoftReference: Hello, reference, count: 23
- Get str from object of SoftReference: Hello, reference, count: 24
- Get str from object of SoftReference: Hello, reference, count: 25
- Get str from object of SoftReference: Hello, reference, count: 26
- Get str from object of SoftReference: Hello, reference, count: 27
- Get str from object of SoftReference: Hello, reference, count: 28
SoftReference比WeakReference生命力更强,当JVM的内存不吃紧时,即使引用的对象被置为空了,Soft还可以保留对该对象的引用,此时的JVM内存池实际上还保有原来对象,只有当内存吃紧的情况下JVM才会清除Soft的引用对象,并且会在未来重新加载该引用的对象。
而WeakReference则当清理内存池时会自动清理掉引用的对象。
下面只针对java中的弱引用进行一些分析
在分析弱引用之前,先阐述一个概念:什么是对象可到达和对象不可到达状态。
其实很简单,我举个例子:
现在有如下两个类class A class B,在JVM上生成他们两个类的实例分别为 instance a instance b
有如下表达式:
A a = new A();
B b = new B();
两个强引用对象就生成了,好吧,那么这个时候我做一下修改:
A a = new A();
B b = new B(a);
B的默认构造函数上是需要一个A的实例作为参数的,那么这个时候 A和B就产生了依赖,也可以说a和b产生了依赖,我们再用一个接近内存结构的图来表达:
a是对象A的引用,b是对象B的引用,对象B同时还依赖对象A,那么这个时候我们认为从对象B是可以到达对象A的。
于是我又修改了一下代码
A a = new A();
B b = new B(a);
a = null;
A对象的引用a置空了,a不再指向对象A的地址,我们都知道当一个对象不再被其他对象引用的时候,是会被GC回收的,很显然及时a=null,那么A对象也是不可能被回收的,因为B依然依赖与A,在这个时候,造成了内存泄漏!
那么如何避免上面的例子中内存泄漏呢?
很简单:
A a = new A();
B b = new B(a);
a = null;
b = null;
这个时候B对象再也没有被任何引用,A对象只被B对象引用,尽管这样,GC也是可以同时回收他们俩的,因为他们处于不可到达区域。
弱引用来了!
A a = new A();
WeakReference wr = new WeakReference(a);
//B b = new B(a);
当 a=null ,这个时候A只被弱引用依赖,那么GC会立刻回收A这个对象,这就是弱引用的好处!他可以在你对对象结构和拓扑不是很清晰的情况下,帮助你合理的释放对象,造成不必要的内存泄漏!!