面试题答案
一键面试Java JVM堆内存主要分配策略
- 指针碰撞(Bump the Pointer):
- 适用场景:当堆内存空间是规整的,即已使用的内存和未使用的内存分别在两端,中间由一个指针分隔开。这种情况通常出现在使用Serial、ParNew等采用标记 - 整理算法的垃圾回收器时。
- 分配过程:分配内存时,只需要将指针向未使用内存方向移动与对象大小相等的距离即可,就像碰撞指针一样,所以叫指针碰撞。
- 空闲列表(Free List):
- 适用场景:当堆内存空间不规整,已使用的内存和未使用的内存相互交错时,垃圾回收器(如CMS)无法简单地通过指针碰撞来分配内存。此时就需要维护一个空闲列表,记录哪些内存块是可用的。
- 分配过程:当对象需要分配内存时,从空闲列表中找到一块足够大的内存块来分配给对象,并更新空闲列表。
对象在堆内存中分配的大致流程
- 对象优先在Eden区分配:
- 当Java程序创建一个新对象时,大多数情况下,对象首先会尝试在新生代的Eden区分配内存。Eden区是新生代中较大的一块内存区域,用于存放新创建的对象。
- Eden区空间不足时:
- 如果Eden区空间不足,会触发Minor GC(新生代垃圾回收)。在Minor GC过程中,垃圾回收器会扫描Eden区和From Survivor区(Survivor区分为From和To两个区域),标记出存活的对象,并将存活对象复制到To Survivor区,同时清理Eden区和From Survivor区中不再使用的对象所占用的内存空间。
- 经过一次Minor GC后,如果To Survivor区能够容纳存活对象,那么存活对象将在To Survivor区中存储。并且这些对象的年龄(对象在Survivor区中经历的垃圾回收次数)会加1。
- 对象晋升到老年代:
- 当对象的年龄达到一定阈值(默认是15,可通过参数
-XX:MaxTenuringThreshold
调整)时,该对象会被晋升到老年代。另外,如果在Minor GC后,To Survivor区无法容纳存活对象,那么这些存活对象也会直接晋升到老年代。 - 老年代用于存放生命周期较长的对象,当老年代空间不足时,会触发Full GC(全量垃圾回收),垃圾回收器会对整个堆(包括新生代和老年代)进行垃圾回收,以释放空间。
- 当对象的年龄达到一定阈值(默认是15,可通过参数