java虚拟机4

垃圾回收基础知识

什么是gc

  • minor gc/young gc:对新生代(eden、from、to----1/3)进行垃圾回收
  • major gc(不同地方不一样,需要根据出处的上下文理解,不同地方不一样,有的只是老年代,有的是所有区域)/old gc(更好的说法,只针对老年代,cms):对老年代(tunured----2/3)进行回收
  • full gc(不仅是新生代、老年代、还要加上方法区(1.7之前是永久代,jdk1.8及之后是元空间))

分代回收理论

  • 1.大部分对象都是朝生夕死 — 新生代(98%)
    • 一个方法内创建的对象,只在这个方法里面使用了,当这个方法执行完后,栈就被销毁了,同时引用就不存在了,垃圾回收时根据可达性分析,相关连的对象就会被回收
  • 2.熬过多次垃圾回收的就越难回收 — 老年代
    • 如果对象能够熬过垃圾回收,说明与静态、常量或者其他有关联,所以熬过一次后,熬过第二次第三次的几率就很大,所以必须要与第一种区别开

垃圾回收算法

  • ①复制算法 – 新生代采用的算法
  • ②标记清除算法、③标记整理算法 – 老年代采用的算法

复制算法

  • 算法执行过程

    • 步骤一:把内存空间一分为二,先把一半的空间预留,只在另一半空间里面存放数据,当一半的空间塞满以后,这时就会触发垃圾回收,从gc roots扫描,将存活的copy过去,直接将扫描完的一半整个区域格式化,这比单独一个个删除效率要高,步骤一完成后,两个区的身份交换,预留的变成对象分配区域,对象分配区域变成预留
    • 步骤二:重复步骤一
  • 特点

    • 实现简单、运行高效,只需要进行一次扫描,一次复制,对于新生代而言,存活的对象就很少
    • 没有内存碎片,可以选择,把对象推向内存的一端
    • 利用率只有一半(缺点)
      • 怎么解决?
      • 增加一个eden区,没有必要预留一半,eden区占80%,其余的from和to各占10%,标准的eden:from:to = 8:1:1
eden区的来源
  • appel式回收

    • 1.先把对象分配在eden区,当eden区满了,将存活的对象复制到from区,因为存活的对象很少,是能够复制过去的,然后将eden区格式化清空
    • 2.然后再次创建对象,eden区第二次又满了,将存活的对象复制到to区,同时将from区依然存活的复制到to区
    • 3.按照第1步和第2步的过程反复进行
  • 提高空间利用率和空间分配担保

    • 提高空间利用率

      效率由50提高到90,只浪费了10%的空间,同时推迟了复制时间

    • 空间分配担保

      如果单次回收超过10%会进入老年代

  • 总结

    • 用在新生代

标记清除算法(mark-sweep)

  • 老年代存放的是回收不了的对象,是不适合用复制算法的

  • 根据可达性分析做标记,将可回收的进行标记,然后像删文件一样的把可回收的区域数据删除掉

  • 特点

    • 1.位置不连续,产生内存碎片,大对象分配就很困难

      • 如果要分配一个大对象?

        对象的分配是一个直接指针,不能分成两段,所以需要对象的内存空间是连续的,这就要求有足够的连续内存空间

    • 2.效率略低(两次)

      • 两遍扫描(还要涉及对象的删除)
    • 3.对比复制算法,优点是空间利用率100%

标记整理算法(Mark-Compact)

  • 跟标记清除相似,需要整理内存空间,将存活的对象移动到内存的一端
  • 特点
    • 没有内存碎片
    • 效率偏低,
      • 两遍扫描、引用指针需要调整(对象保存的地址发生了变换)

jvm常用的垃圾回收器–以jdk1.8为例

单线程垃圾回收器

  • Serial–回收新生代

  • SerialOld–回收老年代

  • 新生代和老年代在回收内存时需要暂停所有的用户线程,只起一个垃圾回收线程来回收内存

  • 虚拟机参数

    • useSerialGC(鸡肋),只能回收几十兆到一两百兆的东西
stop the world(停顿时间)
  • 比如复制算法,因为需要变更引用,所以暂停所有的用户线程

  • 缺点

    • 如果超过10s,用户体验极其不好,应该不仅仅是为了提高吞吐量

