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

JVM垃圾收集中判断对象存活相关问题

如何判断对象是否存活?

1.1. 引用计数算法

        引用计数算法基本思路

  • 在对象内部,添加并维护一个引用计数器;
  • 每当有一个地方引用它的时候,计数器就加 +1;
  • 每当有一个引用失效的时候,计数器就减 -1;
  • 当计数器的值为 0 的时候,那么该对象就是可被 GC 回收的垃圾对象

        注:引用计数算法存在的问题:对象循环引用
a 对象引用了 b 对象,b 对象也引用了 a 对象,a、b 对象却没有再被其他对象所引用了,其实正常来说这两个对象已经是垃圾了,因为没有其他对象在使用了,但是计数器内的数值却不是 0,所以引用计数算法就无法回收它们。

1.2. 可达性分析算法

        可达性分析算法(Reachability Analysis)基本思路:通过定义了一系列称为 “GC Roots” 的根对象作为起始节点集,从 GC Roots 开始,根据引用关系往下进行搜索,查找的路径我们把它称为 “引用链”。当一个对象到 GC Roots 之间没有任何引用链相连时(对象与 GC Roots 之间不可达),那么该对象就是可被 GC 回收的垃圾对象。
可达性分析算法也是 JVM 默认使用的寻找垃圾算法。

        例如:
Object 6、Object 7、Object 8 彼此之前有引用关系,但是没有与 “GC Roots” 相连,那么就会被当做垃圾所回收

1.3. Java 中的四种引用类型

1.3.1. 强引用(Strong Reference)

        强引用是使用最普遍的引用。如果一个对象具有强引用,垃圾回收器绝不会回收它。当内存空间不足时,JVM 宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

Object strongReference = new Object();

        如果强引用对象不使用时需要弱化从而使 GC 能够回收。
        弱化方式 1:显式地设置 strongReference 对象为 null,则 gc 认为该对象不存在引用,这时就可以回收这个对象。但是,具体什么时候收集这要取决于 GC 算法。例如,strongReference 是全局变量时,就需要在不用这个对象时赋值为 null,因为强引用不会被垃圾回收。

strongReference = null;

        应用场景:在 ArrayList 集合类中定义 elementData 数组,在调用 clear () 方法清空集合元素时,将每个数组元素被赋值为 null 。目的是为了将内存数组中存放的引用类型进行内存释放,可以及时释放内存。不选择将 elementData=null,是为了避免在后续调用 add () 等方法添加新元素时,需要进行内存的重新分配。

public void clear() {modCount++;// clear to let GC do its workfor (int i = 0; i < size; i++)elementData[i] = null;size = 0;
}

        弱化方式 2:让对象超出作用域范围。
应用场景:在一个方法的内部有一个强引用,这个引用保存在 VM Stack 栈中(GC Root ),而真正的引用对象 (Object) 保存在堆中。当这个方法运行完成后,就会退出方法栈,则这个对象会被回收。

public void test() {Object strongReference = new Object();// 省略其他操作
}
1.3.2. 软引用(Soft Reference)

        如果一个对象只具有软引用,则内存空间充足时,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。所以,软引用可用来实现内存敏感的高速缓存。
创建软引用,可以使用 SoftReference:

// 强引用
String strongReference = new String("abc");// 软引用
String str = new String("abc");
SoftReference<String> softReference = new SoftReference<String>(str);// 访问软引用
softReference.get();

        软引用对象是在 jvm 内存不够的时候才会被回收,我们调用 System.gc () 方法只是起通知作用,最终何时回收,由 JVM 决定。
所以,当内存不足时,JVM 首先将软引用中的对象引用置为 null,然后通知垃圾回收器进行回收:

// 软引用
String str = new String("abc");
SoftReference<String> softReference = new SoftReference<>(str);str = null;// Notify GC
System.gc();try {byte[] buff1 = new byte[900000000]; // 内存充沛// byte[] buff2 = new byte[900000000]; // 内存不足
} catch (Error e) {e.printStackTrace();
}System.out.println(softReference.get()); // abc 或 null

        应用场景:短视频 APP 中的视频缓存,后退时,显示的短视频内容是重新进行请求还是从缓存中取出呢?

  1. 如果一个短视频在播放结束时,就进行内容的回收,则后退查看前面播放的短视频时,需要重新请求。
  2. 如果将播放过的短视频存储到内存中,会造成内存的开销,甚至会造成内存溢出。
    此时,可以使用软引用解决这个实际问题:
// 获取视频播放器对象
Player videoAlayer = new Player();// 加载短视频
Video video = audioAlayer.getVideo();// 将播放完毕的短视频设置为软引用
SoftReference softReference = new SoftReference(video);// 回退或者再次播放时
if(softReference.get() != null) {// 内存充足,还没有被回收器回收,直接获取缓存video = softReference.get();
} else {// 内存不足,软引用的对象已经回收video = audioLayer.getVideo();// 重新构建软引用softReference = new SoftReference(video);
}

        软引用测试1(内存充足时):

