一、概述:
Java中是JVM负责内存的分配和回收,这是它的优点(使用方便,程序不用再像使用c那样操心内存),但同时也是它的缺点(不够灵活)。为了解决内存操作不灵活这个问题,可以采用软引用等方法。
在JDK1.2以前的版本中,当一个对象不被任何变量引用,那么程序就无法再使用这个对象。垃圾回收器一旦发现这些无用对象,就会对其进行回收。但是在某些情况下,我们会希望有些对象不需要被立即回收,或者说从全局的角度来说没有立即回收的必要性。比如缓存系统的设计,在内存不吃紧或者说为了提高运行效率的情况下,一些暂时不用的对象仍然可放置在内存中,而不是立即进行回收。
为了满足这种要求,从JDK1.2版本开始,Java的设计人员把对象的引用细分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)四种级别,细分的准则是体现在被GC回收的优先级上:强引用>软引用>弱引用>虚引用。
这样,从JDK1.2开始,GC垃圾回收器回收对象时,对象的有效性分析不仅仅是需要考虑对象可达性,还需要考虑对象的引用强度,从而使程序可以更加灵活地控制对象的生命周期。可以用一个公式概括:对象的有效性=可达性+引用类型。
二、具体描述
强引用(Strong Reference)
我们平日里面的用到的new了一个对象就是强引用,例如 Object obj = new Object(); 当JVM的内存空间不足时,宁愿抛出OutOfMemoryError使得程序异常终止也不愿意回收具有 强引用的存活着的对象 !
记住是存活着,不可能是你new一个对象就永远不会被GC回收。当一个普通对象没有其他引用关系,只要超过了引用的作用域或者显示的将引用赋值为null时,你的对象就表明不是存活着,这样就会可以被GC回收了。当然回收的时间是不一定的具体得看GC回收策略。
软引用(Soft Reference)
如果一个对象只具有软引用(就是说软引用指向了某个对象,但只要该对象没有被强引用指向),那么如果内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。
String string = new String();
SoftReference<String> softReference = new SoftReference(string);
软引用所指示的对象被垃圾回收需要满足如下两个条件:
1.当其指示的对象没有任何强引用对象指向它;
2.当虚拟机内存不足时。
应用场景:
一般用来实现内存敏感的缓存,如果有空闲内存就可以保留缓存,当内存不足时就清理掉,这样就保证使用缓存的同时不会耗尽内存。例如适合缓存图片、小段音视频、网页缓存等。
弱引用(Weak Reference)
如果一个对象只具有弱引用(就是说弱引用指向了某个对象,但只要该对象没有被强引用指向),那么在垃圾回收器线程扫描的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
String string = new String();
WeakReference<String> weakReference = new WeakReference(string);
其主要使用场景见于:当前已有强引用指向强引用对象,此时由于业务需要,需要增加对此对象的引用,同时又不希望改变此引用的垃圾回收时机,此时WeakReference正好符合需求,常见于一些与生命周期的场景中,例如
1.防止内存泄漏,在静态内部类中,经常会使用虚引用。例如:一个类发送网络请求,承担 callback 的静态内部类,则常以虚引用的方式来保存外部类的引用,当外部类需要被 JVM 回收时,不会因为网络请求没有及时回应,引起内存泄漏。例如Android中结合静态内部类和WeakReference来解决Activity中可能存在Handler内存泄露问题。Android 中 像WeakHashMap、ThreadLocal、LifeCycle都用到了WeakReference。
2.配合检测内存泄漏。例如 Android 的 LeakCanary 库,就是使用的弱引用配合引用队列,来检测被弱引用的对象在主动触发gc后是否已经被回收来判断是否内存泄漏的。
虚引用(Phantom Reference)
如果一个对象只有虚引用在引用它,垃圾回收器是可以在任意时候对其进行回收的,虚引用主要用来跟踪对象被垃圾回收器回收的活动,当被回收时,JVM会把这个虚引用加入到与之相关联的ReferenceQueue中。与软引用和弱引用不同的是,虚引用必须有一个与之关联的ReferenceQueue。
String string = new String();
ReferenceQueue<String> referenceQueue = new ReferenceQueue<>();
PhantomReference<String> phantomReference = new PhantomReference<>(string, referenceQueue);
应用场景:
1.可以用来监听GC的动作。