多线程并行垃圾回收器

  • Parallel Scavange(默认)---- 回收新生代

  • Parallel Old (默认)— 回收老年代

  • jdk1.8默认垃圾回收器是Parallel Scavange和Parallel Old

  • 虚拟机参数

    • -XX:+UseParallelGC

      回收内存是多线程并发执行

    • -XX:MaxGCPauseMillis (没什么鸟用)实际的暂停时间可能还变长了

      • 千万不要以为这个值设置的越小,JVM暂停的时间就会越短,实际有可能吞吐量还变低了

        500ms 原来是每隔100s回收一次

        100ms 现在是每隔10s回收一次

    • UseAdaptiveSizePolicy

      • 吞吐量=运行用户代码时间/(运行用户代码时间+垃圾回收时间)
      • 自适应生成大小,为了完成最大吞吐量
      • 这个参数默认是开启的
  • 适用于几百兆到几个g都适用

  • ParNew(单独针对cms的)

    • 为了减少stop the world的时间

并发垃圾回收器-cms

  • 第一代并发垃圾回收器,具有划时代意义,减少stw的时间

    • cms只回收老年代
    • 同时JVM推出ParNew用来和cms配对,专门回收新生代
  • cms的垃圾回收过程,其中把标记过程一分为三

    • 1.初始标记
      • 找到gc roots,只对那些和它有直接关联的进行标记,同时gc roots很少,所以这个过程很快,时间短,同时暂停所有用户线程,暂停时间短,所以可以暂停
    • 2.并发标记(让中间标记最长的一段时间并发标记,可以减少stop the wordl的时间)
      • 对gc roots直接关联以外的引用链上的元素进行标记,标记的深度很深,数量级很大,所以消耗的时间长并发的和用户线程跑,但是此时如果用户线程新创建了一部分对象,但是并没有被标记上
    • 3.重新标记
      • 重新标记用户线程新产生的垃圾,此时也需要暂停所有用户线程(stw),但是新产生的垃圾少,所以也能很快标记完,消耗的时间短
    • 4.并发清除
      • 用户和gc并发执行,消耗的时间长
      • 最后重置线程,把并发清除线程重置为用户线程
  • cms中的问题

    • cpu敏感,对CPU要求很高,4核以下,用户会有很强的卡顿感

    • 浮动垃圾,最后一步并发清除的过程中又会产生浮动垃圾,只能等到下一次处理了

      • 本来老年代是存储到99.9%才会触发垃圾回收,但是由于浮动垃圾的存在,以前的版本存到68%就触发垃圾回收,jdk1.6提高到92%
      • 使得触发垃圾回收的时间相比parallel提前了
      • 如果超过了能处理的浮动垃圾
        • 此时会用serialOld来取代,本来处理4G垃圾几秒钟就能完成,转跳成SerialOld处理器后就需要几分钟乃至几十分钟处理
    • 内存碎片

      • 大对象分配非常麻烦

      • UseCMSCompactAtFullCollection

        使用SerialOld来整理

    • 虽然减少了stop the world的时间,但带来的额外问题也非常多,浮动垃圾和内存碎片导致垃圾回收器随时可能被一个单线程的serialold代替,所以以前老版本经常重启,每天晚上进行重启,来清除浮动垃圾和内存碎片,所以jdk1.6、1.7、1.8都没有将cms设置为默认垃圾回收期

  • 为什么是标记清除不是标记整理?

    • 因为清除是并发清除,和用户线程并发进行的,可以确保最好并发清除的效率,减少stop the world的时间。

并发垃圾回收器-g1(garbage first)

设计思想
  • 总是跳不出stop the world的问题,所以想着去预测stw、并实现控制
region
  • 将整个堆空间划成一个整体,切割成一个个的region
    • region的大小范围是1m~32m,并且规定是2的次幂大小
    • 每一块根据需要扮演不同的角色,分别扮演Eden、Survivor、Old、Humongous
  • 针对大对象,多了一个Humongous,假设一个region是1M,如果对象大于等于512k,超过region的一半,判定为大对象,会放到Humongous区,如果更大,就会放到连续的多个Humongous区
  • eden、survivor(survivor0就是from区,survivor1就是to区)用复制,老年代使用标记整理,Humongous等同于老年代
筛选回收
  • MaxGcPauseMillis 垃圾回收最大暂定时间,是一个软目标,会尽最大努力去实现,不同于parallel scavenge强制去做处理
    • 因为划分了区域,不会对整个空间进行垃圾回收,会去选垃圾回收效率比较高的区域进行垃圾回收,这也就是g1名字的来源。
