垃圾回收器是垃圾回收算法的具体实现。新生代、老年代的垃圾回收器必须按照hotspot的要求成对组合进行使用(需要根据JDK的版本以及实际业务进行选择)
一、垃圾回收器算法:
- 分代收集理论:这是目前虚拟机使用的回收算法,它将内存分为不同的代(如新生代和老年代)。新生代使用复制算法,老年代使用标记-整理算法。这种方法结合了不同算法的优点,提高了垃圾回收的效率。
- 复制算法:在复制算法中,内存被分为两块,每次使用其中一块。当这块内存用完时,将存活的对象复制到另一块上,并清除当前内存块中的所有对象。这种方法适用于存活对象较少的情况,因为它只需要扫描一次内存。缺点是需要一块空的内存空间,并且需要复制移动对象
- 标记整理算法:这种算法在标记阶段标记所有存活的对象,然后将存活的对象压缩到内存的一端,清理边界外的所有空间。这种方法避免了内存碎片的产生,适用于老年代对象
- 标记清理算法:这是最基本的垃圾回收算法之一。它分为两个阶段:标记阶段和清除阶段。在标记阶段,从根节点(GC Roots)开始,标记所有从根节点开始的对象。在清除阶段,清除所有未被标记的对象。这种算法适用于存活对象较多的情况,但缺点是容易产生内存碎片
二、垃圾回收器种类及其特点:
-
单线程垃圾收集器:如Serial和Serial Old,适用于单核处理器环境,简单高效。应用场景:适用于客户端应用程序或者硬件资源有限的环境
-
多线程垃圾收集器:如Parallel Scavenge和Parallel Old,适用于多核处理器环境,可以并行处理垃圾回收任务,提高效率。应用场景:一些后台计算任务、大数据处理等场景
-
并发垃圾收集器:如ParNew和CMS,可以在垃圾回收时应用继续运行,减少停顿时间。应用场景:服务器端应用
-
智能并发垃圾收集器:如G1,能够根据应用程序的运行情况动态调整垃圾回收策略,适用于大内存应用
三、详解垃圾收集器:
3.1 Serial收集器(-XX:+UseSerialGC -XX:+UseSerialOldGC)
3.2 Parallel Scavenge收集器(-XX:+UseParallelGC(年轻代),-XX:+UseParallelOldGC(老年代))
3.3 ParNew收集器(-XX:+UseParNewGC)
3.4 CMS收集器(-XX:+UseConcMarkSweepGC(old))
整个过程分为四个步骤:
-
黑色(Black):黑色对象是已经访问过的对象,并且其所有引用(指向其他对象的引用)都已经扫描过。如果一个对象是黑色的,那么垃圾回收器可以确定这个对象及其所有可达的子对象都不会被回收。
-
灰色(Gray):灰色对象是已经被访问过,但是其引用(指向其他对象的引用)还没有完全扫描的对象。垃圾回收器正在处理这些对象的引用,以便将它们标记为黑色或再次标记为灰色(如果它们指向了新的未访问对象)。
-
白色(White):白色对象是最初未被访问到的对象,即它们没有被标记为可达的。理论上,这些对象可以被回收。
-
优点和挑战
(1)优点:
-
允许垃圾回收器在不停止整个应用程序的情况下进行部分垃圾回收。
-
提高了垃圾回收的效率,减少了停顿时间。
(2)挑战:
-
实现复杂:需要精确控制并发执行和重新检查阶段的逻辑。
-
浮动垃圾问题:即使在重新检查阶段也可能出现浮动垃圾。
-
并发控制:必须小心处理并发执行中的线程安全问题。
四、G1
- G1分区基本思路:G1将Java堆划分为多个大小相等的独立区域(Region),JVM最多可以有2048个Region。一般Region大小等于堆大小除以2048,比如堆大小为4096M,则Region大小为2M,当然也可以用参数"-XX:G1HeapRegionSize"手动指定Region大小,但是推荐默认的计算方式。默认新生代占整体的5%,可以通过“-XX:G1NewSizePercent”设置新生代初始占比
- 分区类型:自由分区、新生代分区、大对象分区、老年代分区
-
什么场景适合使用G11. 50%以上的堆被存活对象占用2. 对象分配和晋升的速度变化非常大3. 垃圾回收时间特别长,超过1秒4. 8GB以上的堆内存(建议值)5. 停顿时间是500ms以内
五、ZGC
在2018年9月发布的JDK 11(linux/x64)中,ZGC作为实验性质的特性被正式引入JVM中,在JDK15中正式成为特性.