当前位置: 首页 > article >正文

java中的强引用(Strong reference),软引用(SoftReference),弱引用(WeakReference),虚引用(PhantomReference)

之前在看深入理解Java虚拟机一书中第一次接触相关名词,但是并不理解,只知道Object obj = new Object()类似这种操作的时候,obj就是强引用。强引用不会被gc回收直到gc roots不可达时。而对其他三个名词并不清楚,因为并没有被真正使用过。通过查看软引用,弱引用和虚引用的源码,可以看出这三个类都是继承自Reference。

技术分享图片

一 、概念


1.1软引用(SoftReference)
我理解的软引用的意思是,即使引用对象没有被使用了,gc也不会马上回收,而是只有当堆内存空间不够时才会回收。一般用于缓存对象,因为希望在内存中保留的越久越好。软引用所指向的引用对象停留在堆内存的时间由当前可用堆内存的大小来控制。软引用中有两个字段:clock和timestamp。clock记录初始化或gc的时间,而get引用对象时会更新timestamp与clock时间一致。如果clock和timestamp的间隔时间超过maxInternal,则认为该引用对象很久没有使用是时候回收该引用对象,否则会继续存在。maxInternal的时间有当前可用堆内存大小决定。
1.2弱引用 (WeakReference)
弱引用的意思是如果引用对象gc roots不可达,gc就会进行回收。get操作可能返回引用对象(如果没有gc),否则返回空。
1.3虚引用 (PhantomReference)
虚引用不能返回引用对象,因为get操作返回的一直是null,感觉就是一个虚无缥缈的引用,所以这是称之为虚引用的原因吧。


二 、实例

public class TestObj {private final int _1M=1024*1024;private String name;private String field;private byte[] byteArr=new byte[_1M];public TestObj(String name,String field){this.name=name;this.field=field;}public String toString(){return "TestObj{"+"name='"+name+'\''+",field='"+field+'\''+'}';}
}

构造一个对象TestObj,它的大小为1M多一点。
JVM参数设置:
-Xms16M 初始堆大小
-Xmx16M 堆的最大值
-Xmn8M 年轻代的大小
-XX:+PrintGCDetails
-XX:SurvivorRatio=8   eden:survivor的比例为8:1

第一种情况:
 

/*** -Xms8M 初始堆大小* -Xmx8M  堆最大* -Xmn4M  年轻代大小* -XX:+PrintGCDetails 打印gc日志* -XX:SuvivorRatio=8 eden:suvivor=8:1*/
public class TestJavaReference {public static void main(String[] args){TestObj testObjForSoft = new TestObj("softReference","value");TestObj testObjForWeak = new TestObj("weakReference","value");TestObj testObjForPhantom = new TestObj("phantomReference","value");SoftReference<TestObj> softReference = new SoftReference<TestObj>(testObjForSoft);WeakReference<TestObj> weakReference = new WeakReference<TestObj>(testObjForWeak);ReferenceQueue<TestObj> phantomReferenceQueue = new ReferenceQueue<TestObj>();PhantomReference<TestObj> phantomReference = new PhantomReference<TestObj>(testObjForPhantom,phantomReferenceQueue);System.gc();testObjForSoft=null;testObjForWeak=null;testObjForPhantom=null;TestObj[] cost = new TestObj[3];//给引用类型数组元素赋值,消耗3M多点的内存空间cost[0]=new TestObj("cost1","cost");cost[1]=new TestObj("cost2","cost");cost[2]=new TestObj("cost3","cost");//直接实例化基础类型数组,消耗内存//消耗4M多点的堆空间,触发对weakReference,phantomReference实例的回收//byte[] cost4= new byte[1024*1024*4];//再消耗4M多点的堆空间,触发softReference实例的回收//byte[] cost5= new byte[1024*1024*4];TestObj softObj = softReference.get();if(softObj!=null){System.out.println("softReference reference object is still alive! "+softObj.toString());}else{System.out.println("softReference reference object is not alive!");}TestObj weakObj = weakReference.get();if(weakObj!=null){System.out.println("weakReference reference object is still alive! "+weakObj.toString());}else{System.out.println("weakReference reference object is not alive!");}Reference<TestObj> phantomReferenceFromQueue = (Reference<TestObj>)phantomReferenceQueue.poll();if(phantomReferenceFromQueue!=null){TestObj phantomObj = phantomReferenceFromQueue.get();if(phantomObj!=null){System.out.println("phantomReference reference object is still alive! "+phantomObj.toString());}else{System.out.println("phantomReference reference object is not alive!");}}}
}

gc过程:
 

[GC (System.gc()) [PSYoungGen: 6875K->510K(7680K)] 6875K->4334K(15872K), 0.0042607 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 510K->0K(7680K)] [ParOldGen: 3824K->4298K(8192K)] 4334K->4298K(15872K), [Metaspace: 3315K->3315K(1056768K)], 0.0127617 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] 
softReference reference object is still alive! TestObj{name='softReference',field='value'}
weakReference reference object is still alive! TestObj{name='weakReference',field='value'}
HeapPSYoungGen      total 7680K, used 3329K [0x00000000ff800000, 0x0000000100000000, 0x0000000100000000)eden space 7168K, 46% used [0x00000000ff800000,0x00000000ffb40500,0x00000000fff00000)from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)to   space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)ParOldGen       total 8192K, used 4298K [0x00000000ff000000, 0x00000000ff800000, 0x00000000ff800000)object space 8192K, 52% used [0x00000000ff000000,0x00000000ff432aa8,0x00000000ff800000)Metaspace       used 3379K, capacity 4500K, committed 4864K, reserved 1056768Kclass space    used 361K, capacity 388K, committed 512K, reserved 1048576K

