JVM 垃圾收集器

1.垃圾收集器

1.1 Stop-The-World

所谓的Stop the World机制,简称STW,即在执行垃圾收集算法时,Java应用程序的其他所有除了垃圾收集收集器线程之外的线程都被挂起。

此时,系统只能允许GC线程进行运行,其他线程则会全部暂停,等待GC线程执行完毕后才能再次运行。

这些工作都是由虚拟机在后台自动发起和自动完成的,是在用户不可见的情况下把用户正常工作的线程全部停下来,这对于很多的应用程序,尤其是那些对于实时性要求很高的程序来说是难以接受的。

所以在服务器项目中,应该设计能够去减少Stop-The-World  问题。

注意:市面上所有的垃圾收集器都有Stop-The-World问题,开发中尽量不要调用 System.gc();有可能会导致Stop-The-World问题。

1)停止所有的java执行线程(“stop the world”)

可达性分析必须在一致性的快照中进行,一致性指的是不可以出现分析过程中对象引用关系还在不断变化的情况。这点是导致GC进行时必须停顿所有java线程的一个原因。

2)准确式GC:当系统停下来时,不需要一个不漏的检查完所有执行上下文和全局的引用位置。HotSpot的实现是:在特定位置上(即安全点),虚拟机通过OopMap数据结构在类加载时,将对象内什么偏移量上是什么类型的数据计算出来,并存储到其中,来达到这个目的。在OopMap的协助下,HotSpot可以快速且准确的完成GCRoots的枚举。

总结:Stop-The-World:当我们垃圾收集器在回收垃圾的时候,会暂停我们的所以用户线程,导致当前的用户线程短暂卡。

在生产环境中调优jvm: 不要频繁触发垃圾回收或者是降低用户线程阻塞的时间。

所有的收集器:在回收垃圾的时候都会暂停我们用户的线程。

怎样避免触发垃圾收集器频繁回收?

1.堆内存空间设置大一点

2.堆内存初始化 与最大值一定保持一致。

3.生产环境中不要去调用System.gc();

1.2 Jdk垃圾收集器迭代版本

jdk1.7 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)

jdk1.8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)

jdk1.9 默认垃圾收集器G1

-XX:+PrintCommandLineFlags  命令行查看默认设置收集器类型

-XX:+PrintGCDetails      通过打印的GC日志的新生代、老年代名称判断

jinfo -flag   UseParallelGC 37492 | jinfo -flag   UseParallelOldGC 37492 |jinfo -flag   UseG1GC 37492   相关垃圾回收器参数 进程ID

-XX:+PrintCommandLineFlags  查看命令行相关参数

java -XX:+PrintCommandLineFlags -version

1.3 Jdk7种核心的收集器

1.串行回收器:Serial、Serial old  (采用单线程回收垃圾 适合于堆内存空间比较小 个人小项目)

2.并行回收器:ParNew、Parallel Scavenge、Parallel old 多核多线程、堆内存空间比较大  

3.并发回收器:CMS、G1(分区算法) 减少GC暂停用户线程时间尽可能最短。

 不同颜色代表不同的垃圾收集器。

1.4 垃圾收集器 与 垃圾收集算法区别

垃圾收集器:串行、并行收集、CMS、G1 Java11 ZGC收集器,能够降低对我们用户线程暂停的时间或者用户线程和 GC 线程同时运行

垃圾收集算法:回收算法:标记清除、标记整理、标记复制、分代算法。

2.串行垃圾回收器(Serial)

SerialOldGC与SerialGC

最古老,最稳定、效率高、可能会产生较长的停顿

–新生代、老年代使用串行回收

–新生代复制算法

–老年代标记-压缩

-XX:+UseSerialGC

单线程在收集垃圾的时候,必须暂停当前所有的工作线程,直到清理垃圾完垃圾,工作线程才可以继续执行,Stop The World。

 -XX:+PrintCommandLineFlags  -XX:+UseSerialGC

依然是HotSpot在Client模式下默认的新生代收集器;

对于限定单个CPU的环境来说,Serial收集器没有线程交互(切换)开销,可以获得最高的单线程收集效率;

在用户的桌面应用场景中,可用内存一般不大(几十M至一两百M),可以在较短时间内完成垃圾收集(几十MS至一百多MS),只要不频繁发生,这是可以接受的。

3.并行垃圾回收器(Parallel)

ParNew并行回收器

ParNew垃圾收集器是Serial收集器的多线程版本,主要用于新生代垃圾收集器

1、特点

除了多线程外,其余的行为、特点和Serial收集器一样;

如Serial收集器可用控制参数、收集算法、Stop The World、内存分配规则、回收策略等;

2.应用场景:

在Server模式下,ParNew收集器是一个非常重要的收集器,因为除Serial外,目前只有它能与CMS收集器配合工作;

但在单个CPU环境中,不会比Serail收集器有更好的效果,因为存在线程交互开销。

参数配置:

 "-XX:+UseConcMarkSweepGC":指定使用CMS后,会默认使用ParNew作为新生代收集器;

 "-XX:+UseParNewGC":强制指定使用ParNew;    

"-XX:ParallelGCThreads":指定垃圾收集的线程数量,ParNew默认开启的收集线程与CPU的数量相同;

-XX:+PrintCommandLineFlags  -XX:+UseParNewGC

1.串行垃圾收集和并行垃圾收集的区别

串行:就是GC单线程回收垃圾 只会采用一个线程(一个cpu)清理堆内存垃圾。

应用场景:

堆内存比较小(几十兆空间左右)的项目或者是桌面应用程序(javaswing)。

java后端项目占用对空间非常大,因为定义了很多的Spring bean对象。

优点:简单高效(与其他收集器的单线程相比),清理非常干净;

缺点:只有一个GC线程清理内存空间,如果堆内存空间非常大的情况下,可能会非常消耗时间,从而会引发swt时间非常长,导致用户线程等待的时间过长。

并行:就是GC多线程方式回收垃圾,采用多个线程(多个cpu)清理堆内存垃圾。

优点:如果堆空间比较大的时候,采用并行收集器可以提高清理堆内存空间的效率。

缺点:开启多个线程同时清理垃圾,可能会消耗CPU。

注意:服务器的处理器一定要是多核的才能够真正的发挥并行效果。

2.并发垃圾收集和并行垃圾收集的区别

并行(Parallel):

指有多个GC线程同时清理堆内存空间,效率比较高。但此时用户线程仍然处于等待状态;   如ParNew、Parallel Scavenge、Parallel Old;

并发(Concurrent):

指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行);用户程序在继续运行,而垃圾收集程序线程运行于另一个CPU上;如CMS、G1(也有并行);

应用场景:堆内存空间比较大,多个gc线程同时去回收垃圾。

不管是串行还是并行回收都会暂停我们的用户的线程,导致用户的线程短暂的卡顿。

 

原文地址:https://www.cnblogs.com/ming-blogs/p/14494601.html