一、垃圾回收算法
标记-复制算法
标记-复制算法是一种回收对象的算法,它将内存划分为大小相等的两块,每次只使用其中一块。当第一块内存使用完后,将该块内存上的对象复制到另一块未使用的内存上,然后清空第一块内存。这种算法基于标记-清除算法演变而来,解决了其执行效率不稳定的问题。
标记-复制算法的优点在于其解决了标记-清除算法的执行效率不稳定问题,并且可以更有效地利用网络资源,使网络中的传输更为可靠,提高了网络在处理并发请求时的效率。
此外,标记复制算法是一种在分布式系统中用于解决复制冲突的算法,它大大简化了传统复制冲突解决方案,使得非常大量的计算量和复杂性可以被减少到最小。这种算法利用标记和标记时间戳来分辨请求之间的优先级,当一个标记引用一个特定的内容或服务时,该标记被分配一个唯一的标记时间戳。如果标记时间戳比之前的请求更新,那么这个请求将会被认为是最新的,并准予处理。否则,该请求将被拒绝,因为该请求冲突于之前已经接受的请求。
标记-复制算法的缺点在于其必须有一部分空间时刻空闲着,所以会有一定的空间浪费
标记-复制算法
应用场景:标记-复制算法是一种常用的算法,主要用在年轻代回收上,年轻代实现的版本方式有Serial、ParNew、Parallel Scavenge
标记-清理算法
标记-清理(Mark-Sweep)算法是一种基础的垃圾收集算法,它的工作过程可以分为“标记”和“清除”两个阶段。
在标记阶段,垃圾收集器会从引用根节点开始,遍历所有的对象,标记出所有可达的对象。这个过程是通过可达性分析算法来完成的,如果一个对象没有被任何可达的对象引用,那么这个对象就被认为是不可达的,可以被回收。
在清除阶段,垃圾收集器会遍历整个堆内存,把那些没有被标记为可达的对象统一回收。
这种算法的优点是实现简单,但缺点是效率不高,并且可能产生大量的不连续的内存碎片,导致空间利用率下降,垃圾收集频率变高。
标记-清理算法
应用场景:CMS垃圾回收器[并发标记清理]使用的该算法,应用于老年代回收
标记-整理算法(Mark-Compact Algorithm)
标记-整理算法(Mark-Compact Algorithm)是一种垃圾回收算法,用于回收不再使用的内存空间。它主要用于老年代(Old Generation)或整个堆的垃圾回收阶段。
标记-整理算法的基本思想如下:
标记阶段:从根对象开始遍历整个对象图,标记所有与根对象可达的对象,即被引用的对象。
整理阶段:在整理阶段,标记-整理算法会对内存空间进行整理,将所有存活的对象移动到一端,而未标记的对象则被认为是垃圾对象。移动存活对象的过程中,标记-整理算法会保持存活对象之间的相对位置关系,从而确保所有存活对象在整理后仍然是连续的。
回收阶段:在整理阶段完成后,标记-整理算法会回收并释放未标记的垃圾对象所占用的内存空间,从而实现垃圾回收。
标记-整理算法的主要特点和优点包括:
解决内存碎片问题:通过整理内存空间,将存活的对象移动到一端,从而解决了标记清除算法产生的内存碎片问题。在整理后,所有存活对象在内存空间中是连续的,没有内存碎片,提高了堆的空间利用率。
高效的垃圾回收:通过整理和移动存活对象,避免了对整个堆进行扫描和标记,只需要对存活对象进行整理即可。这使得标记-整理算法的垃圾回收过程非常高效。
整理相对简单:将存活对象移动到一端,保持存活对象的相对位置关系,相对于其他复制算法等,标记-整理算法的整理阶段相对简单。
标记-整理算法
应用场景:标记-整理算法通常用于老年代的垃圾回收,因为老年代的对象生命周期较长,可能会产生较多的内存碎片
二、垃圾回收器
垃圾回收算法的用年轻代用标记复制算法,年老代标记整理算法
Serial收集器:Serial收集器的优点是简单高效、内存占用小、适用于小型堆等;缺点是停顿时间长、不适合大型堆和多核处理器环境。
- 适应场景:适用于单核CPU环境,适用于小规模数据集。
- 区域:新生代和老年代。
- 优点:
- 简单高效:Serial收集器的实现比较简单,因此在执行垃圾收集任务时效率较高,可以快速完成垃圾收集工作。
- 内存占用小:Serial收集器在执行垃圾收集任务时不需要额外的内存开销,因此在内存资源有限的环境中表现较好。
- 适用于小型堆:Serial收集器适用于堆内存较小的场景,例如客户端模式下的虚拟机运行。
- 停顿时间长:由于Serial收集器在进行垃圾收集时需要暂停所有线程,因此会导致应用程序停顿时间较长,影响用户体验和系统性能。
- 不适合大型堆:Serial收集器在处理大型堆内存的场景时表现较差,因为单线程执行垃圾收集任务效率较低,会影响整体性能。
- 不适合多核处理器环境:Serial收集器只使用一个CPU或一个线程进行垃圾收集工作,因此在多核处理器环境中无法充分利用系统资源,导致性能下降。
- JVM参数配置:通过设置JVM参数 新生代:“-XX: UseSerialGC”,老年代:“-XX: UseSerialOldGC” 来启用。
Parallel Scavenge收集器:Parallel Scavenge收集器的优点是高吞吐量、并行多线程、适合短生命周期对象等;缺点是无法处理老年代垃圾收集、对系统配置和参数调整要求较高、可能存在内存碎片化问题。
- 适应场景:适用于需要高吞吐量和响应速度的应用。
- 区域:新生代。
- 优点:
- 高吞吐量:Parallel Scavenge收集器的目标是达到一个可控制的吞吐量(Throughput),因此可以在高吞吐量下完成程序的运算任务,主要适用于在后台不需要太多交互的任务。
- 并行多线程:Parallel Scavenge收集器使用多个线程进行垃圾收集,可以并行处理垃圾收集任务,提高垃圾收集的效率和整体吞吐量。
- 适合短生命周期对象:Parallel Scavenge收集器适合处理大量短生命周期的对象,可以快速清除这些对象,提高垃圾收集的效率。
- 无法处理老年代垃圾收集:Parallel Scavenge收集器主要用于新生代垃圾收集,无法处理老年代垃圾收集,需要结合其他收集器使用。
- 对系统配置和参数调整要求较高:Parallel Scavenge收集器的性能表现需要合理的系统配置和参数调整,例如设置合适的堆大小和新生代大小等,否则可能会导致垃圾收集性能下降或出现其他问题。
- 可能存在内存碎片化问题:Parallel Scavenge收集器采用的是标记-复制算法,可能会导致内存中出现较多的碎片化区域,影响垃圾收集的性能和效率。
- JVM参数配置:通过设置JVM参数“-XX: UseParallelGC”来启用。
ParNew收集器:ParNew收集器的优点是可以并行化执行垃圾收集任务,高效利用系统资源,适合新生代垃圾收集等;缺点是需要合理的系统配置和参数调整,可能存在线程交互的开销和内存碎片化问题。
- 适应场景:是并发标记和清除阶段与应用程序线程并行执行,可以充分利用多核处理器的计算能力,提高垃圾收集的效率和整体吞吐量。
- 区域:新生代。
- 优点:
- 并行化:ParNew收集器可以在多个线程上并行执行垃圾收集任务,充分利用多核处理器的计算能力,提高垃圾收集的效率和整体吞吐量。
- 高效利用系统资源:ParNew收集器在多核心处理器环境中能够高效地利用系统资源,通过开启与处理器核心数量相等的收集线程数,可以更好地应对高负载情况下的垃圾收集任务。
- 适合新生代垃圾收集:ParNew收集器主要用于新生代垃圾收集,可以快速清除大量短生命周期的对象,提高垃圾收集的效率。
- 线程交互的开销:在单核处理器或超线程技术实现的环境中,ParNew收集器的效果可能会受到影响,因为并行化的垃圾收集需要线程之间的交互和同步,会产生一定的开销。
- 对系统配置和参数调整要求较高:ParNew收集器的性能表现需要合理的系统配置和参数调整,例如设置合适的堆大小和新生代大小等,否则可能会导致垃圾收集性能下降或出现其他问题。
- 可能存在内存碎片化问题:ParNew收集器采用的是标记-复制算法,可能会导致内存中出现较多的碎片化区域,影响垃圾收集的性能和效率。
- JVM参数配置:通过设置JVM参数“-XX: UseParallelGC”来启用。
CMS(Concurrent Mark Sweep)收集器:CMS收集器的优点是低延迟、适合老年代垃圾收集、并行处理等;缺点是对CPU资源敏感、无法处理浮动垃圾、存在内存碎片问题等。
- 适应场景:适用于需要较高并发性能的环境,常用于老年代的垃圾回收,多核小内存(4-8G)
- 区域:老年代。
- 优点:
- 低延迟:CMS收集器的目标是实现低延迟的垃圾收集,通过并发标记和清除算法,可以在应用程序运行的同时进行垃圾收集,减少应用程序的停顿时间。
- 适合老年代垃圾收集:CMS收集器主要用于老年代垃圾收集,可以处理大对象的内存分配和回收,避免出现内存碎片化问题。
- 并行处理:CMS收集器使用多线程并行处理垃圾收集任务,可以充分利用多核处理器的计算能力,提高垃圾收集的效率和整体吞吐量。
- 对CPU资源敏感:CMS收集器在并发执行时需要占用一部分CPU资源,因此对CPU资源敏感,可能会影响应用程序的性能。
- 无法处理浮动垃圾:由于并发清除阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只好留待下一次GC时再清理掉。 这一部分垃圾就称为“浮动垃圾”。
解决方案:等下一次GC回收
- 存在内存碎片问题:虽然CMS解决了内存碎片化问题,但是其使用的是标记清除算法,可能会产生一些内存碎片,影响垃圾收集的性能和效率。
解决方案:可以通过配置参数,GC回收几次后进行整理
-XX: UseCMSCompactAtFullCollection: FullGC之后做压缩整理减少碎片
-XX:CMSFullGCsBeforeCompaction: 多少次FllGC之后压缩一次,默认是0,代表每次FullGC后都会压缩一次,与full gc的发生频率负相关,如果只是将秒*时,才会触发,二三十分钟一次,就可以配量0. 如果服务一直处于高压力,gc频率比较高,就要配量3-5次full g才进行一次整理,但是不要太高,长时问不整理,会导致内存碎片化。
4.执行过程存在不确定性:会有可能上一次垃圾未收集完成,此时触发垃圾回收,特别是并发标记和并发清理阶段会出现,回收和应用程序并行,没回收完就触发了full GC,会出现“concurrent mode failure”,将进入STW,用serial old垃圾回收器回收
解决方案:可以配置触发full gc的比例阈值,还可以配置自动调整阈值,根据实际情况预留出空间。
-XX:CMSInitiatingOccupancyFraction: 当老年代使用达该比例时会触发FulIGC (默认是92%)
-XX: UseCMSInitiating0ccupancy0nly: 只使用回收的阈值(-XX:CMSInitiatingOccupancyFraction指定的),如果不指定,JVM仅在第一次使用设定值,后续则会自动调整。
-XX: UseConcMarkSweepGC: 启用CMS收集器
-XX:ConcGCThreads=4: 并发的GC线程数为4个
-XX: CMSScavengeBeforeRemark: 在CMS GC前启动一次minor gc,目的在于减少老年代对年轻代的引用,降CMS GC的标记阶段时的开销,一般CMS的GC耗时 80%都在并发标记阶段
-XX: CMSParallellnitialMarkEnabled: 表示在初始标记的多钱程执行,缩短STW
-XX: CMSParallelRemarkEnabled: 在重新标记的时候多线程执行,缩短STW
G1(Garbage-First)收集器【后续单独篇幅介绍】:G1(Garbage-First)收集器是Java 9及之后版本中默认的垃圾收集器,G1收集器的目标是使垃圾收集停顿时间与应用程序的工作负载量成正比,无论是在单核处理器还是多核处理器环境中都能提供良好的性能表现。
- 适应场景:适用于大规模数据集和高并发环境,适用于多核CPU大内存(8G以上)和分布式环境。
- 区域:新生代和老年代。
- 优点:
- 并行与并发:G1收集器在回收期间,可以有多个GC线程同时工作,有效利用多核计算能力。 此时用户线程STW。 并发性:G1收集器拥有与应用程序交替执行的能力,部分工作可以和应用程序同时执行,因此,一般来说,不会在整个回收阶段发生完全阻塞应用程序的情况。
- 分代收集:从分代上看,G1收集器依然属于分代型垃圾回收器,它会区分年轻代和老年代,年轻代依然有Eden区和Survivor区。和之前的各类回收器不同,它同时兼顾年轻代和老年代。
- 空间碎片整理:G1收集器从整体上来看是基于标记-整理算法实现的,从Region之间又是基于标记-复制算法实现的。由于G1不会产生空间碎片,可以为对象的分配提供更规整的内存。此外还避免了由于分配大对象时找不到连续的内存空间,而不得不提前触发下一次垃圾回收。
- 跨Region引用等大量双向卡表的存在,G1收集器比CMS(只需要处理老年代到新生代的引用)占用更多的内存。CMS收集器使用写后屏障来更新维护卡表,而G1收集器除了使用写后屏障维护卡表,为了实现SATB的算法,还需要使用写前屏障来跟踪并发时指针变化情况。所以G1收集器会增加程序运行时的内存占用。
- 用户程序运行过程中,G1无论是为了垃圾收集产生的内存占用(Footprint)还是程序运行时的额外执行负载(Overhead)都要比CMS要高。
- JVM参数配置:通过设置JVM参数“-XX: UseG1GC”来启用G1收集器。
ZGC(Z Garbage Collector)收集器【后续单独篇幅介绍】:ZGC收集器的优点是低延迟、内存压缩、并行处理等;缺点是对系统配置和参数调整要求较高、实现复杂度较高、内存占用较大等。在选择使用ZGC收集器时,需要根据应用程序的特点和需求进行评估和调整
- 适应场景:适用于需要高吞吐量和低延迟的应用程序,例如数据库、缓存等。
- 区域:新生代和老年代。
- 优点:
- 低延迟:ZGC收集器的目标是实现低延迟的垃圾收集,通过使用读屏障和染色指针等技术,可以在应用程序运行的同时进行垃圾收集,减少应用程序的停顿时间。
- 内存压缩:ZGC收集器使用染色指针技术,可以在垃圾收集过程中实现内存压缩,减少内存碎片化问题,提高内存利用率。
- 并行处理:ZGC收集器使用多线程并行处理垃圾收集任务,可以充分利用多核处理器的计算能力,提高垃圾收集的效率和整体吞吐量。
- 对系统配置和参数调整要求较高:ZGC收集器的性能表现需要合理的系统配置和参数调整,例如设置合适的堆大小和新生代大小等,否则可能会导致垃圾收集性能下降或出现其他问题。
- 实现复杂度较高:ZGC收集器使用读屏障和染色指针等技术,相对于其他垃圾收集器来说实现复杂度较高,可能会增加开发和维护成本。
- 内存占用较大:ZGC收集器需要使用额外的内存来存储染色指针等信息,相对于其他垃圾收集器来说内存占用较大。
- JVM参数配置:通过设置JVM参数“-XX: UseZGC”来启用ZGC收集器。
Shenandoah收集器【后续单独篇幅介绍】:Shenandoah收集器的优点是低延迟、并行处理、内存压缩等;缺点是对系统配置和参数调整要求较高、实现复杂度较高、对CPU资源敏感等。在选择使用Shenandoah收集器时,需要根据应用程序的特点和需求进行评估和调整。
- 适应场景:适用于需要高吞吐量和低延迟的应用程序,例如Java EE应用程序服务器等。
- 区域:新生代和老年代。
- 优点:
- 低延迟:Shenandoah收集器的目标是实现低延迟的垃圾收集,通过使用CAS操作和高效的内存管理技术,可以快速完成垃圾收集工作,减少应用程序的停顿时间。
- 并行处理:Shenandoah收集器使用多线程并行处理垃圾收集任务,可以充分利用多核处理器的计算能力,提高垃圾收集的效率和整体吞吐量。
- 内存压缩:Shenandoah收集器可以在垃圾收集过程中实现内存压缩,减少内存碎片化问题,提高内存利用率。
- 对系统配置和参数调整要求较高:Shenandoah收集器的性能表现需要合理的系统配置和参数调整,例如设置合适的堆大小和新生代大小等,否则可能会导致垃圾收集性能下降或出现其他问题。
- 实现复杂度较高:Shenandoah收集器使用CAS操作和高效的内存管理技术,相对于其他垃圾收集器来说实现复杂度较高,可能会增加开发和维护成本。
- 对CPU资源敏感:Shenandoah收集器在并发执行时需要占用一部分CPU资源,因此对CPU资源敏感,可能会影响应用程序的性能。
- JVM参数配置:通过设置JVM参数“-XX: UseShenandoahGC”来启用Shenandoah收集器。