分析:
调用System.gc的时候对象强引用还存在,所以三个引用对象会进入到老年代。此时老年代保存softReference,weakReference,phantomReference这三个TestObj实例,eden中保存cost数组中三个TestObj实例。


第二种情况:
 

/*** -Xms8M 初始堆大小* -Xmx8M  堆最大* -Xmn4M  年轻代大小* -XX:+PrintGCDetails 打印gc日志* -XX:SuvivorRatio=8 eden:suvivor=8:1*/
public class TestJavaReference {public static void main(String[] args){TestObj testObjForSoft = new TestObj("softReference","value");TestObj testObjForWeak = new TestObj("weakReference","value");TestObj testObjForPhantom = new TestObj("phantomReference","value");SoftReference<TestObj> softReference = new SoftReference<TestObj>(testObjForSoft);WeakReference<TestObj> weakReference = new WeakReference<TestObj>(testObjForWeak);ReferenceQueue<TestObj> phantomReferenceQueue = new ReferenceQueue<TestObj>();PhantomReference<TestObj> phantomReference = new PhantomReference<TestObj>(testObjForPhantom,phantomReferenceQueue);System.gc();testObjForSoft=null;testObjForWeak=null;testObjForPhantom=null;TestObj[] cost = new TestObj[3];//给引用类型数组元素赋值,消耗3M多点的内存空间cost[0]=new TestObj("cost1","cost");cost[1]=new TestObj("cost2","cost");cost[2]=new TestObj("cost3","cost");//直接实例化基础类型数组,消耗内存//消耗4M多点的堆空间,触发对weakReference,phantomReference实例的回收byte[] cost4= new byte[1024*1024*4];//再消耗4M多点的堆空间,触发softReference实例的回收//byte[] cost5= new byte[1024*1024*4];TestObj softObj = softReference.get();if(softObj!=null){System.out.println("softReference reference object is still alive! "+softObj.toString());}else{System.out.println("softReference reference object is not alive!");}TestObj weakObj = weakReference.get();if(weakObj!=null){System.out.println("weakReference reference object is still alive! "+weakObj.toString());}else{System.out.println("weakReference reference object is not alive!");}Reference<TestObj> phantomReferenceFromQueue = (Reference<TestObj>)phantomReferenceQueue.poll();if(phantomReferenceFromQueue!=null){TestObj phantomObj = phantomReferenceFromQueue.get();if(phantomObj!=null){System.out.println("phantomReference reference object is still alive! "+phantomObj.toString());}else{System.out.println("phantomReference reference object is not alive!");}}}
}

gc过程:
 

[GC (System.gc()) [PSYoungGen: 6577K->502K(7680K)] 6577K->4354K(15872K), 0.0289419 secs] [Times: user=0.02 sys=0.00, real=0.03 secs] 
[Full GC (System.gc()) [PSYoungGen: 502K->0K(7680K)] [ParOldGen: 3852K->4291K(8192K)] 4354K->4291K(15872K), [Metaspace: 3208K->3208K(1056768K)], 0.0242277 secs] [Times: user=0.05 sys=0.00, real=0.02 secs] 
[GC (Allocation Failure) [PSYoungGen: 3282K->96K(7680K)] 7573K->7459K(15872K), 0.0013541 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 96K->0K(7680K)] [ParOldGen: 7363K->6186K(8192K)] 7459K->6186K(15872K), [Metaspace: 3244K->3244K(1056768K)], 0.0123107 secs] [Times: user=0.05 sys=0.00, real=0.01 secs] 
softReference reference object is still alive! TestObj{name='softReference',field='value'}
weakReference reference object is not alive!
phantomReference reference object is not alive!
HeapPSYoungGen      total 7680K, used 4593K [0x00000000ff800000, 0x0000000100000000, 0x0000000100000000)eden space 7168K, 64% used [0x00000000ff800000,0x00000000ffc7c5e0,0x00000000fff00000)from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)ParOldGen       total 8192K, used 6186K [0x00000000ff000000, 0x00000000ff800000, 0x00000000ff800000)object space 8192K, 75% used [0x00000000ff000000,0x00000000ff60a8b8,0x00000000ff800000)Metaspace       used 3273K, capacity 4500K, committed 4864K, reserved 1056768Kclass space    used 353K, capacity 388K, committed 512K, reserved 1048576K

