JDK1.2后进行了扩充
强引用
关联的对象不会被回收(无论何种情况下,只要强引用关系还在,垃圾收集器就永远不会回收掉被引用的对象)
对于强引用我们开始学Java的时候是使用的最多的,就是我们栈内存定义一个类型的申明,指向堆中的一个对象的实例,这就是一个强引用。用代码来描述最简洁了。
Object obj = new Object();
如果这个对象不需要了,需要弱化从而使GC能够回收。例如:obj = null;这之后GC会认为对象的引用不存在(通过可达性分析算法),就是说GC从GC Roots节点开始,根据关系向下搜索,搜索过得所走的路程被称为引用链,如果某个对象到到GC Roots之间没有任何引用相连或者说GC Roots到这个对象不可达时,则证明此对象不可能再被使用。了解更多
软引用
关联的对象只有在内存不够的情况下才会被回收(用来描述一些还有用,但非必须的对象),(GC回收这个对象发生在OutOfMemoryError错误之前)
软引用是通过softReference类来实现的,通过也读源码我们发现这样的一段话:
软引用对象,垃圾收集器根据内存需求自行清除这些对象。软引用最常用于实现对内存敏感的缓存。假设垃圾收集器在某个时间点确定对象是软可达的。此时,它可以选择以原子方式清除对该对象的所有软引用,以及对通过强引用链可访问该对象的任何其他软可访问对象的所有软引用。在同一时间或稍后的时间,它将把那些新清除的、已注册到引用队列中的软引用放入队列 在虚拟机抛出OutofMemoryError之前,所有对软可达对象的软引用都保证已被清除。否则,对软引用被清除的时间或对不同对象的一组这样的引用被清除的顺序没有任何限制。但是,鼓励虚拟机实现偏向于清除最近创建的或最近使用的软引用。 这个类的直接实例可以用来实现简单的缓存;这个类或派生的子类也可以在更大的数据结构中使用,以实现更复杂的缓存。只要软引用的引用是强可达的,也就是说,它实际上是在使用的,软引用就不会被清除。因此,例如,一个成熟的缓存可以通过保持对这些条目的强引用来防止其最近使用的条目被丢弃,而让垃圾收集器自行决定是否丢弃剩余的条目。自:1.2作者:Mark Reinhold
代码测试:
public class SoftReferenceTest { public static class User { private String id; private String name; public User(String id, String name) { this.id = id; this.name = name; } @Override public String toString() { return "User{" + "id='" + id + '\'' + ", name='" + name + '\'' + '}'; } } // -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=E:/ public static void main(String[] args) { //创建对象,建立软引用 User u1 = new User("1", "Jack"); SoftReference<User> userSoftRef = new SoftReference<User>(u1); u1 = null;//取消强引用 //从软引用中重新获取强引用对象 System.out.println(userSoftRef.get());//由于堆空间内存足够,不会回收软引用。 System.gc(); try { //让系统认为内存紧张 byte[] b = new byte[1024 * 1024 * 7]; } catch (Throwable throwable) { throwable.printStackTrace(); } finally { //再次从软引用获得数据 System.out.println(userSoftRef.get());//在OOM之前,垃圾回收器会回收软引用的可达对象 } } } /** 输出: User{id='1', name='Jack'} java.lang.OutOfMemoryError: Java heap space Dumping heap to E:/\java_pid12460.hprof ... Heap dump file created [2711968 bytes in 0.012 secs] null */
弱引用
关联的对象一定会被回收,它只能存活到下一次垃圾回收发生之前(GC看到就会回收)
弱引用也是用来描述那些非必须对象,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。在系统GC时,只要发现弱引用,不管系统堆内存空间是否充足,都会回收掉被弱引用关联的对象。
- 弱引用需要用java.lang.WeakReference类来实现,它比软引用的生存期更短。
- 如果一个对象只是被弱引用引用者,那么只要发生GC,不管内存空间是否足够,都会回收该对象。
- 弱引用适合解决某些地方的内存泄漏的问题
- ThreadLocal静态内部类ThreadLocalMap中的Entiry中的key就是一个虚引用;
源码说明:
弱引用对象,不阻止它们的引用被设定为可终结。最终确定。然后回收。弱引用最常用于实现规范化映射。 假设垃圾收集器在某个时间点确定对象是弱可达的。到那时,它将自动清除对该对象的所有弱引用,以及对任何其他弱可及对象的所有弱可及对象的所有弱可及对象的所有弱可及对象的所有弱可及对象的所有弱可及对象的所有弱可及对象的所有弱可及对象的所有弱可及对象的所有弱可及对象的所有弱可及对象的所有弱可及对象的所有弱可及对象。同时,它将声明所有以前弱可达的对象都是可终结的。在同一时间或稍后的时间,它将把那些新清除的、已注册到引用队列中的弱引用放入队列。 自:1.2作者:Mark Reinhold
案例:
public class WeakReferenceTest { public static void main(String[] args) { Object obj=new Object(); WeakReference wrf=new WeakReference(obj); obj=null; System.out.println("未发生GC之前"+wrf.get()); System.gc(); System.out.println("内存充足,发生GC之后"+wrf.get()); } } /* 未发生GC之前java.lang.Object@167cf4d 内存充足,发生GC之后null */
虚引用
一个对象是否有虚引用的存在,不会对其生存时间造成影响,为一个对象设置虚引用的唯一目的是能在这个对象被回收时收到一个系统通知
幻像引用对象,在回收器确定它们的引用被回收之后进入队列。幻像引用最常用于以一种比Java终结机制更灵活的方式调度预分析清理。 如果垃圾收集器在某个时间点确定幻像引用的引用是半可达的,那么在那个时间点或以后的某个时间,它将对引用进行排队。 为了确保可回收对象保持原样,虚拟引用的引用可能不会被检索:虚拟引用的qet方法总是返回nuLL。与软引用和弱引用不同,虚拟引用在进入队列时不会被垃圾回收器自动清除。通过幻像引用可访问的对象将保持此状态,直到所有此类引用被清除或其本身不可访问为止。 自:1.2 作者:Mark Reinhold
案例代码:
public class PhantomReferenceTest { public static void main( String[] args ) throws InterruptedException{ Object obj = new Object(); ReferenceQueue queue = new ReferenceQueue(); PhantomReference<Object> pf = new PhantomReference<Object>(obj, queue); obj=null; while(true){ System.out.printf("pf.get() = %d, isEnqueued: %b\r\n", pf.get(), pf.isEnqueued()); if(pf.isEnqueued()) break; System.gc(); Thread.sleep(1000); } } } /* pf.get() = null, isEnqueued: false pf.get() = null, isEnqueued: true */
一个虚引用被回收的时候会把一个信息填到队列中。虚引用就干了这么件事,当某个对象被回收的时候,我们能够知道被回收了(把引用放到ReferenceQueue中)
作用:可用来管理对外内存(直接内存管理)
过程:通过虚引用指向DirectByteBuffer的时候,任何一个对象被回收时通过监测Queue,GC就能知道要不要回收堆外内存。