JVM垃圾收集和内存分配

对象存活判断

算法

  • 引用计数算法:一个地方引用对象,对象的引用计数器加1。这会产生循环引用问题
  • 可达性分析算法:从 GC Roots 开始搜索,所走过的路径称为引用链,如果对象不可达,则对象可回收。 GC Roots 包括以下
    • 虚拟机栈中的引用对象。
    • 本地方法栈中native方法引用对象。
    • 方法区的静态属性引用对象。
    • 方法区的常量引用对象。
      180526.gcroots.png

引用强度

  • 当内存空间还足够时,保留;当内存空间GC后任然紧张,抛弃。从而规定不同的引用级别。
  • 强引用(Strong Reference):普遍存在,如 Object obj = new Object()。
  • 软引用(Soft Reference):生存到下一次内存溢出之前,如果回收之后内存不够,才抛内存溢出异常。
  • 弱引用(Weak Reference):生存到下一次GC之前。
  • 虚引用(Phantom Reference):不影响生存时间,也无法取得对象实例,唯一作用是使得对象被回收是收到系统通知。

回收判断

  • 回收需要经过两次标记过程。
  • 第一次标记后,对象不可达时,需要进过筛选,是否需要执行finalize()方法:
    • 没有覆盖finalize(),或者已经执行过一次finalize(),则无需执行,放入即将回收的集合。
    • 需要执行finalize()方法,放入F-Queue队列,由Finalizer线程执行。对象可在方法中重新建立引用关联,避免被回收。
  • 第二次标记,对F-Queue进行。
  • 对象的finalize方法只能调用一次,由于运行代价高、不确定性大、无法保证对象的调用顺序,不建议使用。

回收方法区

  • 废弃常量:没有其他地方引用这个字面量。
  • 无用的类:
    • 该类所有实例被回收。
    • 加载该类的加载器被回收。
    • 该类对应的Class对象没有被引用,无法通过反射访问该类的方法。
  • 频繁自定义加载器和动态生成类的场景需要卸载类。

垃圾收集算法

标记-清除

  • 最基础的算法。标记需要回收的对象,之后统一回收。
    180526.marksweep.png
  • 标记和清除效率不高,清除之后内存不连续。

复制

  • 针对效率。将存活的对象复制到另一块内存上。
    180526.copy.png
  • 实现简单,运行高效,但是有效内存缩小,空间换时间。
  • 将新生代划分为一块较大的Eden区和两块较小的Survivor区,HotSpot默认比例为8:1。每次使用一个Eden和一个Survivor,回收时将Eden和Survivor区中存活对象复制到另一个Survivor区中。如果另一个Survivor区没有足够的内存空间,这些对象将直接进入老年代,这是老年代的分配担保

标记-整理

  • 针对内存空间。先标记对象,然后将存活对象向一端移动,之后清理边界以外的内存。
    180526.markcompact.png

分代收集

  • 新生代:每次GC只有少量对象存活,使用复制算法。
  • 老年代:每次GC对象存活率高,没有额外空间作为保证,使用标记-清除标记整理算法。

HotSpot实现判断存活和垃圾回收

枚举根节点

  • 枚举根节点是必须停顿所有的java执行线程,称为 Stop The World。
  • 为了无需检查所有执行上下文和全局引用位置,提高效率,使用 OopMap 得知哪些地方存在对象的引用。

安全点

  • 为每一条指令生成OopMap开销大。
  • 程序在特定的位置记录OopMap,然后停顿开始GC,这个位置称为安全点。
  • 停顿方式分为2种:
    • 抢先式中断:中断所有线程,恢复不在安全点的线程,运行至安全点后中断。几乎不用。
    • 主动式中断:线程轮询中断标志,如果为真,中断线程。

安全区域

  • 线程没有分配CPU时间时,则无法在安全点挂起。
  • 在某一区域内,引用关系不会发生变化,这一区域任意地方GC都是安全的,称为安全区域。
  • 线程在安全区内进行GC,无需关心,离开安全区时,需要等待GC完成。

垃圾收集器

  • 垃圾收集器是内存回收的具体实现。共7种。
    180526.gcm.png

Serial收集器

  • 单线程收集器,需要将用户正常工作的线程全部停止。
    180526.serial.png
  • 简单高效,没有线程交互的开销。客户端新生代内存不大,停顿可接受。
  • 虚拟机Client模式下默认的新生代收集器。

ParNew收集器

  • Serial收集器的多线程版本。
    180526.parnew.png
  • 由于线程交互的开销,单核性能不如Serial,多核性能较高。
  • 虚拟机Server模式下默认的新生代收集器,匹配CMS收集器。

并行(Parallel):多条垃圾收集线程并行工作,用户线程等待。

并发(Concurrent):垃圾收集线程和用户线程交替执行,也可能在不同CPU上同时执行。

Parallel Scavenge收集器

  • 使用停止复制算法的多线程新生代收集器,关注可控制的吞吐量(运行代码时间/(运行代码时间+垃圾收集时间))。
  • 可直接控制最大垃圾收集停顿时间和吞吐量大小,也可自适应调节。

