点击上方「蓝字」关注我们
什么是垃圾?
对于jvm来说垃圾是指运行程序中没有任何指针指向的对象,这个对象就需要被回收。
个人理解:对于个人所需来说,比如你家里拆快递盒子,用过后,你一直不用,要及时清除,要不然很容易把房间堆满,那这样的话后面的放不进去物品了。
为什么需要GC?
对于高级语言来讲,因为不断的创建对象,如果不清理,迟早内存会满。所以需要清理不要的垃圾。这个有点类型你家里有一间储存物品的杂货间,如果一直往里面塞东西而又不梳理,迟早有一天会被塞满的。
什么是垃圾收集器GC?
垃圾收集器全称:Garbage Collection,下文简称GC,其实就是各种垃圾算法的一种实现。目前还没有符合所有场景的收集器出现。
并行和并发的区别?
并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行。而垃圾收集程序运行在另一个CPU上。
有哪些垃圾收集器?
串行垃圾回收器(Serial)
JVM第一个垃圾收集器,JDK 1.3.1之前都是有这个收集器。可以作用新生代和老年代。
算法:新生代-使用复制算法,老年代-使用标记-整理算法。
特点:串行单线程、不支持并发、会导致"stop the world"。
//配置如下使用
-XX:+UseSerialGC
并发垃圾回收器(parNew)
为了解决 serial拉圾收集器引起的停机问题,在serial基础上开发了多线程版本,但是parNew是针对client的版本。
特点:多线程、唯一能够与cms搭配使用的收集器;
算法:年轻代采用复制算法、年老代采用标记-整理
//强制指定使用ParNew;
-XX:+UseParNewGC
//指定垃圾收集的线程数量,ParNew默认开启的收集线程与CPU的数量相同;
-XX:ParallerGCThreads
并行垃圾收集器(Parallel Scavenge)
Parallel Scavenge是后个多线程新生代收集器,使用的算法是复制算法。实现方式是当垃圾达到一个可控制的吞吐量(Throughput)则启动垃圾回收。
特点:作用于新生代、老年代由Serial搭配使用,可以有效的减少停顿时间提升用户体验。
算法:新生代复制算法,老年代标记整理
//注意:启动后不需要手工指定新生代的大小(-Xmn)、Eden和Survivor区的比例(-XX:SurvivorRatio)、晋升老年代对象年龄(-XX:PretenureSizeThreshold)等细节参数了
-XX:+UseAdaptiveSizePolicy
//使用Parallel收集器+老年代串行
-XX:+UseParallelGC
//启用并行压缩
-XX:+UseParallelOldGC。
并发垃圾回收器 CMS(Concurrent Mark Sweep)
cms主要是解决停顿时间的问题而开发的一种,低停顿、并发收集器,基于标记清除,可以作用于新生代和老年代。
CMS收集器的运行过程分为下列4步:
初始标记:标记GC Roots能直接到的对象。速度很快但是仍存在Stop The World问题。
并发标记:进行GC Roots Tracing 的过程,找出存活对象且用户线程可并发执行。
重新标记:为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录。仍然存在Stop The World问题。
并发清除:对标记的对象进行清除回收。
特点:作用于新生代、老年代、代停顿、并发收集;
算法:标记-清除;
缺点:无法处理浮动垃圾、对CPU资源非常敏感、会造成空间碎片化;
//新生代使用并行收集器,老年代使用CMS+串行收集器
-XX:+UseConcMarkSweepGC
//设定CMS的线程数量
-XX:ParallelCMSThreads
G1垃圾回收器(G1)
特点:作用于新生代、老年代、空间连续减少垃圾碎片、缩小回收范围减少全局停顿、并发收集;
算法:分区算法;
缺点:无法处理浮动垃圾、对CPU资源非常敏感、会造成空间碎片化;
G1收集器的阶段分以下几个步骤:
1、初始标记(它标记了从GC Root开始直接可达的对象);
2、并发标记(从GC Roots开始对堆中对象进行可达性分析,找出存活对象);
3、最终标记(标记那些在并发标记阶段发生变化的对象,将被回收);
4、筛选回收(首先对各个Regin的回收价值和成本进行排序,根据用户所期待的GC停顿时间指定回收计划,回收一部分Region)。
//启用G1垃圾回收器 -XX:+UseG1GC
查看jdk所用内存垃圾回收器
通过命令:
java -XX:+PrintCommandLineFlags -version
堆大小的计算?
初始堆大小 | memory / DefaultInitialRAMFraction | memory / 64 |
---|---|---|
最大堆大小 | MIN(memory / DefaultMaxRAMFraction, 1GB) | MIN(memory / 4, 1GB) |
在JDK8默认的配置下使用 新生代,老年代的垃圾回收策略,新生代区域使用标记-复制算法,老年代区域使用标记-整理算法。
收集器 | 类型 | 区域 | 算法 | 特性 | 场景 |
---|---|---|---|---|---|
Seial | 串行 | 新生代 | 复制算法 | 响应速度优化 | 单CPU环境下的Client模式 |
Serial Old | 串行 | 老年代 | 标记-整理 | 响应速度优化 | 单CPU环境下的Client模式、CMS的后备预案 |
ParNew | 并行 | 新生代 | 复制算法 | 响应速度优先 | 多CPU环境时在Server模式下与CMS配合 |
Parallel Scavenge | 并行 | 新生代 | 复制算法 | 吞吐量优先 | 后台运算不需要太多交互任务 |
Parallel Scavenge old | 并行 | 老年代 | 标记整理 | 吞吐量优先 | 后台运算不需要太多交互任务 |
CMS | 并发 | 老年代 | 标记清除 | 响应速度优先 | 主要用于互联网站或B/S系统服务端上的Java应用 |
G1 | 并发 | 新生代/老年代 | 标记-整理+复制算法 | 响应速度优先 | 面向服务端应用,将来替换CMS |
新生代收集器:Serial、ParNew、Parallel Scavenge
老年代收集器:CMS、Serial Old、Parallel Old
整堆收集器:G1
GC的参数整理
-XX:+UseSerialGC:在新生代和老年代使用串行收集器
-XX:SurvivorRatio:设置eden区大小和survivior区大小的比例
-XX:NewRatio:新生代和老年代的比
-XX:+UseParNewGC:在新生代使用并行收集器
-XX:+UseParallelGC :在新生代使用并行回收收集器
-XX:+UseParallelOldGC:老年代使用并行回收收集器
-XX:ParallelGCThreads:设置用于垃圾回收的线程数
-XX:+UseConcMarkSweepGC:新生代使用并行收集器,老年代使用CMS+串行收集器
-XX:ParallelCMSThreads:设定CMS的线程数量
-XX:CMSInitiatingOccupancyFraction:设置CMS收集器在老年代空间被使用多少后触发
-XX:+UseCMSCompactAtFullCollection:设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片的整理
-XX:CMSFullGCsBeforeCompaction:设定进行多少次CMS垃圾回收后,进行一次内存压缩
-XX:+CMSClassUnloadingEnabled:允许对类元数据进行回收
-XX:CMSInitiatingPermOccupancyFraction:当永久区占用率达到这一百分比时,启动CMS回收
-XX:UseCMSInitiatingOccupancyOnly:表示只在到达阀值的时候,才进行CMS回收
最后
jvm的垃圾收集器主要作用于新生代和老年代,不同的版本和不同的收集群可以针对不同的场景需要,并不意味的最新就是最好的,有时候在架构和业务层面考虑,可以根据需要进行配置,当然本文仅介绍垃圾收集器,具体里面的算法,由下文再继续深入。
参考文章:
https://blog.51cto.com/u_6877873/1743686
https://www.oracle.com/java/technologies/javase/gc-tuning-6.html
https://www.cnblogs.com/chenpt/p/9803298.html
https://zhuanlan.zhihu.com/p/58896619