第二十二课 ParNew工作机制
1、主打多线程垃圾回收机制,主要回收新生代(回收算法和Serial一样,不过Serial是单线程的)
垃圾回收线程数量是跟CPU核数一样的,也可以使用-XX:ParallelGCThreads来设置线程数量
2、指定使用ParNew垃圾回收器
-XX:+UseParNewGC,只要加入这个选项,就是使用ParNew来对新生代进行垃圾回收
3、单线程垃圾回收好还是多线程垃圾回收好
一般服务端程序都是使用多线程垃圾回收,因为他们都是多核CPU。对于一些单核的客户端程序,用单线程好一点
第二十三课 老年代垃圾回收器CMS工作时,内部又干了些啥
1、老年代采用标记清理算法,最大的问题会造成内存碎片。
2、以CMS垃圾回收器采取的是垃圾回收线程和系统工作线程尽量同时执行的模式来处理的,共分为4个阶段:
- 初始标记
- 并发标记
- 重新标记
- 并发清理
2.1、初始标记
标记出所有GC Roots直接引用的对象,STW,,速度快,仅标记GC Roots直接引用的那些对象。
在初始标记阶段,仅仅会通过“replicaManager”这个类的静态变量代表的GC Roots,去标记出来他直接引用的ReplicaManager对 象,这就是初始标记的过程。
方法的局部变量和类的静态变量是GC Roots。但类的实例变量不是GC Roots。
2.2、并发标记
系统线程可以随意创建各种新对象,继续运行。
在这个过程中,垃圾回收线程,会尽可能的对已有的对象进行GC Roots追踪。是最耗时的,跟系统程序并发运行的,不会对系统运行造成影响。
2.3 、重新标记
因为第二阶段里,一边标记存活对象和垃圾对象,一边系统在不停创建新对象,让老对象变成垃圾,所以第二阶段结束后,会有很多存活对象和垃圾对象是第二阶段没标记出来的,如下图
重新标记下在第二阶段里新创建的一些对象,还有一些已有对象可能失去引用变成垃圾的情况,如下图。
这个重新标记的阶段,是速度很快的,他其实就是对在第二阶段中被系统程序运行变动过的少数对象进行标记,所以运行速度很快。
2.4、并发清理
这个阶段其实是很耗时的,因为需要进行对象的清理,但是他也是跟系统程序并发运行的,所以其实也不影响系统程序的执行,如下图
思考题
parnew+cms的gc,如何保证只做ygc,jvm参数如何配置?
修改新生代和老年代的比例,比如改成2:1,修改Eden区和Survivor区的比例,比如改成6:2:2
第二十四课 线上部署系统,如何设置垃圾回收相关参数
1、并发标记和并发清理导致CPU资源紧张
CMS默认启动的垃圾回收线程的数量是(CPU核数 + 3)/ 4
“-XX:CMSInitiatingOccupancyFaction”参数可以用来设置老年代占用多少比例的时候触发CMS垃圾回收,JDK 1.6里面默认的值是 92%
2、CMS垃圾回收,Concurrent Mode Failure问题
并发清理时,因为没有Stop the World,所以可能会有新的垃圾对象进入老年代,这种对象被称为浮动垃圾,它们不会在这次gc中被清除,所以CMS在老年代内存占用达到一定比例时,就自动执行Full GC,否则内存可能溢出。这个比例可以通过-XX:CMSInitiatingOccupancyFaction来设置。但如果CMS垃圾清理时,放入老年代的对象大于了可用空间,就会发生Concurrent Mode Failure,此时会自动用Serial Old来代替CMS,就是直接把程序Stop the World,回收完后再恢复。
3、CMS垃圾回收,内存碎片问题
标记清理算法会导致内存碎片问题。
为了解决这个问题,CMS有一个参数 -XX:+UseCMSCompactAtFullCollection(默认开启),在Full GC后再次进行Stop the World,把存活对象挪到一起。
还有一个参数是 -XX:CMSFullGCsBeforeCompaction,执行多少次Full GC后再执行一次碎片整理工作,默认是0,意思是每次Full GC之后都会进行一次内存整理。
思考题:触发老年代GC的时机。
- 老年代可用内存小于新生代全部对象的大小,如果没开启空间担保参数,会直接触发Full GC,所以一般空间担保参数都会打开;
- 老年代可用内存小于历次新生代GC后进入老年代的平均对象大小,此时会提前Full GC;
- 新生代Minor GC后存活对象大于Survivor,或同龄对象大于Survivor的50%,会进入老年代,此时如果老年代内存不足
- -XX:CMSInitiatingOccupancyFaction参数
第二十五课 案例实战,新生代JVM参数设置
JVM参数 :
-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M - XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5 -XX:PretenureSizeThreshold=1M -XX:+UseParNewGC - XX:+UseConcMarkSweepGC
设置堆内存3072M,新生代2048M,每个虚拟机栈1M,方法区256M,Eden:Survivor=8:1:1,对象年龄大于5岁进入老年代,对象大于1M进入老年代,使用ParNew、CMS垃圾回收器
第二十六课 案例实战,老年代JVM参数设置
JVM参数如下:
-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M - XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5 -XX:PretenureSizeThreshold=1M -XX:+UseParNewGC - XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFaction=92 -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0
Full GC优化的前提是Minor GC的优化,Minor GC的优化的前提是合理分配内存空间,合理分 配内存空间的前提是对系统运行期间的内存使用模型进行预估。
第二十七课 作业
线上系统是怎么设置的JVM垃圾回收参数?设置的合理吗?
第二十八课 答疑
略
第二十九课 G1垃圾回收器的工作原理
1、可以搞定所有的垃圾回 收。 将Java堆内存拆分为多个大小相等的Region。
G1也会有新生代和老年代的概念,但是只不过是逻辑上的概念 也就是说,新生代可能包含了某些Region,老年代可能包含了某些Reigon
2、设置垃圾回收的预期停顿时间
比如可以指定,G1垃圾回收时,在1小时内由于回收导致的Stop the World不超过1分钟
G1可以做到让你来设定垃圾回收对系统的影响,他自己通过把内存拆分为大量小Region,以及追踪每个Region中可以 回收的对象大小和预估时间,最后在垃圾回收的时候,尽量把垃圾回收对系统造成的影响控制在你指定的时间范围内,同时在有限的时 间内尽量回收尽可能多的垃圾对象。
总结:
Region动态转移给新生代或者老年代,按需分配 然后触发垃圾回收的时候,可以根据设定的预期系统停顿时间,来选择最少回收时间和最多回收对象的Region进行垃圾回收,保证GC 对系统停顿的影响在可控范围内,同时还能尽可能回收最多的对象。
第三十课 G1原理深度图解
1、G1对应的内存大小
JVM启动的时候一旦发现你使用的是G1垃圾回收器,可以使用“-XX:+UseG1GC”来指定使用G1垃圾回收器,此时会自动用堆大 小除以2048 因为JVM最多可以有2048个Region,然后Region的大小必须是2的倍数,比如说1MB、2MB、4MB之类的。
刚开始时,默认新生代占比5%,可以通过-XX:G1NewSizePercent来设置比例
在系统运行时,JVM会给新生代增加更多的Region,但默认不会超过60%,可以通过-XX:G1MaxNewSizePercent来设置。而一旦Region进行了垃圾回收,此时新生代的Region数量会减少,这些其实都是动态的
2、新生代依旧有Eden和Survivor的概念
-XX:SurvivorRatio=8依旧有效。比如总共有100个Region属于新生代,那么其中就有80个Region属于Eden,20个属于Survivor
3、G1的新生代垃圾回收
当新生代的Region占比达到最大值,就会触发young gc,使用复制算法,进入Stop the World状态。选择回收哪些会根据设置的垃圾回收的预期停顿时间来判断
4、对象何时进入老年代
老年代最多可以占据40%的Region。
(1)对象在新生代躲过了很多次的垃圾回收,达到了一定的年龄了,“-XX:MaxTenuringThreshold”参数可以设置这个年龄,他就 会进入老年代
(2)动态年龄判定规则,如果一旦发现某次新生代GC过后,存活对象超过了Survivor的50%
5、大对象Region
G1提供了专门的Region来存放大对象,而不是让大对象进入老年代的Region中。
在G1中,大对象的判定规则就是一个大对象超过了一个Region大小的50%,比如按照上面算的,每个Region是2MB,只要一个大对 象超过了1MB,就会被放入大对象专门的Region中。而且一个对象如果太大,会横跨多个Region来存放。
在新生代、老年代在回收的时候,会顺带带着大对象Region一起回收
第三十一课 线上系统G1参数如何设置
1、-XX:InitiatingHeapOccupancyPercent,他的默认值是45%。
意思是,如果老年代占据了堆内存的45%的Region的时候,此时就会尝试触发一个新生代+老年代一起回收的混合回收阶段。
2、G1垃圾回收过程
初始标记:Stop the World,标记GC Roots能引用的对象,这个过程速度很快
“并发标记”:这个阶段会允许系统程序的运行,从GC Roots开始追踪全部的存活对象,比如哪些对象被新建了,哪些对象失去了引用。
最终标记阶段:“Stop the World”,终标记一下有哪些存活对象,有哪些是垃圾对象
混合回收:Stop the World,计算新生代、老年代、大对象中每个Region存活对象数量,回收时间,然后根据设置的停顿时间,选择回收部分Region。混合回收可以执行多次,即先停止工作,回收掉一些Region,恢复系统运行,再停止工作,回收一些Region。
参数
-XX:G1MixedGCCountTarget参数,意味着最后一个阶段,先停止系统运行,混合回收一些Region,再恢复系统运行,接着再次禁止系统运行,混合回收一些Region,反 复8次。
-XX:G1HeapWastePercent,默认值是5% 他的意思就是说,在混合回收的时候,对Region回收都是基于复制算法进行的,都是把要回收的Region里的存活对象放入其他 Region,然后这个Region中的垃圾对象全部清理掉。在回收过程就会不断空出来新的Region,一旦空闲出来的Region数量达到了堆内存的5%,此时就会 立即停止混合回收,意 味着本次混合回收就结束了。
-XX:G1MixedGCLiveThresholdPercent,他的默认值是85%,意思就是确定要回收的Region的时候,必须是存 活对象低于85%的Region才可以进行回收。
3、回收失败时的Full GC
万一出现拷贝的过程中发现没有空闲Region可以承载自己的存活对象了,就会触发一次失败。
一旦失败,立马就会切换为停止系统程序,然后采用单线程进行标记、清理和压缩整理,空闲出来一批Region,这个过程是极慢极慢 的
第三十二课 案例实战
这里核心还是在于调节“-XX:MaxGCPauseMills”这个参数的值,在保证他的新生代gc别太频繁的同时,还得考 虑每次gc过后的存活对象有多少,避免存活对象太多快速进入老年代,频繁触发mixed gc。
针对并发量很大的场景,为了提高GC效率,一般使用G1垃圾回收, 或是增大内存
第三十六课 系统卡死问题
基于JVM运行的系统最怕什么
有一个很大的问题,就是每次一旦年轻代塞满之后,在进行垃圾回收的时候,这个期间都必须停止系统程序的运行!
基于JVM运行的系统最害怕的问题:系统卡顿问题!
对年轻代的gc进行调优,只要你给系统分配足够的内存即可,核心点还是在于堆内存的分配、新生代内存的分配
内存足够的话,通常来说系统可能在低峰时期在几个小时才有一次新生代gc,高峰期最多也就几分钟一次新生代gc。
而且一般的业务系统都是部署在2核4G或者4核8G的机器上,此时分配给堆的内存不会超过3G,给新生代中的Eden区的内存也就1G左 右
系统卡顿几十毫秒,就这期间的请求会卡顿几十毫秒,几乎用户都是无感知的,所以新生代gc一般基本对系统性能影响 不大。
什么时候新生代gc对系统影响很大?
当你的系统部署在大内存机器上的时候
如何解决大内存机器的新生代GC过慢的问题?
用G1垃圾回收器,可以完美解决大内存垃圾回收时间过长的问题
老年代gc至少比新生代gc慢10倍以上,比如新生代gc每次耗费200ms,其实对用户影响不大,但是老年代每次gc耗费2s, 那可能就会导致老年代gc的时候用户发现页面上卡顿2s,影响就很大了
JVM性能优化到底在优化什么
系统真正最大的问题,就是因为内存分配、参数设置不合理,导致你的对象频繁的进入老年代,然后频繁触发老年代gc,导致系统频繁的每隔几分钟就要卡死几秒钟
第三十七课 什么是Young GC和Full GC
1、Minor GC/Young GC
新生代GC
2、Full GC?Old GC?傻傻分不清楚
Old GC:老年代GC
3、Full GC
Full GC指的是针对新生代、老年代、永久代的全体内存空间的 垃圾回收,所以称之为Full GC。
Full GC可以理解为针对JVM内所有内存区域的 一次整体垃圾回收。
4、Major GC
老年代的垃圾收集叫做Major GC,Major GC通常是跟full GC是等价的,收集整个GC堆。
5、Mixed GC
G1中特有的概念,一旦老年代占据堆内存的45%了,就要触发Mixed GC,此时对年轻代和老年代都会进行回收。
第三十八课 GC触发时机
1、Young GC的触发时机
一般就是在新生代的Eden区域满了之后就会触发,采用复制算法来 回收新生代的垃圾
2、Old GC和Full GC的触发时机
(1)发生Young GC之前进行检查,如果“老年代可用的连续内存空间” < “新生代历次Young GC后升入老年代的对象 总和的平均大小”,说明本次Young GC后可能升入老年代的对象大小,可能超过了老年代当前可用内存空间
此时必须先触发一次Old GC给老年代腾出更多的空间,然后再执行Young GC
(2)执行Young GC之后有一批对象需要放入老年代,此时老年代就是没有足够的内存空间存放这些对象了,此时必 须立即触发一次Old GC
(3)老年代内存使用率超过了92%,也要直接触发Old GC,当然这个比例是可以通过参数调整的
概括成一句话,就是老年代空间也不够了,没法放入更多对象了,这个时候务必执行Old GC对老年代进行垃圾回收。
第三十九课 BI系统是如何避免Young GC
“商务智能BI”,指的就是给你看一些数据报表,然后让你平时能够更好的了解自己的经营状况,然后让老板“智能”的去调整经营 策略,提升业绩
业务初期
普通的4核8G的配置,然后在这个配置之下,一般 来说给堆内存中的新生代分配的内存都在1.5G左右,Eden区大概也就1G左右的空间
中后期
BI系统中有一种数据报表,他是支持前端页面有一个JS脚本,自动每隔几秒钟就发送请求到后台刷新一下数据的,这种报表称之为“实时数据报表”,如下图所示
每个请求大概需要加载出来100kb的数据进行计算,因此每秒500个请求,就需要加载出来50MB的数据到内存 中进行计算,如下图所示
在上述系统运行模型下,基本上每秒会加载50MB的数据到Eden区中,只要区区200s,也就 是3分钟左右的时间,就会迅速填满Eden区,然后触发一次Young GC对新生代进行垃圾回收。
BI系统运行几分钟过后,就会突然卡顿个10ms,但是对终端用户和系统 性能几乎是没有影响的,如下图。
提升机器配置:运用大内存机器
当高峰期会有每秒10万的并发压力时,需要将部署的机器全面提升到了16核32G的高配置机器上去。每台机器可以抗个每秒几千请求,此时只要部署比如二三十台机器就可以了。
但是如果要是用大内存机器的话,那么新生代至少会分配到20G的大内存,Eden区也会占据16G以 上的内存空间,此时如下图所示。
此时每秒几千请求的话,每秒大概会加载到内存中几百MB的数据,那么大概可能几十秒,甚至1分钟左右就会填满Eden区,会就需要 执行Young GC。
此时Young GC要回收那么大的内存,速度会慢很多,也许此时就会导致系统卡顿个几百毫秒,或者1秒钟,如下图所示
要是系统卡顿时间过长,必然会导致瞬间很多请求积压排队,严重的时候会导致线上系统时不时出现前端请求超时的问题,就是 前端请求之后发现一两秒后还没返回就超时报错了。
用G1来优化大内存机器的Young GC性能
所以当时对这个系统的一个优化,就是采用G1垃圾回收器来应对大内存的Young GC过慢的问题。
对G1设置一个预期的GC停顿时间,比如100ms,这样,也许Young GC的频率会更高一些,但是每次停顿时间很小,这样对系统影响就不大了。
第四十课 避免频繁Full GC
略