JVM-GC基础

基本的垃圾回收算法

引用计数(Reference Counting)

增加一个引用,引用计数加1,去掉一个引用,引用计数减1,然后回收那些引用计数为0的对象
问题:无法处理循环引用问题(例如A、B两个对象互相引用,但没有其他对象引用它们,这时它们也无法被回收)

标记-清除(Mark-Sweep)

从引用根节点开始标记所有被引用的对象,然后遍历整个堆,清除未标记的对象
问题:产生碎片

JVM-GC基础_第1张图片
标记-清除

复制(Copying)

首先将内存空间分为对等的两半,每次只使用其中一半
每次回收时,遍历当前使用区域,将正在使用的对象复制到另外一个区域
好处:一次遍历即可,且不会产生碎片
问题:需要两倍空间

JVM-GC基础_第2张图片
复制

标记-整理(Mark-Compact)

从引用根节点开始标记所有被引用的对象,然后遍历整个堆,清除未标记的对象,并把存活对象压缩到一块
好处:避免了空间的浪费,且不会产生碎片

JVM-GC基础_第3张图片
标记-整理

比较

空间:复制>标记-清除=标记-整理(复制需要两倍空间)
时间:复制<标记-清除<标记-整理(复制最快,一次遍历即可;标记-整理比标记-清除要慢,因为除了清除之外,还要移动数据)

JVM分代结构

JVM内存采用分代结构,分别为Young、Tenured、Permanent,其中Young又细分为Eden和两个大小相同的Survivor区:From和To。

JVM-GC基础_第4张图片
分代结构

分代依据

  1. 绝大部分的对象都是临时对象
  2. 不同对象的生命周期不同,采用不同的算法,可以提高不同的效率

JVM GC过程

JVM-GC基础_第5张图片
GC过程
  1. 新建的对象都在Eden中创建
    大的对象直接在Old中创建:1)超过-XX:PretenureSizeThreshold设置,2)大于整个Eden
    如果Eden满了,则触发MinorGC

  2. MinorGC
    暂停程序
    将Eden和From中存活的对象复制到To,同时各个对象的年龄值加1(MinorGC后,Eden和From都是空的)
    如果To满了,则将对象移到Old,如果此时Old满了,则发送Promotion Failed错误,触发FullGC
    如果对象的年龄超过-XX:MaxTenuringThreshold,也移到Old(这里有一个动态对象年龄的概念:不是每次都要求对象的年龄一定要超过-XX:MaxTenuringThreshold才晋升到Old,如果Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入Old)

  3. FullGC
    如果Old满了,触发FullGC
    如果Perm满了,触发FullGC
    暂停程序(CMS算法的整个过程可以并行执行,只需短暂暂停程序2次)
    回收Old,如果回收后还是满了,则抛出OutOfMemoryError: Java heap space
    默认情况下,JVM是不回收Perm区的,要回收需要使用CMS算法,并设置-XX:+CMSClassUnloadingEnabled, -XX:+CMSPermGenSweepingEnabled,如果回收后还是满了,则抛出OutOfMemoryError: PermGen space

JVM GC算法

串行

效率高,但无法利用多核,一般在小程序使用,使用-XX:+UseSerialGC打开

JVM-GC基础_第6张图片
串行

并行

对Young并行收集,使用-XX:+UseParallelGC打开
JDK6.0后可对Old进行并行收集,使用-XX:+UseParallelOldGC打开

JVM-GC基础_第7张图片
并行

并发

保证大部分回收工作并发执行(应用不暂停),适合响应要求高的应用,使用-XX:+UseConcMarkSweepGC打开

JVM-GC基础_第8张图片
并发

G1

待补

比较

Serial Throughput CMS G1
参数 -XX:+UseSerialGC -XX:+UseParallelGC -XX:+UseConcMarkSweepGC, -XX:+UseParNewGC -XX:+UseG1GC
Young(都是暂停整个应用) 单线程 多线程 多线程 多线程
Old 单线程,暂停应用,压缩 多线程,暂停应用,压缩 单或多线程,部分暂停,不压缩 多线程,部分暂停,压缩
增加CPU使用率,产生碎片,如果没有足够的CPU或者碎片太多,则退化成serial gc 增加CPU使用率,适合Heap大于4G的情况,Old区也是从一个region拷贝到另外一个region

G1和CMS的机制是差不多的,只是G1把old分区了,这样更有利于多线程的扫描
CMS每次清除后,都不会压缩整理的,会产生碎片,而G1每次都像young那样,进行数据移动,也就解决了碎片的问题

选择

  1. 如果heap少于100MB,选择Serial
  2. 对于TPS,如果CPU够用,则选择并发GC,如果CPU使用率较高,则选择Throughput
  3. 对于平均响应时间,通常Throughput比并发GC要好
  4. 对于90%或99%的响应时间,并发GC比Throughput要好
  5. 如果选用并发GC,heap少于4G选择CMS,大于4G选择G1(这个保留,对G1算法不了解,了解后再修正)

GC Root

垃圾回收从Root开始,栈是程序真正执行的地方,所以从栈开始找,而栈又属于线程独有,所以从所有的线程的栈开始找

JVM-GC基础_第9张图片
GC Root
  1. 线程的栈帧中引用的对象
  2. 方法区中的类静态属性引用的对象
  3. 方法区中的常量引用的对象
  4. 本地方法栈中JNI引用的对象

你可能感兴趣的:(JVM-GC基础)