概述
-
第二阶段的文章回顾和总结
-
你真的熟悉老年代么?对象在什么时候会进入到老年代?
-
频繁的FULL GC通常有什么原因
-
优化JVM需要注意哪些点?
你真的熟悉老年代回收么?
传统的JVM模型采用固定分代的形式,首先我们来回顾一下老年代的回收触发条件。
对象什么时候进入老年代
新生代是如何回收内存的?
讲述老年代回收之前,我们回顾一下新生代是如何分配内存的,没错,就是采用的
改良复制算法,新生代使用的是eden+2个survior区域进行内存的布局,默认情况下是8:1:1,这个值是可以改变的,我们可以使用参数:
-XX:SurvivorRatio=8进行改变,最后我们来看下他的结构图:
改良复制算法在eden区域占满之后,触发一次YGC,会把存活对象复制到S1区域,之后清空掉整个eden区,这个过程是非常快的,而到了下一次YGC,会把S1中的存活对象和Eden区域的存活对象复制到S2区域,并且清空掉S1区域和EDEN区域。所以新生代回收的特点是:
内存分为三块区域,每次只使用其中的两块,留存一块作为备用切换
以上是新生代的回收流程。当新生代触发YGC的时候,存活对象根据动态判断条件会进入老年代,那么老年代的FULL GC是如何处理的?他的触发条件是什么?这里的内容要反复回顾,因为十分的重要:
-
一个对象躲过了15次垃圾回收,年龄一到就进入老年代
-
对象超过新生代的总大小,超过一定的阈值,会直接往老年代分配
-
一次Young GC之后存活对象太多了,由于Survior区域无法存放,这批对象直接进入老年代
-
对象进入到Survior区域之后,Survior突然发现对象的占用内容超过50%,此时会根据年龄排序,把大于Survior区域的50%的年龄N的对象进入老年代,比如年龄2,3,4的对象,年龄为3的对象占比超过50%,意味着年龄3、4会进入老年代。
老年代GC是如何触发的?
老年代的FULL GC如何触发呢,在之前的文章有表格讲述触发老年代的时间,这里再次总结一下:
-
如果YGC存活对象太多,Survior区域放不下,如果要放入老年代,要是此时老年代也放不下,就会触发Full GC,回收老年代一批对象,再把这些年轻代的存活对象放入老年代中。
正常情况下一班多少次GC:
正常情况下FULL GC的频率在几十分钟一次,几小时一次,这个GC频率还算是比较可以接受的,当然每次FULL GC最好在几百毫秒内。
如果线上的系统有这个表现基本不需要太关心优化的问题。
如何观察JVM内存模型:
分析手法:
这里可以按照下面的过程进行分析:
-
EDEN区域的对象增长速率有多快?
-
YGC的频率有多高
-
一次YGC多长的耗时
-
老年代增长的速率多高
-
Full GC频率多高
-
一次Full GC耗时
频繁FULL GC的几种表现:
-
机器CPU负载高
-
频繁FULL GC报警
-
系统无法处理请求或者过慢。
频繁FULL GC一般有哪些原因:
之前讲述的案例比较多,这里集中讲解以下:
- 系统承载高并发请求,或者处理数据量过大,导致YGC频繁,YGC过后对象太多,内存分配不合理,Survior区域过小,
对象频繁进入老年代,频繁FULL GC
-
系统短时间加载大量的对象,出现很多
短命大对象,并且新生代无法存放只能进入老年代,必然频繁FULL GC
-
内存泄露,莫名其妙
创建大量对象,导致对象无法回收,并且占用老年代无法回收,也会触发FULL GC
-
永久代
加载过多类导致的FULL GC,多数情况是由于反射的错误使用导致。
-
误调用
System.gc()
优化JVM应该注意点:
-
最好有一套JVM通用模板:这里不是指随便百度一套模板就拿来用,而是要根据当前的系统分析得业务系统会产生多少对象名字啊什么时候会产生FULL GC,新生代要分配多少内存,老年代要分配多少内存
-
不要随便DUMP日志:自己试验可以随便用,但是一旦到了线上环境,不仅需要找运维沟通,使用这个命令需要在业务访问流量的低峰的时候再使用,需要非常小心谨慎的对待。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70000430/viewspace-2785191/
,如需转载,请注明出处,否则将追究法律责任。