垃圾回收相关八股
文章目录
- 堆内存被通常分为下面三部分:
- 垃圾回收概念
- 内存分配和回收原则(内存分配策略、垃圾回收策略):
- 垃圾回收区域和垃圾回收的对象:
- 如何触发垃圾回收?
- minorGC、majorGC、fullGC:
- 判断一个对象是否为垃圾(不再被引用了)的方法:
- 判断一个常量(运行时常量池中)是否为废弃常量:
- 判断一个类是否为无用类(满足以下三个条件):
- 垃圾收集算法:
- 垃圾收集器:
- 垃圾回收器 CMS 和 G1的区别?
- 三色标记法:
堆内存被通常分为下面三部分:
1. 新生代内存(Eden 区、两个 Survivor 区 S0 和 S1 都属于新生代)
2. 老年代
3. 永久代,后来被元空间取代,在本地内存中。
垃圾回收概念
垃圾回收(Garbage Collection, GC)是JVM自动管理内存的一种机制,主要回收不再被程序引用的对象所占用的内存,减少内存泄漏和内存管理错误的可能性。
内存分配和回收原则(内存分配策略、垃圾回收策略):
• 对象优先在 Eden 区分配
• 大对象直接进入老年代:为了减少新生代的垃圾回收频率和成本
• 长期存活的对象将进入老年代:对象都会首先在 Eden 区域分配,如果对象在 Eden 出生并经过第一次 Minor GC 后仍然能够存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间(s0 或者 s1)中,并将对象年龄设为 1。对象在 Survivor 中每熬过一次 MinorGC,年龄就增加 1 岁,当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。
• (老年代的对象不会再晋升到永久代或元空间了了,因为jdk8之后被元空间取代了,取消晋升了)
垃圾回收区域和垃圾回收的对象:
1. 堆内存(新生代和老年代):○ 通过new关键字创建的对象○ 数组对象、集合类对象○ 字符串常量(池)
2. 元空间/方法区(永久代):○ 废弃常量(运行时常量池里面的)○ 已被卸载的类信息/无用类○ 废弃的类元数据
如何触发垃圾回收?
• 内存不足时:当JVM检测到堆内存不足,无法为新的对象分配内存时,会自动触发垃圾回收。
• 手动请求:虽然垃圾回收是自动的,开发者可以通过调用 System.gc()建议JVM进行垃圾回收,只是建议,并不会保证jvm一定执行
• 对象数量或内存使用达到阈值:垃圾收集器内部实现了一些策略,以监控对象的创建和内存使用,达到某个阈值时触发垃圾回收。
• JVM参数:启动 Java 应用时可以通过 JVM 参数来调整垃圾回收的行为。
minorGC、majorGC、fullGC:
Minor GC (新生代垃圾回收):
• 作用范围:只针对新生代进行回收,包括Eden区和两个Survivor区(S0和S1)
• 触发条件:当Eden区空间不足时,JVM会触发一次Minor GC,将Eden区和一个Survivor区中的存活对象移动到另一个Survivor区或老年代。
• 特点:通常发生得非常频繁,因为新生代中对象的生命周期较短,回收效率高,暂停时间相对较短。
Major GC(老年代垃圾回收):
• 作用范围:主要针对老年代进行回收,但不一定只回收老年代。
• 触发条件:当老年代空间不足时,或者系统检测到年轻代对象晋升到老年代的速度过快,可能会触发Major GC。
• 特点:相比Minor GC,Major GC发生的频率较低,但每次回收可能需要更长的时间,因为老年代中的对象存活率较高。
Full GC(完全垃圾回收):
• 作用范围:对整个堆内存(包括年轻代、老年代)和永久代/元空间进行回收。
• 触发条件:
a. 当永久代/元空间内存不足的时候会自动触发
b. Minor GC时,如果老年代空间不足以容纳存活的对象会触发Full GC,对整个堆内存进行回收。
c. 可以通过调用 System.gc()建议JVM进行垃圾回收,只是建议,并不会保证jvm一定执行
• 特点:是最昂贵的操作,因为它需要停止所有的工作线程(Stop The World),遍历整个堆内存来查找和回收不再使用的对象,因此应尽量减少Full GC的触发。
判断一个对象是否为垃圾(不再被引用了)的方法:
• 引用计数法:为每个对象分配一个引用计数器,每当有一个地方引用它时,计数器加1;当引用失效时,计数器减1。当计数器为0时,表示对象不再被任何变量引用,可以被回收。缺点是不能解决循环引用的问题(如两个对象相互引用,没有其他引用了,但是GC无法回收)
• 可达性分析算法(主要方法):从根对象出发,向下追溯它们引用的对象,以及这些对象引用的其他对象,以此类推。如果一个对象到根对象没有任何引用链相连(即从GC Roots到这个对象不可达),那么这个对象就被认为是不可达的,可以被回收。 GC Roots根对象包括:虚拟机栈中引用的对象、本地方法栈中引用的对象、方法区中类静态属性(变量)引用的对象、方法区常量引用的对象(常量是final static修饰的)
GC Roots的设计原则:正在被使用或可能被使用的对象必须保持可达。(虚拟机栈中的引用代表当前正在执行的上下文)
判断一个常量(运行时常量池中)是否为废弃常量:
• 如果该常量不再被引用了则为废弃常量,常用方法有引用可达性分析
判断一个类是否为无用类(满足以下三个条件):
1. 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
2. 加载该类的 ClassLoader 已经被回收。
3. 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
垃圾收集算法:
1. 标记-清除算法:分为“标记(Mark)”和“清除(Sweep)”阶段:首先标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象。缺点是效率不高和垃圾碎片(会产生大量不连续的内存碎片)
2. 标记-整理算法:首先标记出所有不需要回收的对象,然后让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。
3. 复制/标记-复制算法:将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
4. 分代收集算法:新生代采用复制算法,老年代采用标记-清除 或 标记-整理算法
标记复制的标记过程和复制过程会不会停顿?
在标记-复制算法 中,标记阶段和复制阶段都会触发STW。
标记阶段停顿是为了保证对象的引用关系不被修改。
复制阶段停顿是防止对象在复制过程中被修改。
垃圾收集器:
作用:自动管理 Java 应用程序的运行时内存;它负责识别哪些内存是不再被应用程序使用的,并释放这些内存以便重新使用。减少了程序员手动管理内存的负担,降低了内存泄漏和溢出错误的风险。
“Stop The World”(STW) 是指 JVM 暂停所有应用线程(用户线程),只让垃圾回收线程运行,直到垃圾回收完成。
1. Serial +Serial Old 收集器:新生代采用标记-复制算法,老年代采用标记-整理算法。○ 特点:单线程 意味着它只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程( "Stop The World" ),直到它收集结束。○ 缺点:Stop The World 带来的不良用户体验○ 优点:简单而高效;由于没有线程交互的开销,自然可以获得很高的单线程收集效率
2. ParNew 收集器:仅负责新生代,采用标记-复制算法○ 特点:ParNew 收集器其实就是 Serial 收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样。○ 缺点:和Serial 收集器一样也会有Stop The World
3. Parallel Scavenge + Parallel Old 收集器:新生代采用标记-复制算法,老年代采用标记-整理算法。○ 特点:关注点是吞吐量(高效率的利用 CPU)
4. CMS 收集器:CMS 仅负责老年代,采用标记-清除”算法 (详见二哥)○ 特点:是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用。(第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作)○ 优点:并发收集、低停顿(可能是标记清除用时较短吧)○ 缺点:对cpu资源敏感、标记-清除算法会产生大量的空间碎片
5. G1 收集器:新生代采用标记-复制算法,老年代采用标记-整理算法。○ 特点是:以极高概率满足 GC 停顿时间要求的同时,还具备高吞吐量性能特征。○ 优点:可以预测垃圾回收停顿时间,避免了内存碎片问题;回收的范围是整个Java堆(包括新生代,老年代),而前六种收集器回收的范围仅限于新生代或老年代。
6. ZGC 收集器:不区分新生代和老年代,都采用标记-整理算法回收,后来也推出分代的了○ 特点:通过全堆并发标记-整理实现亚毫秒级停顿,适合超大堆和极致低延迟场景。○ 适用于大内存低延迟服务的内存管理和回收,在 128G 的大堆下,最大停顿时间才 1.68 ms,性能远胜于 G1 和 CMS。
垃圾回收器 CMS 和 G1的区别?
1. 使用的范围不一样:○ CMS收集器是老年代的收集器,可以配合新生代的Serial和ParNew收集器一起使用○ G1收集器收集范围是老年代和新生代。不需要结合其他收集器使用
2. STW的时间:○ CMS收集器以最小的停顿时间为目标的收集器○ G1收集器可预测垃圾回收的停顿时间(建立可预测的停顿时间模型)
3. 垃圾碎片:○ CMS收集器是使用“标记-清除”算法进行的垃圾回收,容易产生内存碎片○ G1收集器使用的是“标记-整理”算法,进行了空间整合,没有内存空间碎片
4. 垃圾回收的过程不一样○ CMS:初始标记、并发标记、重新标记、并发清除○ G1:初始标记、并发标记、最终标记、筛选回收
什么情况下使用CMS,什么情况使用G1?
1. CMS适用场景:
○ 低延迟需求:适用于对停顿时间要求敏感的应用程序
○ 老年代收集:主要针对老年代的垃圾回收。
2. G1适用场景:
○ 大堆内存:适用于需要管理大内存堆的场景,能够有效处理数GB以上的堆内存。
○ 对内存碎片敏感:G1通过紧凑整理来减少内存碎片,降低了碎片化对性能的影响。
○ 高吞吐量性能:G1在提供较低停顿时间的同时,也保持了相对较高的吞吐量
三色标记法:
三色标记法用于标记对象的存活状态,它将对象分为三类:
• 白色(White):尚未访问的对象。垃圾回收结束后,仍然为白色的对象会被认为是不可达的对象,可以回收。
• 灰色(Gray):已经访问到但未标记完其引用的对象。灰色对象是需要进一步处理的。
• 黑色(Black):已经访问到并且其所有引用对象都已经标记过。黑色对象是完全处理过的,不需要再处理。
CMS垃圾收集过程(标记过程是结合使用的三色标记法完成的):
• 初始标记:标记所有从 GC Roots 根对象直接可达的对象(标记成灰色),这个阶段需要 STW,但速度很快。
• 并发标记:从初始标记的对象(灰色)出发,遍历其所有引用的对象(将其标记为为灰色,原来了灰色对象自身标记为黑色)。这个阶段是并发进的,和应用线程同时进行。。
• 重新标记:完成剩余的标记工作,重新标记阶段的目标是处理并发标记阶段遗漏的引用变化。为了确保所有存活对象都被正确标记,remark 需要在 STW 暂停期间执行。
• 并发清除:清除未被标记的对象,回收它们占用的内存空间。