JVM 内存分配策略与回收策略

JVM 内存分配策略和回收策略是调优 JVM 性能时需要考虑的关键因素。以下是它们的详细说明:

内存分配策略
JVM 内存分配策略通常涉及两个方面:对象的内存分配和内存分配的位置。下面是常见的内存分配策略:

对象的内存分配
指针碰撞
这是在堆中分配连续空间的一种策略。当分配器在分配内存之前知道哪些内存是可用的时,这种策略是有效的。

空闲列表
这是在堆中分配不连续的空间的一种策略。当分配器无法确定哪些内存块是可用的时,该策略是有用的。

内存分配的位置
Eden Space
新创建的对象首先分配到 Eden 区域。当 Eden 区域满时,将触发一次 Minor GC。

Survivor Space
如果对象在 Eden 区域中存活了一段时间,它将被移动到 Survivor 区域。这些区域用于存储从 Eden 区域中幸存下来的对象。

Tenured Generation
在经历了一定数量的 Minor GC 后,仍然存活的对象会被移动到年老代。这些对象可能会在一次 Major GC 中被回收。

直接内存
直接内存分配是另一种内存分配策略。JVM 通过使用 ByteBuffer.allocateDirect() 方法从直接内存分配器中分配内存。由于直接内存直接在本地内存中分配,因此可以获得更高的性能。

内存回收策略
内存回收是 JVM 的一个重要部分。以下是常见的内存回收策略:

Minor GC
当 Eden 区域被填满时,将触发一次 Minor GC。在 Minor GC 中,Eden 区域中的无用对象将被清除,并将有用的对象移动到 Survivor 区域。

Major GC
在经过多次 Minor GC 后,年老代可能会被填满。当这种情况发生时,将触发一次 Major GC。在 Major GC 中,年老代中的无用对象将被清除。

Full GC
Full GC 是清除整个堆的过程,包括 Eden、Survivor 和年老代。Full GC 通常需要更长的时间来完成,因为它需要遍历整个堆。

CMS GC
CMS GC 是一种针对响应时间的内存回收策略。它的目标是在减少停顿时间的同时最小化内存占用。

JVM内存回收策略通常采用分代垃圾收集算法,不同年龄段的对象采用不同的回收策略,以达到更高的效率和更低的暂停时间。

在新生代中,通常使用复制算法来回收内存。新生代中的对象一般都是生命周期短暂的,因此采用复制算法回收内存更加高效。该算法将内存划分为两个区域,一个Eden区和两个Survivor区(通常是两个相同大小的区域),对象首先被分配到Eden区,当Eden区满时,活着的对象将被复制到Survivor区中,如果Survivor区也满了,那么需要将其中的存活对象复制到另一个Survivor区中。在复制过程中,内存中的数据将被整理,同时清除不再使用的对象。

在老年代中,通常使用标记-清除或标记-整理算法回收内存。老年代中的对象一般都是生命周期较长的,采用复制算法会浪费大量的内存,因此采用标记-清除或标记-整理算法回收内存。标记-清除算法首先标记不再使用的对象,然后清除标记的对象。标记-整理算法首先标记不再使用的对象,然后将所有存活的对象移动到一端,然后清理其余部分的对象。

在堆中的不同区域使用不同的内存分配策略。在Eden区中,内存分配通常采用指针碰撞(Bump the Pointer)或空闲列表(Free List)算法,其中指针碰撞假定内存分配在内存的一端,空闲列表则使用一个指针列表来维护可用的内存块。在老年代中,由于内存碎片化的可能性较高,因此采用空闲列表算法更加常见。

除了这些常用的策略之外,还有一些其他的策略和算法,例如增量式垃圾收集、压缩算法等,这些策略和算法通常用于特定的场景和要求。