学习深入理解jvm虚拟机心得——谈谈GC垃圾收集器和回收策略(2)

1.前言

有幸有了大段完整的学习时间,本人也逐步调整心态静下心来看一看经典的java书籍。今天把gc章节看完,感觉脑子里比较混乱,可能是这一章节的新名词比较多吧,parallel scavange,parnew,serial啊等等。有必要将这些没记住的内容总结下来。我比较认同的一个观点就是,带着问题去学习。面试中问到的与jvm基础相关的肯定少不了gc。下面举例

a.堆内存中新生代老年代各自什么gc算法,为什么不同,还有什么算法。

b.创建大对象时,是在哪个分代中分配。

c.谈谈minor gc/major gc/full gc触发条件。

d.简述新生代老年代gc过程。

e.full gc是否真正回收了废弃对象。

以上问题摘自https://blog.csdn.net/fanx_000/article/details/80297487

2.如何判断对象是否存活

2.1 引用计数法,优点:实现简单,效率高。缺点:无法解决循环引用的问题。

2.2 可达性分析算法(这个名词有点绕),思路:通过GC Roots对象作为起点,开始向下搜索,走过的路径称为引用链。当一个对象和GC Roots之间没有引用链时,称这个对象是不可达的,即可回收的。

可作为GC Roots的对象包括:虚拟机栈中(栈帧中的本地变量表)引用的对象、本地方法栈(JNI)中引用的对象、方法区中的静态属性引用的变量、方法区中常量引用的对象。

2.3 引用的引申:java中创建对象时默认使用强引用,如Object a = new Object();除了强引用还有软引用、弱引用、虚引用。此概念不在这里扩展。

2.4 针对以上提出的问题e,我没有找到答案,是否是跟finalize()有关呢?

2.5 方法区回收

回收的对象为废弃的常量、废弃的类。回收的条件不在此多做介绍。注:大量使用反射、动态代理技术、cglib等bytecode框架,动态生成JSP等的场景都需要虚拟机具备类卸载的功能,以保证方法区不会溢出。

3.垃圾回收算法

3.1 标记-清除

该算法有两个过程,标记和清除。使用可达性分析算法标记哪些可回收的对象,然后统一对这些对象回收。

3.2 复制

该算法被设计成将内存分为大小相等的两块,每次只用其中的一块。当这一块的内存使用完了,就把还存活的对象复制到另外一块上,然后把当前内存一次清理掉。这样使得每次都是对整个半区进行清理,无需考虑内存碎片问题,只需移动堆顶指针,按顺序分配内存。

针对问题d-新生代的gc过程:新生代将内存区域分为一份Eden区和两个Survivor区,比例默认是8:1。每次使用eden区和其中一个survivor区。当回收时,将eden和survivor中仍存活的对象复制到另一个survivor区,最后清理掉eden和刚才用过的survivor区。(survivor空间不足会触发空间分配担保)

3.3 标记-整理

该算法分为两个阶段,标记过程与标记-清除算法相同,后续将仍然存活的对象向堆得一侧移动,最后对端边界以外的内存进行清理。

3.4 分代收集

针对问题a:新生代在gc回收时,存活的对象少,复制成本小,所以采用的是复制算法;老年代的对象存活几率大,并且没有额外的内存做分配担保,采用标记-整理算法。

4.Hotspot版本的算法实现

涉及到的概念有枚举根节点、安全点、安全区域,这部分没看懂。

5.垃圾收集器

最重要的写在最前面,JDK1.7/1.8默认采用的垃圾收集器为Parallel scanvenge + Parallel Old组合,JDK1.9使用G1收集器。

多角度总结收集器:单线程/多线程、吞吐量优先/暂停时间优先、并行/并发。关于后两个名词的解释如下图

常用收集器如图所示

图3-5展示了7种作用于不同分代的收集器,如果两个收集器之间存在连线,就说明他们可以搭配使用。以下详细介绍

  • Serial是一款单线程垃圾收集器,他不仅仅代表着只使用一个CPU或一条收集线程去完成垃圾收集工作,而且必须暂停用户线程,直到gc结束。俗称STOP THE WORLD。
  • ParNew是Serial的多线程版本,即并行收集器。
  • Parallel Scavenge是一款并行收集器,同前两者,使用复制算法。那他和ParNew的区别在哪儿呢?他的特点是他的关注点与其他收集器不同,CMS等收集器的关注点是尽可能的缩短垃圾收集时用户线程的停顿时间。而前者是为了达到一个可控制的吞吐量(用一个公式表示:吞吐量=用户线程花费的时间/(用户线程花费的时间+垃圾收集花费的时间))。该策略会高效的利用cpu,适合后台任务的计算。相关参数:-XX:MaxGCPauseMillis/-XX:GCTimeRatio,前者设置最大垃圾收集停顿时间,后者设置吞吐量大小。
  • 前面三个收集器都新生代的实现,下面介绍几款老年代的实现。Serial Old,特点:单线程、使用标记-整理算法。一般在c端使用。
  • Parallel Old,特点:并行(多线程)、使用标记-整理算法。
  • CMS(Concurrent Mark Sweep),特点:以获取最短回收停顿个时间为目标的收集器。回收步骤分为以下四部:初始标记、并发标记、重新标记、并发清理。讲述的内容比较多,包括缺点、如何优化,在此不做详细介绍。
  • G1(Garbage-First),特点:面向服务端应用,代替CMS。突出优点:可预测停顿。

5.GC日志分析

详细参考https://blog.csdn.net/zc19921215/article/details/83029952

6.内存分配和垃圾回收策略

内存分配遵循以下几个原则:

  • 优先在Eden区分配
  • 大对象直接进入老年代,使用不同的垃圾收集器,表现不一致。若使用Serial/ParNew,则可以通过调整参数-XX:PretenureSizeThreshold,当大于设置的值时,将对象直接分配到老年代。
  • 长期存活的对象晋升老年代,关键字:对象年龄计数器。
  • 动态对象年龄判定,没看懂这个晋升规则。
  • 空间分配担保

对象内存分配过程:对象优先在新生代分配(开启TLAB时则分配到对应的空间上),若剩余空间不足,根据空间分配担保规则,在进行minor gc之前,检查老年代的剩余的最大连续内存空间是否比新生代对象的总大小/历代晋升的平均大小的大。如果不够放下新生代存活的对象,则进行一次full gc。

7.工具介绍、实际问题排查

常用可视化工具有jconsole/visual vm,命令行工具有jps/jstat等等,前者查看虚拟机进程,后者配合参数使用,查看gc回收状态。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值