分析:
由于此时年轻代不能再分配一个4M多的空间,而老年代也放不下,所以会发生full gc, 弱引用和虚引用的对象会被回收,而软引用还存在。此时老年代保存softReference这个TestObj实例,以及cost数组中三个TestObj实例,eden中保存cost4这个byte数组。


第三种情况:
 

/*** -Xms8M 初始堆大小* -Xmx8M  堆最大* -Xmn4M  年轻代大小* -XX:+PrintGCDetails 打印gc日志* -XX:SuvivorRatio=8 eden:suvivor=8:1*/
public class TestJavaReference {public static void main(String[] args){TestObj testObjForSoft = new TestObj("softReference","value");TestObj testObjForWeak = new TestObj("weakReference","value");TestObj testObjForPhantom = new TestObj("phantomReference","value");SoftReference<TestObj> softReference = new SoftReference<TestObj>(testObjForSoft);WeakReference<TestObj> weakReference = new WeakReference<TestObj>(testObjForWeak);ReferenceQueue<TestObj> phantomReferenceQueue = new ReferenceQueue<TestObj>();PhantomReference<TestObj> phantomReference = new PhantomReference<TestObj>(testObjForPhantom,phantomReferenceQueue);System.gc();testObjForSoft=null;testObjForWeak=null;testObjForPhantom=null;TestObj[] cost = new TestObj[3];//给引用类型数组元素赋值,消耗3M多点的内存空间cost[0]=new TestObj("cost1","cost");cost[1]=new TestObj("cost2","cost");cost[2]=new TestObj("cost3","cost");//直接实例化基础类型数组,消耗内存//消耗4M多点的堆空间,触发对weakReference,phantomReference实例的回收byte[] cost4= new byte[1024*1024*4];//再消耗4M多点的堆空间,触发softReference实例的回收,这里要分步初始化,防止出现内存溢出byte[] cost5= new byte[1024*1024*2];byte[] cost6= new byte[1024*1024];byte[] cost7= new byte[1024*1024];TestObj softObj = softReference.get();if(softObj!=null){System.out.println("softReference reference object is still alive! "+softObj.toString());}else{System.out.println("softReference reference object is not alive!");}TestObj weakObj = weakReference.get();if(weakObj!=null){System.out.println("weakReference reference object is still alive! "+weakObj.toString());}else{System.out.println("weakReference reference object is not alive!");}Reference<TestObj> phantomReferenceFromQueue = (Reference<TestObj>)phantomReferenceQueue.poll();if(phantomReferenceFromQueue!=null){TestObj phantomObj = phantomReferenceFromQueue.get();if(phantomObj!=null){System.out.println("phantomReference reference object is still alive! "+phantomObj.toString());}else{System.out.println("phantomReference reference object is not alive!");}}}
}

gc过程:
 

[GC (System.gc()) [PSYoungGen: 6875K->502K(7680K)] 6875K->4386K(15872K), 0.0029011 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 502K->0K(7680K)] [ParOldGen: 3884K->4327K(8192K)] 4386K->4327K(15872K), [Metaspace: 3369K->3369K(1056768K)], 0.0102190 secs] [Times: user=0.06 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 3207K->96K(7680K)] 7535K->7495K(15872K), 0.0029352 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 96K->0K(7680K)] [ParOldGen: 7399K->6375K(8192K)] 7495K->6375K(15872K), [Metaspace: 3370K->3370K(1056768K)], 0.0066042 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[Full GC (Ergonomics) [PSYoungGen: 6279K->6144K(7680K)] [ParOldGen: 6375K->6219K(8192K)] 12655K->12363K(15872K), [Metaspace: 3371K->3371K(1056768K)], 0.0173394 secs] [Times: user=0.05 sys=0.00, real=0.02 secs] 
[Full GC (Ergonomics) [PSYoungGen: 6215K->6144K(7680K)] [ParOldGen: 7243K->7243K(8192K)] 13458K->13387K(15872K), [Metaspace: 3371K->3371K(1056768K)], 0.0034531 secs] [Times: user=0.05 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 6144K->6144K(7680K)] [ParOldGen: 7243K->6190K(8192K)] 13387K->12334K(15872K), [Metaspace: 3371K->3371K(1056768K)], 0.0068943 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
softReference reference object is not alive!
weakReference reference object is not alive!
phantomReference reference object is not alive!
HeapPSYoungGen      total 7680K, used 6408K [0x00000000ff800000, 0x0000000100000000, 0x0000000100000000)eden space 7168K, 89% used [0x00000000ff800000,0x00000000ffe42340,0x00000000fff00000)from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)ParOldGen       total 8192K, used 7214K [0x00000000ff000000, 0x00000000ff800000, 0x00000000ff800000)object space 8192K, 88% used [0x00000000ff000000,0x00000000ff70b828,0x00000000ff800000)Metaspace       used 3379K, capacity 4500K, committed 4864K, reserved 1056768Kclass space    used 361K, capacity 388K, committed 512K, reserved 1048576K