Serial Old收集器

  • Serial收集器的老年代版本,使用标记-整理算法。
    180526.serial.png
  • 用途:
    • jdk1.5之前和Parallel Scavenge收集器搭配。
    • CMS收集器的后备,当并发收集遇到失败时使用。

Parallel Old收集器

  • Parallel Scavenge收集器的老年代版本,注重吞吐量。
    180526.parallelold.png

CMS收集器

  • CMS(Concurrent Mark Sweep)收集器,获取最短的停顿时间,提高响应速度。
  • 基于标记-清除,分为4个部分。其中并发标记和并发清理耗时较长,但是与用户进程并发,效率高。
    • 初始标记: GC Roots 能直接关联到的对象。
    • 并发标记:并发搜索可达对象。
    • 重新标记:修正并发标记期间产生变动的对象的标记。
    • 并发清理。
      180526.cms.png
  • 缺点:
    • CPU资源敏感。并发过程占用CPU,使得用户线程变慢。
    • 无法收集浮动垃圾。并发清理时会有新的垃圾产生,需要在老年代中预留一定比例空间存储清理时新产生的老年代对象。如果预留的内存无法满足要求,会 Concurrent Mode Failure,从而启动Serial Old收集器。
    • 空间碎片。标记-清除内存空间不连续,可以在CMS收集器效果不佳时进行Full GC,整理内存碎片。

G1收集器

  • 面向服务端应用的垃圾收集器。特点如下:
    • 并行和并发
    • 分代收集:分为新生代和老年代。
    • 空间整合:在两个Region之间使用复制算法,从整体上看,使用整理算法。
    • 可预测停顿:规定垃圾收集的时间上限。
  • 整个堆分为多个大小相等的Region,新生代和老年代不物理隔离,都是多个Region的集合
  • G1跟踪各个Region回收的价值和成本大小,可以按照优先级回收空间,并且限制回收时间。
  • 使用 Remembered Set 管理Region之间的对象引用,从而避免每次回收进行全堆扫描。每一个 Region具有一个Remembered Set,每当对reference类型的数据进行写操作时,检查该对象是否处于不同的Region,如果不同,将相关引用信息记入被引用的Region对应的Remembered Set。进行内存回收时,GC Roots中加入Remembered Set从而避免全堆扫描。
  • 类似CMS收集器,分为4个部分。
    • 初始标记: GC Roots 能直接关联到的对象。
    • 并发标记:并发搜索可达对象。
    • 最终标记:修正并发标记期间产生变动的对象的标记。
    • 筛选回收:对各个Region的回收价值和成本进行分析,根据期望的回收时间实行回收计划。
      180526.g1.png

垃圾收集器参数

  • 180526.para1.png
    180526.para2.png

内存分配策略

  • JVM-彻底搞懂 逃逸分析&标量替换
  • 211229.mem.png
  • 逃逸分析后,如果可标量替换,分配在栈上。
  • 大对象直接进入老年代。典型的是很长的字符串或者大数组。
  • 小对象优先分配在Eden。Eden没有足够空间,虚拟机进行一次Minor GC。
  • 长期存活的对象将进入老年代。每个对象都有一个年龄计数器。
    • 对象在Eden出生并经过一次Minor GC后存活,并且能被Survivor容纳,将进入Survivor。
    • 在Survivor每经过一次Minor GC,年龄加1,年龄增加到阈值(默认15),则进入老年代。
    • 年龄动态判定。如果Survivor中相同年龄的所有对象大小总和大于Survivor的一半,大于等于该年龄的对象直接进入老年区。

空间分配担保:

  • 在Minor GC之前,老年代进行担保。
  • 老年代最大可用的连续空间大于新生代所有对象的总空间,或者历次晋升到老年代对象的平均大小,则进行 Minor GC。否则Full GC。

逃逸分析

  • 逃逸状态:
    • 全局逃逸。对象的作用范围超过当前方法或线程,如静态变量、已逃逸对象、当前方法返回值。
    • 参数逃逸。对象作为方法参数传递。
    • 没有逃逸。
  • 优点
    • 分配在栈上——标量替换
    • 锁消除——同步消除
  • 缺点:
    • 必须在JIT中完成。需要收集足够多的数据才能判断是否逃逸。

标量替换

  • 标量:基本数据类型就是标量(如:int,long等基本数据类型以及reference类型等)
  • 聚合量:可进一步分解的量,如Java对象
  • 替换前提:逃逸分析后确定不会被外部访问,且可以进一步分解
  • 替换步骤:JVM不创建该对象,而是将该对象成员变量分解成若干个被这个方法使用的成员变量,这些代替的成员变量在栈帧或寄存器上分配空间
  • jdk7会后默认开启

同步消除

  • 如果对象没有出现线程逃逸,那该对象的读写就不会存在资源的竞争,不存在资源的竞争,则可以消除对该对象的同步锁。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值