Java虚拟机--判断可触及性(七)
知识点的梳理:
- 可触及性包含3种状态:对象只有在不可触及状态时才会被回收!;
- 可触及的:从根节点开始,可以到达这个对象;
- 可复活的:对象的所有引用都被释放,但是对象有可能在finalize()函数中复活;
- 不可触及的:对象的finalize()函数被调用,并且没有复活,就会进入不可触及状态。该状态的对象不可能被复活,因为finalize()函数只会被调用一次;
- Java提供4个级别的引用:强引用,软引用,弱引用,虚引用;
- 说明
- 可触及性的必要性:不被使用的对象,很有可能借助finalize()函数复活自己,所以我们需要一个状态来判定一个对象到底是否可回收!
- 示例1:对象的复活
public class CanReliveObj { public static CanReliveObj obj; @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("CanReliveObj finalize called"); obj = this; } @Override public String toString() { return "I am CanReliveObj"; } public static void main(String[] args) throws InterruptedException { obj = new CanReliveObj(); //第一次清除对象,对象通过finalize()复活 obj = null; System.gc(); Thread.sleep(1000); if(obj == null){ System.out.println("obj是null"); }else{ System.out.println("obj 可用"); } System.out.println("第2次GC"); //第二次清除对象,无法再复活 obj = null; System.gc(); Thread.sleep(1000); if(obj == null){ System.out.println("obj是null"); }else{ System.out.println("obj 可用"); } } } |
- 引用和可触及性的强度
- 软引用,弱引用,虚引用可在java.lang.ref包中找到它们。FinalReference表示"最终"引用,它用于实现对象的finalize()方法
- 强引用
- 能力:程序中一般的引用类型,此级别的对象是可触及的,不会被回收;
- 能力特点:
- 可直接访问目标对象;
- 强引用指向的对象在任何时候都不会被回收,虚拟机宁愿抛出OOM异常,也不会回收强引用所指向的对象;
- 强引用可能导致内存泄漏;
- 示例:
步骤1 : StringBuffer str = new StringBuffer("Hello World"); |
说明:假设此段代码在函数体内运行,那么局部变量str将被分配在栈上,而对象StringBuffer实例被分配在堆上。局部变量str指向StringBuffer实例所在堆空间,通过str可以操作该实例,那么str就是StringBuffer实例的强引用 |
步骤2:StringBuilder str1 = str; |
此时str1指向str指向的对象,同时在局部变量表上会分配空间存放str1变量。此时,该StringBuffer示例就有两个引用。对引用的"=="操作用于表示两操作数所指向的堆空间地址是否相同,不表示两操作数所指向的对象是否相等 |
这两个引用都是强引用 |
- 软引用--- 可被回收的引用
- 能力:比强引用弱一点的,软可触及的引用类型;
- 能力特点:
- 堆空间不足时,会被回收;
- 使用java.lang.SoftReference类实现;
- 示例1:软引用在系统堆内存不足时被回收
public class SoftRef { //声明User类 public static class User { public User(int id,String name){ this.id = id; this.name = name; } public int id; public String name; @Override public String toString() { return "User [id=" + id + ", name=" + name + "]"; } } public static void main(String[] args) { User u = new User(1,"geym");//建立user实例,强引用 SoftReference<User> userSoftRef = new SoftReference<SoftRef.User>(u);//通过强引用,建立软引用 u = null;//去除强引用 System.out.println(userSoftRef.get());//从软引用获取强引用对象 System.gc();//垃圾回收 System.out.println("After GC:"); System.out.println(userSoftRef.get());//再次获取软引用中的对象 byte[] b =new byte[1024*925*7];//分配一块大内存,让系统认为内存紧张 System.gc();//垃圾回收 System.out.println(userSoftRef.get());//从软引用获取数据 } } |
运行参数:-Xmx10m User [id=1, name=geym](第一次从软引用中获取数据) After GC: User [id=1, name=geym](GC没有清除软引用) |
- 示例2:每个软引用都可以附带一个引用队列,当对象的可达性状态发生改变时(由可达变为不可达),软引用对象就会进入引用队列。通过该队列,可跟踪对象的回收情况
public class SoftRefQ { public static class User{ public User(int id,String name){ this.id = id; this.name = name; } public int id; public String name; @Override public String toString() { return "User [id=" + id + ", name=" + name + "]"; } } static ReferenceQueue<User> softQueue = null; public static class CheckRefQueue extends Thread{ @Override public void run() { //跟踪引用队列 while(true){ if(softQueue != null){ UserSoftReference obj = null; try{ obj = (UserSoftReference)softQueue.remove(); }catch(Exception e){ e.printStackTrace(); } if(obj != null){ System.out.println("user id" + obj.uid+" is delete"); } } } } } //实现自定义软引用类,扩展软引用的目的是记录User.uid,在后续引用队列中,可以通过这个uid知道哪个User实例被回收了 public static class UserSoftReference extends SoftReference<User>{ int uid; public UserSoftReference(User referent,ReferenceQueue<? super User> q) { super(referent,q); uid=referent.id; } } public static void main(String[] args) throws InterruptedException { Thread t = new CheckRefQueue(); t.setDaemon(true); t.start(); User u = new User(1,"geym");//建立user实例,强引用 softQueue = new ReferenceQueue<User>();//通过强引用,建立软引用 //创建软引用时,指定了一个软引用队列,当给定的对象实例被回收时,就会被加入这个引用队列,通过访问该队列可以跟踪对象的回收情况 UserSoftReference userSoftRef = new UserSoftReference(u,softQueue); u = null;//去除强引用 System.out.println(userSoftRef.get());//从软引用获取强引用对象 System.gc();//垃圾回收 //内存足够,不会被回收 System.out.println("After GC:"); System.out.println(userSoftRef.get());//再次获取软引用中的对象 System.out.println("try to create byte array and GC"); byte[] b =new byte[1024*925*7];//分配一块大内存,让系统认为内存紧张 System.gc();//垃圾回收 System.out.println(userSoftRef.get());//从软引用获取数据 Thread.sleep(1000); } } |
使用如下参数执行此段代码:-Xmx10m User [id=1, name=geym](第一次从软引用获得对象) After GC: User [id=1, name=geym](GC后,软引用对象没有回收) try to create byte array and GC(创建大数组,耗尽内存) user id1 is delete(引用队列探测到对象被删除) null(对象已被回收,无法再通过软引用获取对象) |
- 弱引用---发现即回收
- 能力:弱可触及的引用类型。比软引用稍弱。只要发现弱引用,不管系统堆空间使用情况如何,都会将对象进行回收。
- 能力特点:
- 垃圾回收器线程优先级很低,因此,并不一定能很快发现持有弱引用的对象。这种情况下,弱引用对象可以存在较长的时间;
- 一旦一个弱引用对象被垃圾回收器回收,便会加入到一个注册的引用队列中;(这一点和软引用一样,可查看软引用的代码示例)
- 使用java.lang.ref.WeakReference类实现
- 能力分析:
- 软引用,弱引用都适合来保存那些可有可无的缓存数据。这样做,可以让系统内存不足时,让这些缓存数据被回收,不会导致内存溢出。而当内存资源充足时,这些缓存数据又可以存在相当长的时间,从而起到加速系统的作用;
- 示例1:
public class WeakRef { public static class User{ public User(int id,String name){ this.id = id; this.name = name; } public int id; public String name; @Override public String toString() { return "User [id=" + id + ", name=" + name + "]"; } } public static void main(String[] args) { User u = new User(1,"hey"); //构造弱引用 WeakReference<User> userWeakRef = new WeakReference<User>(u); u = null;//去除强引用 System.out.println(userWeakRef.get());//从弱引用中重新获取对象 System.gc();//GC //不管当前内存空间足够与否,都会回收它的内存 System.out.println("After GC:"); System.out.println(userWeakRef.get());//重新尝试从弱引用中获取对象 } } |
运行结果: User [id=1, name=hey] After GC: null |
- 虚引用--- 对象回收跟踪
- 能力:虚可触及的引用了类型。所有应用类型中最弱的一个。持有虚引用的对象,和没有引用几乎是一样的。随时都可能被垃圾回收器回收。
- 能力特点:
- 试图通过虚引用的get()方法取得强引用时,总是会失败;
- 虚引用必须和引用队列一起使用,它的作用在于跟踪垃圾回收过程;
- 当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象后,将这个虚引用加入引用队列,已通知应用程序对象的回收情况;
- 示例1:
public class TraceCanReliveObj { public static TraceCanReliveObj obj; static ReferenceQueue<TraceCanReliveObj> phantomQueue = null; public static class CheckRefQueue extends Thread{ @Override public void run() { //跟踪引用队列 while(true){ if(phantomQueue != null){ PhantomReference<TraceCanReliveObj> objt = null; try{ objt = (PhantomReference<TraceCanReliveObj>)phantomQueue.remove(); }catch(Exception e){ e.printStackTrace(); } if(objt != null){ System.out.println("TraceCanReliveObj is delete by GC"); } } } } } @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("CanReliveObj finalize called"); obj = this; } @Override public String toString() { return "I am CanReliveObj"; } public static void main(String[] args) throws InterruptedException { Thread t = new CheckRefQueue(); t.setDaemon(true); t.start(); phantomQueue = new ReferenceQueue<TraceCanReliveObj>(); obj = new TraceCanReliveObj(); PhantomReference<TraceCanReliveObj> phantomRef = new PhantomReference<TraceCanReliveObj>(obj,phantomQueue); obj = null; System.gc(); Thread.sleep(1000); if(obj==null){ System.out.println("obj是null"); }else{ System.out.println("obj可用"); } System.out.println("第2次GC"); obj = null; System.gc(); Thread.sleep(1000); if(obj == null){ System.out.println("obj是null"); }else{ System.out.println("obj可用"); } } } |
执行效果: CanReliveObj finalize called(对象复活) obj可用 第2次GC(第2次,对象无法复活) TraceCanReliveObj is delete by GC(引用队列捕获到对象被回收) obj是null |
Java虚拟机--判断可触及性(七)相关推荐
- java中对象的生存期_深入理解Java虚拟机-判断对象是否存活算法与对象引用
我们知道Java中的对象一般存放在堆中,但是总不能让这些对象一直占着内存空间,这些对象最终都会被回收并释放内存,那么我们如何判断对象已经成为垃圾呢?这篇文章会提出两种算法解决这个问题.另外,本文还要谈 ...
- Java 虚拟机 最易理解的 全面解析
先上一个最容易理解的类实例化的内存模型案例截图: 转载自:https://www.zybuluo.com/Yano/note/321063 周志明著的<深入理解 Java 虚拟机>的干货~ ...
- 《深入理解 Java 虚拟机》转载周志明
JDK 是什么? JRE 是什么? Java历史版本的特性? Java Version SE 5.0 Java Version SE 6 Java Version SE 7 Java 8 运行时数据区 ...
- 《深入理解Java虚拟机》-周志明(转)
(转载):如果文章有错误,欢迎评论或私信指出,谢谢~ https://blog.csdn.net/Yano_nankai/article/details/50957578 本文固定链接:https:/ ...
- JAVA虚拟机:JVM和Java体系架构
一.Java及JVM简介 2022 TIOBE 排行榜: https://www.tiobe.com/tiobe-index/ 二.Java 生态圈 Java 是目前应用最为广泛的软件开发平台之一.拥 ...
- 【java虚拟机系列】JVM类加载器与ClassNotFoundException和NoClassDefFoundError
在我们日常的项目开发中,会经常碰到ClassNotFoundException和NoClassDefFoundError这两种异常,对于经验足够的工程师而言,可能很轻松的就可以解决,但是却不一定明白为 ...
- 深入浅出解读 Java 虚拟机的差别测试技术
为什么80%的码农都做不了架构师?>>> 本文分享基于字节码种子生成有效.可执行的字节码文件变种,并用于 JVM 实现的差别测试.本文特别提出用于修改字节码语法的classfu ...
- 虚拟机与Java虚拟机
虚拟机 所谓虚拟机(Virtual Machine),就是一台虚拟的计算机.它是一款软件,用来执行一系列虚拟计算机指令.大体上,虚拟机可以分为系统虚拟机和程序虚拟机. 大名鼎鼎的Visual Box, ...
- 【jvm系列-01】初识虚拟机与java虚拟机
JVM系列整体栏目 内容 链接地址 [一]初识虚拟机与java虚拟机 https://blog.csdn.net/zhenghuishengq/article/details/129544460 [二 ...
最新文章
- 软件测试_单元测试反模式,完整列表
- add_compile_options和CMAKE_CXX_FLAGS的区别
- Eclipse公共许可证
- Winform开发中另一种样式的OutLookBar工具条
- Java多线程(review)
- java邮箱地址正则表达式_Java 中用正则表达式修改 Email 地址
- IT互联网公司的笔试的输入输出- c++ python
- 消除SQL Server中重要维护任务的风险
- wget 下载需要登录验证的网页文件
- 用Python实现视频字符化(蔡徐坤唱跳Rap视频)
- 怎样下载苹果 Apple Store 官方 APP 里的限时免费应用福利?
- web服务之LAMPLNMP架构
- 程序异常终止:Process finished with exit code -1073741819 (0xC0000005)
- 微软 Surface Laptop 系统恢复
- 阿里云 mysql 1045_解决阿里云登录mysql出现的1045错误
- 【Unity】Unity实现鼠标控制摄像机围绕物体旋转镜头 滑轮控制远近
- Java如何学才能快速入门(零基础入门)
- 数字内容市场水量上涨,阜博集团能否成为“鲶鱼”?
- 秒杀所有区间相关问题
- 仿 Lenovo商城首页