前言
垃圾收集器需要先判断对象是否存在,其次才能进行垃圾收集。
不用的垃圾收集器(GC)它的作用也不一样,需要区分。

对象存在判断

引用计数法

原理:给对象添加一个引用计数器,有引用了+1;引用失效了-1;为0了不再使用。
优点:简单、高效
缺点:无法解决对象之间循环引用问题
现状:没有GC在使用这个。

可达性分析算法

原理:通过“GC Root”对象作为起始点,从这些节点开始向下搜索,走过的路径是引用链。当对象无法到达GC Roots链时,即不可达。
现状:大部分虚拟机都采用这个方式。
GC Roots对象:
1)虚拟机栈中引用的对象;方法区中类静态属性引用的对象;方法区中常量引用的对象;本地方法栈中JNI引用的对象。
判断方式:对象至少经过2次标记过程,第一次当没有对象引用时,对象执行一遍覆盖的finalize()方法;第二次会标记进行执行。

垃圾收集算法

标记-清除算法

原理:标记所有需要回收的对象,标记完成后,统一回收所有标记对象。
缺点:标记、清除效率不高;会产生大量碎片。

复制算法

原理:内存分为2部分,每次只使用一部分;当快用完时,复制存活对象到另一个内存中。
缺点:内存浪费一半。

标记-整理算法

原理:标记所有需要回收的对象,标记完成后,统一整理对象到一端。然后直接清理剩余对象。

分代收集介绍

根据对象存活时间的不同,将对象分为了新生代和老年代,在新生代里面再次分为Eden、Survivor0、Survivor1空间。
image.png

参数:

  • -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

GC算法:复制算法
特点:单线程收集器,采取STW方式

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
  • 并发清除:并发清除。(耗时略长)

耗时长的都处于并发状态了,可以与用户线程共存。
缺点:

  1. 对CPU敏感,需要CPU资源。
  2. 无法处理浮动垃圾。(标记清除后,产生的垃圾)
  3. 采取标记清除算法,会产生大量的空间碎片。容易触发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占有一块连续的虚拟内存地址。
image.png

JDK默认收集器

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

内存分配与回收策略

  1. 对象优先分配在Eden区域上。没有空间时,发起Minor GC
  2. 大对象直接分配在老年代。(大对象:需要大量连续内存空间,比如:很长的字符串、byte[]数组)
  3. 正常情况,每个对象都分配在Eden区,默认0岁。经过一次Minor GC后,芳容Survivor区,变成1岁。每经历一次Minor GC,增加一岁。到15岁时,进入老年代。

参考