JVM-GC基础


基本的垃圾回收算法

引用计数(Reference Counting)

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

标记-清除(Mark-Sweep)

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

标记-清除

复制(Copying)

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

复制

标记-整理(Mark-Compact)

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

标记-整理

比较

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

JVM分代结构

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

分代结构

分代依据

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

JVM GC过程

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打开

串行

并行

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

并行

并发

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

并发

G1

待补

比较

-SerialThroughputCMSG1
参数-XX:+UseSerialGC-XX:+UseParallelGC-XX:+UseConcMarkSweepGC-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算法不了解,了解后再修正)

JVM GC Root

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

GC Root

  1. 线程的栈帧中引用的对象
  2. 方法区中的类静态属性引用的对象
  3. 方法区中的常量引用的对象
  4. 本地方法栈中JNI引用的对象
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值