public class Test4 {public static void main(String[] args) {String str1=new String("elysia");//软引用//str2=str1SoftReference<String> str2=new SoftReference<>(str1);//弱化强引用str1=null;//Notify GCSystem.gc();try {byte[] buff=new byte[900000000];//内存充沛
//            byte[] buff1=new byte[900000000];//内存充沛
//            byte[] buff2=new byte[900000000];//内存充沛
//            byte[] buff3=new byte[900000000];//内存不足}catch (Error error){error.printStackTrace();}//内容充足时,GC不会回收,可以正常访问//内容不足时,GC进行回收,不能正常访问,返回nullSystem.out.println(str2.get());}
}

        软引用测试2(内存不足时):

public class Test4 {public static void main(String[] args) {String str1=new String("elysia");//软引用//str2=str1SoftReference<String> str2=new SoftReference<>(str1);//弱化强引用str1=null;//Notify GCSystem.gc();try {byte[] buff=new byte[900000000];//内存充沛byte[] buff1=new byte[900000000];//内存充沛byte[] buff2=new byte[900000000];//内存充沛byte[] buff3=new byte[900000000];//内存不足}catch (Error error){error.printStackTrace();}//内容充足时,GC不会回收,可以正常访问//内容不足时,GC进行回收,不能正常访问,返回nullSystem.out.println(str2.get());}
}

1.3.3. 弱引用(Weak Reference)

        只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否都会回收它的内存。
创建弱引用,使用 WeakReference:

String str = new String("abc");
WeakReference<String> weakReference = new WeakReference<>(str);
str = null;System.gc();// 一旦发生GC,弱引用一定会被回收
System.out.println(weakReference.get());

        弱引用测试1(没有回收时):

public class Test4 {public static void main(String[] args) {String str1=new String("elysia");//弱引用WeakReference<String> str2=new WeakReference<>(str1);//弱化强引用str1=null;//此时没有进行回收System.out.println(str2.get());}
}

        弱引用测试2(进行回收后):

public class Test4 {public static void main(String[] args) {String str1=new String("elysia");//弱引用WeakReference<String> str2=new WeakReference<>(str1);//弱化强引用str1=null;//Notify GCSystem.gc();//进行回收后System.out.println(str2.get());}
}

1.3.4. 虚引用(Phantom Reference)

        虚引用是最弱的一种引用关系,如果一个对象仅持有虚引用,完全不会对其生存时间构成影响,它就和没有任何引用一样,随时可能会被回收。
虚引用,主要用来跟踪对象被垃圾回收的活动,可以在垃圾收集时收到一个系统通知
在 JDK1.2 之后,用 PhantomReference 类来表示,通过查看这个类的源码,发现它只有一个构造函数和一个 get () 方法,而且它的 get () 方法仅仅是返回一个 null,也就是说将永远无法通过虚引用来获取对象。

public class PhantomReference<T> extends Reference<T> {public T get() {return null;}
}

        案例:

public class Test4 {public static void main(String[] args) {String str1=new String("elysia");//虚引用PhantomReference<String> str3=new PhantomReference<>(str1, new ReferenceQueue<>());System.out.println(str3.get());}
}

    http://www.dtcms.com/a/392540.html

    相关文章:

  • 【C++】告别“类型转换”踩坑,从基础到四种核心强制转换方式
  • WinDivert学习文档之五-————编程API(八)
  • 【LVS入门宝典】LVS NAT模式深度解析:流量走向与IP包头修改机制
  • 第二章 微调:定制专属模型——从通用能力到场景适配
  • 为统信UOS2.0离线安装python3.11.9开发环境
  • Maven 进阶:依赖管理的 “坑” 与解决方案
  • 2.15Vue全家桶-VueRouter
  • 五、Maven引入
  • 通过 TypeScript 在 Vue 3 中利用类型系统优化响应式变量的性能
  • Maven 入门:从 “手动导包” 到 “自动化构建” 的第一步
  • 【Python】数组
  • AI任务相关解决方案18-基于大模型、MCP、Agent与RAG技术的数据分析系统研究报告
  • 飞牛NAS系统版本重大更新:支持挂载115网盘!挂载教程来袭!
  • SpringAI、Dify与Ollama的技术落地与协作
  • Python Selenium 核心技巧与实战:从基础操作到极验滑动验证码破解
  • PyQt6 实战:多源输入 ASCII 艺术转换器全解析(图片 / 视频 / 摄像头实时处理 + 自定义配置)
  • Java 大视界 —— Java 大数据在智能农业病虫害精准识别与绿色防控中的创新应用
  • Qt qDebug()调试函数,10分钟讲清楚
  • Go语言基于 DDD(Domain Driven Design)领域驱动设计架构实现备忘录 todolist
  • Go基础:Go变量、常量及运算符详解
  • c++如何开发游戏
  • 3D体素(Voxel)算法原理内容综述
  • 家庭劳务机器人进化史:从单一功能到全能管家的四阶跃迁
  • 【工具推荐及使用】——基于pyecharts的Pythpn可视化
  • Transformer实战(19)——微调Transformer语言模型进行词元分类
  • ModelView【QT】
  • ES6 promise-try-catch-模块化开发
  • webrtc弱网-ProbeController类源码分析与算法原理
  • Pycharm远程同步Jetson Orin Super
  • 深入解析Tomcat类加载器:为何及如何打破Java双亲委派模型