深入理解JVM - 阶段总结与回顾(一)
前言
这篇文章不写新内容,而是回顾之前的文章内容。
为什么开设专栏
开设这个专栏的目的毫无疑问是给个人的成长做一个记录和归档,因为这段时间下来发现学东西一定要系统并且有目的循序渐进的学才有更快的成长,JVM的内容和细节是学不完的,所以要清楚学这个东西的作用是什么很关键,个人学这个东西无非就是为了面试以及了解底层原理,同时本着书到用时方恨少的原则编写专栏。
系列文章阶段总结:
专栏地址:深入理解JVM虚拟机
第一篇:深入理解JVM虚拟机 - JVM的初步了解
系列的开篇,在第一篇专栏我们需要了解JVM到底是个什么东西,他对于JAVA开发者而言的重要意义,同时我们编写的代码是如何通过JVM运行并且实现我们想要的效果的,重点在于JAVA加载到JVM的工作流程。
这里也讲述了JVM的双亲委派模型和Tomcat的双亲委派模型是如何打破他的,类加载器也是JVM的一个核心内容。
第二篇:深入理解JVM虚拟机 - 虚拟机的发展历史
基本就是《深入理解JVM虚拟机》的笔记了,当然很多博客也介绍了。
第三篇:深入理解JVM - 分代的基本概念
讲述了JVM的传统分代模型的特点,以及新生代和老年代的划分,重点是新生代如何进入老年代,需要的条件以及相关的参数设置也是面试高频点。最后介绍了JVM的调优参数。
第四篇:深入理解JVM虚拟机 - jvm的对象分配策略
这篇文章个人认为值得关注的点是实际的JVM测试结果和书里面不一样!同时可以发现新版本当中的Parnew会在连续分配大对象的时候让对象直接进入老年代,真的很奇怪!还是建议学这些东西一定自己尝试,花点时间换来的收益是你意向不到的!
第五篇:深入理解JVM - 垃圾回收算法
和标题一样,介绍JVM的垃圾回收算法以及新生代老年代是如何运用算法进行回收的,需要注意的是这些垃圾算法没有好坏之分,一切看实际垃圾收集器开发者的设计理念。比如CMS还是典型的标记-清除,但是到了G1就是标记整理了。
第六篇:深入理解JVM - CMS收集器
讲述CMS收集器的手机细节,以及CMS的好搭档ParNew的一些处理细节,CMS+ParNew相信还是多数公司的首选垃圾收集器。毕竟不是谁都有那么庞大的用户量或者大项目这种。
第七篇:深入理解JVM - 实战老年代优化
无需多介绍,内容和标题类似。
第八篇:深入理解JVM - G1收集器
功能和使用看似都十分简单,然而内含的原理十分复杂,主要也是讲解G1收集器的一些功能和细节点。
同时是否需要研究原理这就看个人需求了,当然多懂点总是好事。
第九篇:深入理解JVM - G1调优简述
感觉完全是水了一篇,可以不看,因为个人目前没遇到需要用上G1去调优的场景(=-=)
相关讨论
回收的说法我混淆了?
Minor gc: 中文翻译是次要GC,次要这两个字很容易混淆,但是多数情况是新生代回收
Young gc:新生代回收
Full gc:多数人会认为是老年代回收,然而实际上和这个单词的含义不同,所以full GC应该类似于全堆回收
Old gc:毫无疑问就是老年代回收
Major gc: 重要GC,当然这个词用的不多,如果不知道的情况下默认为老年代GC即可。
Mixed gc: 混合GC,这个概念最开始应该是在G1才提到,值得是老年代和新生代以及大对象一起回收
系统真正怕的是什么?
垃圾收集的stop world操作,但是可以明显的发现,新生代的回收几乎是没有影响了,有20ms没法操作系统,几乎没有几个人可以感知,就连我们连一个网页都不止几秒的时间,更不要说这微小的感知了。
但是老年代的回收就不一样了,我们假设老年代回收的时间需要新生代的10倍,那就是200ms,好像也是没啥感觉哈,但是如果是2000ms,那问题就很大了,CPU的处理速度如果需要停顿2S那基本会让人误以为是程序出了问题,所以老年代的停顿是不可容忍的。
系统内存过大的问题:
如果采用传统的分代模型作用于现在动辄上百G的服务,那么假设新老年代需要几十个G的内存,那么一次停顿的时间大概到了一分钟了,因为对象太多了,回收的效率直线下降。
因此超大内存的出现也是G1收集器出现的一个原因之一。
如何解决大内存系统的垃圾回收问题?
使用G1收集器,该收集器首先会判断新生代是否占用的比率符合停顿模型的参数,此时需要根据参数判断是否需要进行处理,如果需要处理,则根据最大停顿时间进行垃圾回收、
其实本质上就是把本来触发条件回收变成了定时判断是否符合垃圾回收的条件,有一点垃圾就回收一点,这样系统可以正常运行,垃圾回收也可以尽可能的回收内存,并且只要垃圾回收的速度赶上分配速度,那么整个系统是基本上不会有任何JVM问题的!
要命的老年代回收!
老年代回收肯定是因为新生代出了问题,因为对象没有地方放得时候老年代肯定就会执行了,老年代回收本来就比新生代慢十倍,加上频繁的老年代更加容易让系统卡顿,让用户体验变得非常差。
之前总结过老年代回收的一些条件:
- 动态对象年龄判断:如果老年代的可用对象大于新生代,那么基本没有问题,但是一旦小于这个值,就需要校验一下老年代的最大连续空间是否大于历代晋升老年代的平均大小,如果是则执行mior gc,如果不是此时老年代肯定是放不下则需要进行Full Gc了,的。
- 对象年龄超过阈值:一般是1 -15 ,注意也只有1 -15,没有23,25,26,30这些参数,因为Marking word对象头只给了4位 1111表示对象的年龄!
- 对象回收之后,存活的对象在老年代放不下了。
- CMS并发回收的过程中一旦剩余空间无法分配,会出现concurrent mode fail并且开启Serrial进行Full gc。
如何选择cms+Parnew还是g1?
Parnew+cms:一般中小型的系统对于系统的性能要求不是很高,同时也不需要高同步和实时特征的时候使用
G1收集器:如果是超大内存的系统适合使用g1的收集器。
什么时候触发永久代回收?
永久代回收的空间有限,多数情况下存放了类变量和一些静态的常量或者.class对象的引用信息,需要注意的是反射和动态代理对象是最容易引发永久代溢出的,特别是现代框架多数使用反射加载对象,导致方法区不断出现对象占用。方法去一旦回收基本都是OOM.
不断创建动态代理对象也是测试方法区溢出效果的最好办法。
几乎没有影响 young gc?
假设一个业务系统是初期阶段,用户量不是很大,每秒大概也就500次请求,生产的对象大概也就是50M左右,假设大概3分钟就能把新生代填满,同时频繁的young gc,但是会发现这一次young gc也就花了20ms几乎是无法被用户感知的。
但是多数情况下业务初期根本不需要调优JVM。
另外,调优不要为了调优而调优,自己做的开源项目可以乱搞,但是公司拿来赚钱的项目是需要多方面考虑的,切记。
系统升级后垃圾收集器的影响
假设一台小机器系统升级到32核16G,老年代和新生代占用6G左右,这时再使用分代回收的方式效率十分低了,所以我们使用G1收集器进行垃圾的回收的工作并且让回收操作提前完成也是保证后续的计算任务不会造成卡顿或者阻塞。
写在最后
不断复习和回顾总结,知识才能学得更加牢靠!