分析:
由于eden:7168k的空间已经被使用了89%,所以不能在放下一个1M的对象,而且老年代8192k的空间也已经消耗了88%,不能再放下一个1M的对象。所以只能full gc(当准备要触发一次young GC时,如果发现统计数据说之前young GC的平均晋升大小比目前old gen剩余的空间大,则不会触发young GC而是转为触发full GC。full gc 和 young gc关系参考:https://www.zhihu.com/question/41922036/answer/93079526),所以软引用所指对象被最终收回。果然软引用存活时间够长,只要堆内存足够。

http://www.lryc.cn/news/2416402.html

相关文章:

  • session.setAttribute和request.setAttribute的区别
  • 白盒模型和黑盒模型
  • RT-Thread : IEEE1588/PTP 协议的实现
  • 二、Linux开发中常用到的命令
  • C#学习教程14——进程与线程
  • centos7无界面系统物理机安装教程超详细完整教程图解
  • 100款绿色重量级软件
  • 正则表达式匹配和替换
  • Turbo C安装与配置
  • STIL和WGL的例子文件
  • 清华紫光输入法linux,清华紫光拼音输入法
  • Pycharm、Vscode设置美女背景【内附20张高清图片】
  • Android开发知识(十)快速接入高德地图SDK(地图+定位+标记+路线规划+搜索)
  • java 调用webservice的各种方法总结
  • Android activity-alias 的用法解析
  • java.lang.String--String常用方法介绍
  • android中setBackgroundColor()中不能设置背景颜色的问题
  • mentohust找不到服务器 重启认证,mentohust官方使用说明(全+转)
  • TranslateMessage()函数
  • Java课程设计:Servlet+JSP+MySql 实现的图书管理系统(内附源码)
  • C语言—scanf函数的详解(上)
  • 函数的调用及作用域D08
  • 【计算机网络】计算机网络的体系结构
  • 代数几何与复分析:黎曼曲面与代数曲线
  • 【SpringBoot应用篇】SpringBoot集成JUnit单元测试
  • tinyxml和rapidxml
  • Windows上Qt开发环境搭建
  • 浅析入侵检测系统及最新研究
  • 攻防世界-easyphp 思路分析
  • 文献翻译 | Frequency Domain De-correlation Parameter in Speech Noise Reduction System Based on Frequency