可预测停顿—MaxGcPauseMillis
  • 通过筛选回收的方式实现可预测停顿
垃圾回收步骤
  • 跟cms一样,也是三次标记

  • ①初始标记,gc roots,与cms相同,会stop the world,但是时间很短

  • ②并发标记

    • 解决回收过程中新分配的对象(不是垃圾),在每一个region内划分一块很小的区域------TAMS(top at mark start),放这些指针(指向哪些并发过程中新new出来的存活的对象)
    • 并发标记也会出现漏标问题------SATB(snapshot at the beginning),保存一个快照(内存引用关系)
  • ③最终标记,处理漏标对象,stop the world

  • ④筛选回收,不会回收整个堆,通过标记已经算出来,每块区域有多少垃圾,哪个回报率最高,stop the world时间最短,可以获得最大的收益,同时会用到标记整理

    • 相比于cms,是筛选回收,而cms是简单的并发回收整个老年代
问题
  • 解决了cms里面的痛点
    • 1.大对象
    • 2.内存碎片
  • 但是如果region里的对象与另一个region里的对象有依赖怎么办?
    • 见java虚拟机5

垃圾回收器总结

新生代

  • 回收器回收对象和算法回收器类型
    Serial新生代,复制算法单线程(串行)
    Parallel Scavenge新生代,复制算法并行的多线程回收器
    ParNew新生代,复制算法并行的多线程回收器

老年代

  • 回收器回收对象和算法回收器类型
    Serial Old老年代,标记整理算法单线程(串行)
    Parallel Old老年代,标记整理算法并行的多线程回收器
    CMS老年代,标记清除算法并发的多线程回收器
    G1跨新生代和老年代,标记整理+化整为零并发的多线程回收器

回收器的配对处理

  • 新生代回收器老年代回收器
    SerialSerial Old
    Parallel ScavengeParallel Old
    Parallel ScavengeSerial Old
    PraNewCMS+Serial Old
    PraNewSerial Old
    SerialCMS+Serial Old
    G1G1
  • CMS+Serial Old

    • 如果CMS预留空间不足以放浮动垃圾,使用Serial Old取代
  • jdk1.8,cms实际只能跟ParNew组合了

cms与g1应该怎么选

选择

  • serial—serialold 几十兆到一百兆

  • parallel scavenge—parallel old 几百兆到几个g

  • parnw—cms 几个g到几十个g (对g1起到铺垫作用,jdk1.8与g1有竞争之力,到jdk1.9可能就不如g1了)

  • g1 几十g(30g~50g)到一百多个g(jdk官方文档当堆的空间大于6gb以上或更大,推荐采用g1)

  • cms和g1有重合之处,此时可以参考jdk文档

    • 假设内存空间就是8个g,怎么选?

      jdk官方文档说明:启用垃圾优先(G1)垃圾收集器的使用。它是一种服务器样式的垃圾收集器,适用于具有大量RAM的多处理器计算机。它极有可能满足GC暂停时间目标,同时保持良好的吞吐量。==建议将G1收集器用于需要大堆(大小约为6 GB或更大)==且GC延迟要求有限(稳定且可预测的暂停时间在0.5秒以下)的应用程序

并不是cms一定比g1差?

  • 为了解决漏标问题和新new出来的对象有地方存放,每个region都需要一块区域去解决并发标记的需要,所以花费的内存空间不一定低(漏标、new出来的对象),甚至付出20%的内存空间来存放这部分内容,像cms就比较单纯,只是追求stop the world的时间

总结

  • 1.只有cms是标记清除,其余基本是标记整理
  • 2.避免full gc,从一堆不容易删除的对象中找出少数几个可以删除的,同时又要标记又要整理内存空间,相当耗时间
  • 3.g1因为对区域进行了划分,所以可以对清除空间进行选择
  • 4.G1HeapRegionSize,默认大小会由 g1区会自动设置
  • 5.cms达不到追求停顿时间的目的,cms只能说是尽可能减少。
  • 6.crud 代码去实现, jvm最重要的关注点是思想
  • 7.full gc也会去回收方法区的内容,回收效率非常低,所以后面换成元空间,不再当成对象。
  • 8.三色标记?
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值