阅读 690

JVM核心知识-GC机制

Java开发相对于C语言最方便的点,就是代码上不需要主动去管理内存的回收,而由JVM负责分配回收。

GC算法

标记清除算法(Mark-Sweep)

标记出所有需要回收的内存对象,当垃圾回收时只清除标记的对象。缺点:回收的空间不连续,可能导致创建对象虽然未使用空间足够,但连续的空间不足无法创建。

标记复制算法(Mark-Copy)

将空间分为A、B两块。对象在A块创建。当A空间满了,垃圾回收,将A中存活的对象复制到B中,清空A空间。之后在B空间中尽享相同的逻辑。优点:合理规划了空间的连续性,每次回收都是整块内存的回收。缺点:将内存分为了两块,每次只能用一半的内存。

标记整理算法(Mark-Compact)

结合标记清除和标记复制的缺点,为了优化算法。提出了标记整理算法。对象的回收依然使用标记方式,在垃圾回收时,把存活的对象整理到内存一端,将其他内存回收。这样做的的好处时腾出了一整块连续的空间。优点:创造了连续的空间环境缺点:效率有所损失

分代收集算法(Generational Collection)

根据对象的存活特性,将内存分为几个区域,分别对不同区域进行不同的回收算法。目前大部分的JVM都采用分代收集算法。常见的将堆分为年轻代、老年代,以及堆外的永久代。老年代存放的对象一般回收较少,年轻代回收的对象相对更多。一般对象多数是”朝生夕死“,即创建完很快就会被回收。针对这些区域的对象特性采用不同的收集算法,提高内存利用率。例如:以hotspot为例,内存划分为年轻代、老年代、永久代,年轻代又分为Eden、From Survivor、To Survivorimage.png

年轻代采用标记复制算法,对象的创建在Eden区进行,当Eden区进行垃圾回收时,将Eden存活对象复制到From Survivor区,清空Eden。当From Survivor也满了,将Eden区和From Survivor存活的对象复制到To Survivor区,清空Eden和From Survivor。老年代采用标记整理算法,将存活的对象整理到一端,腾出连续的空间。

GC收集器

Serial/Serial Old收集器

Serial收集器是单线程收集器,是分代收集器。年轻代使用单线程复制收集算法(Serial收集器),老年代使用单线程标记整理算法(Serial Old收集器)。进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束(Stop TheWorld)。参数:-XX:UseSerialGC特点:单线程没有多线程切换的开销。

ParNew 收集器

相当于Serial收集器的多线程版本。年轻代使用并行复制收集算法(ParNew 收集器),老年代使用单线程标记整理算法(Serial Old收集器)。参数:-XX:+UseConcMarkSweepGC":指定使用CMS后,会默认使用ParNew作为新生代收集器;-XX:+UseParNewGC":强制指定使用ParNew;-XX:ParallelGCThreads":指定垃圾收集的线程数量,ParNew默认开启的收集线程与CPU的数量相同;特点:ParNew收集器在单CPU环境中不比Serial效果好,甚至可能更差,两个CPU也不一定跑的过,但随着CPU数量的增加,性能会逐步增加。

Parallel Scavenge/Parallel Old收集器

JDK8默认收集器。同样是并发收集器,Parallel Scavenge收集器的关注点与其他收集器不同, Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput)(吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间))Parallel Old是Parallel老年代收集器,使用标记整理算法。jdk1.6之前老年代只能使用Serial Old收集器。参数:

  • -XX:MaxGCPauseMillis:控制最大垃圾收集停顿时间
  • -XX:GCTimeRatio:设置吞吐量大小
  • -XX:+UseAdaptiveSizePolicy:GC自适应的调节策略(GC Ergonomics),当这个参数打开之后,无需手动指定年轻代空间大小,也不需要设置Survivor对象年龄,虚拟机会根据运行情况,动态调整这些参数值。
    特点:能够控制GC的吞吐量,自适应调整。

CMS 收集器(ConcurrentMarkSweep)

CMS是老年代垃圾收集器,在收集过程中可以与用户线程并发操作。它可以与Serial收集器和Parallel New收集器搭配使用。CMS牺牲了系统的吞吐量来追求收集速度,适合追求垃圾收集速度的服务器上。参数:-XX:+UseConcMarkSweepGC来开启CMS。特点:并发收集、降低停顿时间

CMS收集器GC的过程

  • 初始标记:标记GCRoot关联对象。会触发Stop The World
  • 并发标记:进行GC Roots Tracing的过程。
  • 重新标记:处理因为初始标记过程中用户线程创建对象而没有处理的对象标记。
  • 并发清除:标记清除算法,清除垃圾。吃阶段可能产生浮动垃圾。

G1收集器

G1收集器的设计将整个堆内存分为多个大小相等的块(Region)。任然沿用年轻代、老年代的概念,但相对之前的空间连续,换成了多个Region的集合。G1跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region(这也就是Garbage-First名称的来由)。G1收集器GC的过程

  • 初始标记(Initial Marking) 仅仅只是标记一下GC Roots 能直接关联到的对象,并且修改TAMS(Nest Top Mark Start)的值,让下一阶段用户程序并发运行时,能在正确可用的Region中创建对象,此阶段需要停顿线程,但耗时很短。
  • 并发标记(Concurrent Marking) 从GC Root 开始对堆中对象进行可达性分析,找到存活对象,此阶段耗时较长,但可与用户程序并发执行。
  • 最终标记(Final Marking) 为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程的Remembered Set Logs里面,最终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set中,这阶段需要停顿线程,但是可并行执行。
  • 筛选回收(Live Data Counting and Evacuation) 首先对各个Region中的回收价值和成本进行排序,根据用户所期望的GC 停顿时间来制定回收计划。

ZGC

JDK11提供的新GC收集器,暂时没了解。

本文由博客一文多发平台 OpenWrite 发布!

文章分类
后端
文章标签