https://cloud.tencent.com/developer/article/1632413
- 阿里面试: 说说强引用、软引用、弱引用、虚引用吧
在 Java 里,除了基本类型,其他类型都属于引用类型,它主要包括:类、接口、数组、枚举、注解
通过引用,可以对堆中的对象进行操作
// 这里的 person 就是指向Person 实例“张三”的引用 Person person = new Person("张三");
在 Java 中最常见的就是强引用,把一个对象赋给一个引用变量,这个引用变量就是一个强引用
类似
Object obj = new Object()
这类的引用
当一个对象被强引用变量引用时,它处于可达状态,是不可能被垃圾回收器回收的,即使该对象永远不会被用到也不会被回收。
当内存不足,JVM 开始垃圾回收,对于强引用的对象,就算是出现了 OOM(java.lang.OutOfMemoryError ) 也不会对该对象进行回收。因此强引用有时也是造成 Java 内存泄露的原因之一
对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显示地将相应(强)引用赋值为 null,一般认为就是可以被垃圾收集器回收。(具体回收时机还要要看垃圾收集策略)
public class StrongRefenenceDemo { // demo 中尽管 o1已经被回收,但是 o2 强引用 o1,一直存在,所以不会被GC回收 public static void main(String[] args) { Object o1 = new Object(); Object o2 = o1; o1 = null; System.gc(); System.out.println(o1); //null,会被回收 System.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
弱引用常被用来实现规范化映射
JDK 中的 WeakHashMap 就是一个这样的例子
弱引用也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
弱引用需要用
java.lang.ref.WeakReference
类来实现,它比软引用的生存期更短。对于只有弱引用的对象来说,只要垃圾回收机制一运行,不管 JVM 的内存空间是否足够,都会回收该对象占用的内存。
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()); } }
java.lang.Object@2133c8f8 java.lang.Object@2133c8f8 null null
WeakHashMap 弱引用案例
public class WeakHashMapDemo { public static void main(String[] args) throws InterruptedException { myHashMap(); 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} {}
ThreadLocal 中ThreadLocalMap 使用弱引用作为 键 的源码
static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal<?>> { Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } //...... }
虚引用也称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。
虚引用,顾名思义,就是形同虚设,与其他几种引用都不太一样,一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。
虚引用需要
java.lang.ref.PhantomReference
来实现。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收,它不能单独使用也不能通过它访问对象,虚引用必须和引用队列(RefenenceQueue)联合使用。
虚引用的主要作用是跟踪对象垃圾回收的状态。仅仅是提供了一种确保对象被 finalize 以后,做某些事情的机制。
PhantomReference 的 get 方法总是返回 null,因此无法访问对应的引用对象。其意义在于说明一个对象已经进入 finalization 阶段,可以被 GC 回收,用来实现比 finalization 机制更灵活的回收操作。
换句话说,设置虚引用的唯一目的,就是在这个对象被回收器回收的时候收到一个系统通知或者后续添加进一步的处理。
Java 允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。
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