1. 什么是垃圾?
在Java虚拟机中,“垃圾”指的是内存中不再使用或不可达的对象。垃圾收集器(GC)的主要任务就是识别并回收这些垃圾对象,释放内存资源。在JVM中,垃圾收集器管理的是堆(Heap)和方法区(Method Area)中的对象,它们的生命周期是动态的,需要在运行时进行分配和回收。
2. 对象是否已经死了?
JVM使用可达性分析算法来判断对象是否是垃圾。通过GC Roots(GC根节点)开始,沿着引用链向下搜索,若某个对象无法通过GC Roots访问,则认为它是垃圾,能够被回收。常见的GC Roots包括:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象,比如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等
- 方法区中类静态属性引用的对象,比如Java类的引用类型静态变量
- 方法区中常量引用的对象,比如字符串常量池(String Table)里的引用
- 本地方法栈JNI(Native方法)引用的对象
- Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如NPE,OOM)等,还有系统类加载器
- 所有被同步锁(synchronized关键字)持有的对象
- 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等
3. 什么时候回收垃圾?
垃圾回收通常在以下两种情况下触发:
- 内存分配失败:当堆内存不足以分配新对象时,GC会被触发。
- System.gc():开发者可以通过显式调用
System.gc()
来请求垃圾回收。
4. 引用
JVM提供了不同的引用类型来管理对象的生命周期,帮助开发者控制对象的回收时机。
4.1 强引用
普通的引用(如Object obj = new Object();
)属于强引用。当一个对象具有强引用时,GC不会回收该对象。
4.2 软引用
软引用用于内存不足时进行回收,JVM会尽量保留软引用对象,只有在内存紧张时才会回收。
4.3 弱引用
弱引用的对象只能存活到下一个GC发生时,垃圾回收器无论如何都会回收弱引用的对象。
4.4 虚引用
虚引用不会影响对象的生命周期,主要用于在对象被GC回收时做一些清理工作。
5. 垃圾收集算法
垃圾收集的核心是如何有效管理堆内存中的对象,JVM使用不同的垃圾收集算法来进行优化。
5.1 标记-清除算法
标记-清除算法分为两步:标记所有需要回收的对象,随后清除这些对象。缺点是效率较低,并且会造成内存碎片。
5.2 标记-复制算法
标记-复制算法将堆内存分为两块,每次只使用一块,当这块内存填满时,将存活对象复制到另一块并清理已使用的内存。此算法效率较高,但需要额外的内存空间。
5.3 标记-整理算法
与标记-清除算法类似,但是整理算法将所有存活对象移动到内存的一端,然后清理掉边界以外的内存,避免了内存碎片的问题。
6. HotSpot的算法实现细节
6.1 枚举根节点
GC的第一步是找到所有的根节点,这些根节点是GC Roots,通过它们可以遍历到所有可达的对象。在HotSpot中,使用OopMap来标记对象引用位置,帮助GC高效地找到根节点。
6.2 安全点(Safepoint)
安全点是JVM执行时程序中可暂停的点,GC只能在这些点暂停线程并进行垃圾回收。通过安全点,JVM能在不干扰程序执行的情况下进行GC。
6.3 安全区 (Safe Region)
安全区是指一段不会改变引用关系的代码区域,在这段代码执行时,GC可以安全进行。
6.4 记忆集与卡表
为了优化跨代引用带来的性能问题,HotSpot使用记忆集和卡表记录哪些对象之间存在引用关系,以减少每次GC时的检查范围,提高效率。
7. 垃圾收集器
JVM提供了多种垃圾收集器,每种垃圾收集器适用于不同的应用场景。
7.1 Serial收集器
Serial收集器是单线程的,适用于客户端模式,内存受限的环境。它的优点是内存消耗最小。
7.2 ParNew收集器
ParNew是Serial收集器的多线程版本,适用于服务端模式,能够提高垃圾回收的吞吐量。
7.3 Parallel Scavenge收集器
Parallel Scavenge收集器以吞吐量为目标,通过并行回收提高性能,适用于对吞吐量要求较高的应用。
7.4 Serial Old收集器
Serial Old收集器是Serial收集器的老年代版本,适用于客户端模式。
7.5 Parallel Old收集器
Parallel Old收集器是Parallel Scavenge收集器的老年代版本,支持多线程并发回收。
7.6 CMS收集器
CMS收集器旨在减少GC停顿时间,适用于对响应时间要求高的应用。
7.7 Garbage First收集器
G1收集器采用Region堆内存布局,目标是低延迟和高吞吐量,适用于大内存和大并发的应用。它是JVM 9及以后的默认收集器。
8. 内存分配与回收策略
8.1 年轻代(Young Generation)
年轻代用于存放新创建的对象,新生代对象存活率较低,回收效率高。新生代使用复制算法进行回收,分为Eden区和两个Survivor区。
8.2 老年代(Old Generation)
老年代存放存活较长时间的对象,采用标记-整理算法回收。长期存活的对象会晋升到老年代。
总结
JVM的垃圾收集机制通过多种垃圾收集算法和策略来优化内存回收,提升程序性能。理解不同垃圾收集器的特点及内存分配策略,能够帮助开发者更好地管理Java应用的内存,减少垃圾回收带来的性能损耗。同时,选择合适的垃圾收集器和内存策略,可以在不同场景下提升应用的响应速度和吞吐量。
标签:收集器,对象,回收,GC,垃圾,JVM,内存 From: https://blog.csdn.net/fulai00/article/details/143906164