俗话说,温故而知新。该篇是《码出高效——Java开发手册》的读书笔记,主要是对Java中四种引用类型作个总结和回顾.
Java语言是一门OOP的静态强类型语言。在Java中,我们常常通过
1 | Object obj = new Object() |
这样的方式实例化一个对象。在JVM遇到new指令时,会触发其在堆内存上分配内存空间,设置零值(如引用类型初始设置为null),设置对象的元数据信息,最后执行初始化方法调用构造函数完成初始化工作,并通过obj这样一个引用变量关联到堆内存上的对象首地址。
通常来讲,对象在堆上被创建之后就持有一个引用。如果一个对象从GCRoots开始到其自身的引用链上不可达,那么该对象就会被JVM标记为可回收对象,在下一次GC来临时被回收掉。根据对象持有的引用类型的不同,相应的,对象在GC之间的存活时间也不同。具体来说,Java的引用类型可以分为以下四种:
- 强引用(Strong Referencce)
- 软引用 (Soft Reference)
- 弱引用 (Weak Reference)
- 虚引用 (Phantom Reference)
1. 强引用
形如
1 | Object obj = new Object() |
其实就是强引用关系。只要obj这个引用不被主动置为 null, 并且处于GC Roots 可达的状态,那么在Java虚拟机进行GC操作时,即使内存耗尽,也不会回收该对象。
2. 软引用
软引用的引用力度弱于“强引用”,是用在非必须对象的场景中。在OOM之前,垃圾回收器会把这些软引用指向的对象加入回收范围,以获得更多的内存空间。
3. 弱引用
弱引用的引用力度与上述二者相比就更弱了一些,其特点是在垃圾回收器进行年轻代回收时,如果当前被弱引用指向的对象只存在着弱引用这一引用路径的化,则该对象一定会被回收。
4. 虚引用
虚引用是最最最弱的一种引用关系(虽然我也不知道它的具体用处是啥),用虚引用完成一个对象的定义之后,随即就无法通过该引用获取到该对象,因此也有“幽灵引用”的说法。
下面给出一段SoftReference和WeakReference的使用代码,来证明一下软引用和弱引用在内存紧张的情况下其回收能力的强大。
1 | public class SoftReferenceDemo { |
设置JVM运行参数为 -Xmx20M 并运行上述程序. 可以看到当使用软引用时(即打开注释1并把对应的强引用代码注释), i的计数可以去到35000左右才发生OOM. 相反如果直接强引用,则计数i到2400的时候就已经OOM了。这表明了在内存紧张的情况下,使用软引用能够使内存空间更快更好地被释放,从而支撑了程序的正常运行。那为什么使用了软引用最后依然会OOM呢?这是因为虽然注释1处的确使用了软引用,但是它本身还是占有一定内存的,而且这些个SoftReference
1 | public class SoftReferenceIdleDemo { |
这第二段代码段会一直输出 “house is still here“,这个现象则向我们展示了,在强引用对象House的引用已经被置为null的情形下,软引用会一直持有对象new House()的有效引用。那么如果我们想在对象的强引用被置null的情况下能够感知这一动作,并且随之主动断开对同一对象的引用关系的话,显然SoftReference是做不到了,只能拜托WeakReference弱引用了.
下面这段代码可以证明这一点。
1 | public class WeakReferenceIdleDemo { |
添加JVM 启动参数 -XX:PrintGCDetails 打印GC回收细节, 运行上述代码可得到以下类似结果:
house is still here, count=85006
[GC (Allocation Failure) [PSYoungGen: 32768K->840K(37888K)] 32768K->848K(123904K), 0.0011459 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
house is still here, count=85007
house is null and existed 639 ms.
这说明,在YGC阶段,WeakReference指向的new House()对象是真的被回收了。
参考:
《码出高效——Java开发手册》 章节 7.5.1