前言
垃圾收集器需要先判断对象是否存在,其次才能进行垃圾收集。
不用的垃圾收集器(GC)它的作用也不一样,需要区分。
对象存在判断
引用计数法
原理:给对象添加一个引用计数器,有引用了+1;引用失效了-1;为0了不再使用。
优点:简单、高效
缺点:无法解决对象之间循环引用问题
现状:没有GC在使用这个。
可达性分析算法
原理:通过“GC Root”对象作为起始点,从这些节点开始向下搜索,走过的路径是引用链。当对象无法到达GC Roots链时,即不可达。
现状:大部分虚拟机都采用这个方式。
GC Roots对象:
1)虚拟机栈中引用的对象;方法区中类静态属性引用的对象;方法区中常量引用的对象;本地方法栈中JNI引用的对象。
判断方式:对象至少经过2次标记过程,第一次当没有对象引用时,对象执行一遍覆盖的finalize()方法;第二次会标记进行执行。
垃圾收集算法
标记-清除算法
原理:标记所有需要回收的对象,标记完成后,统一回收所有标记对象。
缺点:标记、清除效率不高;会产生大量碎片。
复制算法
原理:内存分为2部分,每次只使用一部分;当快用完时,复制存活对象到另一个内存中。
缺点:内存浪费一半。
标记-整理算法
原理:标记所有需要回收的对象,标记完成后,统一整理对象到一端。然后直接清理剩余对象。
分代收集介绍
根据对象存活时间的不同,将对象分为了新生代和老年代,在新生代里面再次分为Eden、Survivor0、Survivor1空间。
参数:
- -XX:newSize和-XX:MaxNewSize
设置新生代的大小。不固定,建议真个堆的1/3或者1/4
- -XX:SurivorRatio
设置Eden和其中一个Survivor的比值,默认8:1
- -Xms
JVM的初始内存大小
- -Xmx
JVM的最大可用内存大小
- -Xmn
JVM的新生代大小
- -Xss
线程的堆栈大小
GC分类
- Minor GC 会清理年轻代的内存。(正常情况大部分年轻代对象朝生夕灭,基本都不存在伊甸区拷贝更不说去老年代了)
- Major GC 是清理老年代。
- Full GC 是清理整个堆空间—包括年轻代和老年代
算法:
新生代主要用复制算法;
老年代主要用标记-清除算法或者标记-整理算法
垃圾收集器
新生代收集器:Serial、ParNew、Parallel Scavenge
老年代收集器:CMS、Serial Old(MSC)、Parallel Old
通用收集器:G1
名称解释:
Stop The World:暂定所有的线程,进行垃圾回收。
Serial
ParNew
GC算法:复制算法
特点:serial的多线程版本,采取STW方式
Parallel Scavenge
GC算法:复制算法
特点:主要是缩短用户线程的停顿时间,提高吞吐量。采取STW方式
Serial Old
GC算法:标记-整理算法
特点:serial的老年代收集器,单线程。采取STW方式
Parallel Old
GC算法:标记整理算法
特点:parallel scavenge的老年代收集器,多线程。采取STW方式
CMS
GC算法:标记清除算法
特点:最短回收停顿时间。
处理过程:
- 初始标记:标记GC Roots可以直接关联到的对象,STW
- 并发标记:进行GC Roots Tracing的过程。(耗时略长)
- 重新标记:修正并发标记变动的记录,STW
- 并发清除:并发清除。(耗时略长)
耗时长的都处于并发状态了,可以与用户线程共存。
缺点:
- 对CPU敏感,需要CPU资源。
- 无法处理浮动垃圾。(标记清除后,产生的垃圾)
- 采取标记清除算法,会产生大量的空间碎片。容易触发Full GC
参数设置:
-XX:+UseConcMarkSweepGC 显式使用此GC
G1
特点:多核并行并发运行;空间整合,整体基于标记-整理算法,局部基于复制算法;可预测停顿。
处理过程
- 初始标记:仅仅标记下GC Roots可以关联的对象。
- 并发标记:进行可达性分析,进行GC Roots Tracing过程。(耗时长)
- 最终标记:修正表发标记变动的记录。
- 筛选回收:对各个Region的回收价值和成本进行排序,制定回收计划。
参数设置
-XX:+UseG1GC 使用G1收集器
-XX:MaxGCPauseMillis=50 控制预期的最高GC时长,默认值为200ms,如果线上业务特性对于GC停顿非常敏感,可以适当设置低一些。但是 这个值如果设置过小,可能会带来比较高的cpu消耗
G1实现
G1主要取代CMS垃圾收集器,不会产生很多内存脆片,STW更可控。
而G1的各代存储地址是不连续的,每一代都使用了n个不连续的大小相同的Region,每个Region占有一块连续的虚拟内存地址。
JDK默认收集器
jdk1.7 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
jdk1.8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
jdk1.9 默认垃圾收集器G1
内存分配与回收策略
- 对象优先分配在Eden区域上。没有空间时,发起Minor GC
- 大对象直接分配在老年代。(大对象:需要大量连续内存空间,比如:很长的字符串、byte[]数组)
- 正常情况,每个对象都分配在Eden区,默认0岁。经过一次Minor GC后,芳容Survivor区,变成1岁。每经历一次Minor GC,增加一岁。到15岁时,进入老年代。