MST

星途 面试题库

面试题:Java虚拟机性能调优之内存管理

在Java虚拟机中,简述堆内存的分代模型以及不同代采用的垃圾回收算法,并且说明为什么要这样设计?
13.3万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

堆内存分代模型

  1. 新生代
    • 新创建的对象通常首先分配在新生代。它又分为一个较大的Eden区和两个较小的Survivor区(一般称为From Survivor和To Survivor)。大多数对象在Eden区中创建,当Eden区满时,会触发Minor GC,存活的对象会被移动到其中一个Survivor区(如From Survivor)。
    • 在后续的Minor GC中,Eden区和From Survivor区中存活的对象会被复制到To Survivor区,然后From Survivor区被清空。From Survivor区和To Survivor区角色互换,如此反复。当对象在Survivor区熬过一定次数的GC后,会晋升到老年代。
  2. 老年代
    • 存放经过多次Minor GC后依然存活的对象,这些对象生命周期较长。老年代空间一般比新生代大,因为对象晋升到老年代后通常不会轻易被回收。
  3. 永久代(Java 8之前)/元空间(Java 8及之后)
    • 永久代(Java 8之前):存储类元数据、常量池等信息。由于其大小在JVM启动时就基本固定,容易出现内存溢出问题。
    • 元空间(Java 8及之后):使用本地内存,不再受JVM堆大小限制,能动态扩展,减少了OOM(OutOfMemoryError)的风险。主要存放类的元数据等。

不同代采用的垃圾回收算法

  1. 新生代垃圾回收算法
    • 复制算法:在新生代采用复制算法。因为新生代对象具有朝生夕灭的特点,存活对象较少。复制算法将内存分为两块,每次只使用其中一块,当这块内存满时,将存活对象复制到另一块,然后清空当前使用的内存块。在新生代中,Eden区和Survivor区的设计就是基于复制算法思想,将存活对象在Eden区、Survivor区之间复制移动,实现垃圾回收。这种算法简单高效,不会产生内存碎片。
  2. 老年代垃圾回收算法
    • 标记 - 清除算法:该算法分为标记和清除两个阶段。首先标记出所有需要回收的对象,然后统一回收所有被标记的对象。但是它会产生大量不连续的内存碎片,导致后续大对象可能无法分配到足够的连续内存空间。
    • 标记 - 整理算法:在标记 - 清除算法基础上,后续增加了整理阶段,将存活对象向一端移动,然后直接清理掉端边界以外的内存,解决了内存碎片问题,但由于涉及对象移动,效率相对较低。老年代对象存活率高,移动对象成本高,所以标记 - 整理算法在老年代使用相对合适。同时,在一些垃圾回收器中,也会结合标记 - 清除算法来提高回收效率。
    • CMS(Concurrent Mark Sweep)算法:是一种以获取最短回收停顿时间为目标的老年代垃圾回收算法。它的主要过程包括初始标记(STW,Stop The World,暂停用户线程)、并发标记、重新标记(STW)、并发清除。初始标记和重新标记阶段会短暂停顿用户线程,并发标记和并发清除阶段与用户线程并发执行,尽量减少对应用程序的影响。但它会产生内存碎片,并且在并发清除阶段如果有新对象分配到老年代且空间不足时,可能会触发Full GC。
    • G1(Garbage - First)算法:在老年代采用的是标记 - 整理算法。G1将整个堆划分为多个大小相等的Region,把堆看成是Region的集合。在回收时,优先回收垃圾最多的Region(这也是G1名字的由来)。在老年代回收过程中,它通过标记存活对象,然后将存活对象移动到其他Region,实现内存整理,减少内存碎片。

这样设计的原因

  1. 分代设计原因
    • 提高回收效率:不同生命周期的对象分开管理,针对不同代的特点采用不同的垃圾回收算法。新生代对象生命周期短,朝生夕灭,采用复制算法能高效回收垃圾。老年代对象存活率高,采用适合处理存活率高对象的算法,如标记 - 整理等算法,避免频繁复制对象带来的高开销。
    • 优化内存管理:分代模型使得内存管理更加精细化。新生代的频繁回收可以快速释放空间,老年代的稳定管理可以避免大对象频繁分配和回收带来的性能问题。同时,永久代/元空间存放类元数据等,与对象存储分开,有利于管理和维护。
  2. 垃圾回收算法选择原因
    • 新生代:复制算法适合新生代对象存活率低的特点,能快速回收垃圾,且不会产生内存碎片,保证内存的连续性,有利于新对象的快速分配。
    • 老年代:老年代对象存活率高,移动对象成本高。标记 - 清除算法简单,但会产生内存碎片;标记 - 整理算法解决了内存碎片问题,虽然效率相对低些,但适合老年代情况。CMS算法追求低停顿时间,适合对停顿敏感的应用场景;G1算法将堆分区,能更好地控制停顿时间,并有效处理内存碎片问题,满足现代应用对垃圾回收的高性能要求。