强引用
像“Object obj = new Object()”这类的引用均为强引用,当一个对象被强引用变量引用时,它处于可达状态,是不可能被垃圾回收器回收的,即使该对象永远不会被用到也不会被回收。
当JVM出现内存不足时,JVM进行垃圾回收,对于强引用的对象,就算是出现了 OOM 也不会对该对象进行回收,打死都不收。因此强引用有时也是造成 Java 内存泄露的原因之一。如果将其引用赋值为null,一般认为是可以被垃圾回收掉的。(具体回收时间还要看使用的GC策略)
示例代码:
public class StrongRefenenceDemo {public static void main(String[] args) {Object o1 = new Object();Object o2 = o1;o1 = null;System.gc();System.out.println(o1); //nullSystem.out.println(o2); //java.lang.Object@2503dbd3}
}
软引用
软引用是一种相对强引用弱化了一些的引用,需要用java.lang.ref.SoftReference 类来实现,可以让对象豁免一些垃圾收集。
软引用用来描述一些还有用,但并非必需的对象。
对于只有软引用的对象:当系统内存充足时它不会被回收,当系统内存不足时它才会被回收。
//VM options: -Xms5m -Xmx5m
public class SoftRefenenceDemo {public static void main(String[] args) {softRefMemoryEnough();System.out.println("------内存不够用的情况------");softRefMemoryNotEnough();}private static void softRefMemoryEnough() {Object o1 = new Object();SoftReference<Object> s1 = new SoftReference<Object>(o1);System.out.println(o1);System.out.println(s1.get());o1 = null;System.gc();System.out.println(o1);System.out.println(s1.get());}/*** JVM配置`-Xms5m -Xmx5m` ,然后故意new一个一个大对象,使内存不足产生 OOM,看软引用回收情况*/private static void softRefMemoryNotEnough() {Object o1 = new Object();SoftReference<Object> s1 = new SoftReference<Object>(o1);System.out.println(o1);System.out.println(s1.get());o1 = null;byte[] bytes = new byte[10 * 1024 * 1024];System.out.println(o1);System.out.println(s1.get());}
}
输出结果:
--------------------------------------输出结果-------------------------------------------------
java.lang.Object@2503dbd3
java.lang.Object@2503dbd3
null
java.lang.Object@2503dbd3
------内存不够用的情况------
java.lang.Object@4b67cf4d
java.lang.Object@4b67cf4d
java.lang.OutOfMemoryError: Java heap space
at reference.SoftRefenenceDemo.softRefMemoryNotEnough(SoftRefenenceDemo.java:42)
at reference.SoftRefenenceDemo.main(SoftRefenenceDemo.java:15)
null
null
弱引用
弱引用也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
弱引用需要用java.lang.ref.WeakReference类来实现,它比软引用的生存期更短。
public class WeakReferenceDemo {public static void main(String[] args) {Object o1 = new Object();WeakReference<Object> w1 = new WeakReference<Object>(o1);System.out.println(o1);System.out.println(w1.get());o1 = null;System.gc();System.out.println(o1);//均被回收System.out.println(w1.get());//均被回收}
}
关于WeakHashMap示例代码
public class WeakHashDemo {public static void main(String[] args) throws InterruptedException {myHashMap();System.out.println("-----------------------------------------------");myWeakHashMap();}public static void myHashMap() {HashMap<String, String> map = new HashMap<String, String>();String key = new String("k1");String value = "v1";map.put(key, value);System.out.println(map);key = null;System.gc();System.out.println(map);}public static void myWeakHashMap() throws InterruptedException {WeakHashMap<String, String> map = new WeakHashMap<String, String>();// String key = "weak";// 刚开始写成了上边的代码// 思考一下,写成上边那样会怎么样?那可不是引用了String key = new String("weak");String value = "map";map.put(key, value);System.out.println(map);// 去掉强引用key = null;System.gc();Thread.sleep(1000);System.out.println(map);}
}
----------------------------------输出结果--------------------------------------------
{k1=v1}
{k1=v1}
-----------------------------------------------
{weak=map}
{}
虚引用
虚引用也称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。虚引用需要java.lang.ref.PhantomReference 来实现。
虚引用需要java.lang.ref.PhantomReference 来实现。
如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收,它不能单独使用也不能通过它访问对象,虚引用必须和引用队列(RefenenceQueue)联合使用。
虚引用的主要作用是跟踪对象垃圾回收的状态。仅仅是提供了一种确保对象被 finalize 以后,做某些事情的机制。
PhantomReference 的 get 方法总是返回 null,因此无法访问对应的引用对象。其意义在于说明一个对象已经进入 finalization 阶段,可以被 GC 回收,用来实现比 finalization 机制更灵活的回收操作。
设置虚引用的唯一目的,就是在这个对象被回收器回收的时候收到一个系统通知或者后续添加进一步的处理。
public class PhantomReferenceDemo {public static void main(String[] args) throws InterruptedException {Object o1 = new Object();ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();PhantomReference<Object> phantomReference = new PhantomReference<Object>(o1,referenceQueue);System.out.println(o1);System.out.println(referenceQueue.poll());System.out.println(phantomReference.get());o1 = null;System.gc();Thread.sleep(3000);System.out.println(o1);System.out.println(referenceQueue.poll()); //引用队列中System.out.println(phantomReference.get());}}
-------------------------------------------------------------------------------------------------------
java.lang.Object@4554617c
null
null
null
java.lang.ref.PhantomReference@74a14482
null
引用队列
ReferenceQueue 是用来配合引用工作的,没有ReferenceQueue 一样可以运行。
SoftReference、WeakReference、PhantomReference 都有一个可以传递 ReferenceQueue 的构造器。
创建引用的时候,可以指定关联的队列,当 GC 释放对象内存的时候,会将引用加入到引用队列。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动,这相当于是一种通知机制。
Reference的四种状态:
-
Active:新创建的引用实例处于Active状态,但当GC检测到该实例引用的实际对象的可达性发生某些改变(实际对象处于 GC roots 不可达)后,它的状态将变化为Pending或者Inactive。如果 Reference 注册了ReferenceQueue,则会切换为Pending,并且Reference会加入pending-Reference链表中,如果没有注册ReferenceQueue,会切换为Inactive。
-
Pending:当引用实例被放置在pending-Reference 链表中时,它处于Pending状态。此时,该实例在等待一个叫Reference-handler的线程将此实例进行enqueue操作。如果某个引用实例没有注册在一个引用队列中,该实例将永远不会进入Pending状态。
-
Enqueued:在ReferenceQueue队列中的Reference的状态,如果Reference从队列中移除,会进入Inactive状态。
-
Inactive:一旦某个引用实例处于Inactive状态,它的状态将不再会发生改变,同时说明该引用实例所指向的实际对象一定会被GC所回收。
想要了解更多技术文章请关注公众号“职谷智享”,每天关注的前50名小伙伴可领取200+互联网相关技术书籍。