总结:GC调优之原理篇

一、介绍

二、GC区域

GC是针对堆内存而言的。

虚拟机栈是线程独占的,也就是说随着线程初始而初始,消亡而消亡,当线程被销毁后,虚拟机栈上的内存自然会被回收,也就是说虚拟机栈上的这块内存空间不在虚拟机GC范围。 垃圾回收的内存范围 :

三、GC哪些对象?

1、结论

  堆上的实例对象无法再次引用它,那么它就是被GC的对象,我们称之为对象“已死”。当对象被标记为“已死”,则表示此对象是可被GC回收的。

2、有哪些算法判断对象是否“已死”呢?

主要有引用计数法和可达性分析算法。

引用计数法: 给对象添加一个引用计数器,当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是死亡的。 优点是简单,缺点是无法解决循环引用的问题。

可达性分析算法:给定一个集合的引用作为根出发,通过引用关系遍历对象图,能被遍历到的(可到达的)对象就被判定为存活,其余对象(也就是没有被遍历到的)就自然被判定为死亡。Tracing GC的本质是通过找出所有活对象来把其余空间认定为“无用”,而不是找出所有死掉的对象并回收它们占用的空间。 GC roots这组引用是tracing GC的起点。
 

四、什么时候回收?

1、结论:在区域快满的时候(观察得到)

2、如下,当Eden区快满的时候会进行一次回收( 发生在Eden空间上的GC称为Minor GC ),Old区也是一样的道理,但是当Old区发生fullgc的时候,同时一般只会伴随一次Minor G,因为fullgc一般是由于年轻代进行Minor GC之前,会检查老年代有没有足够的空间,没有的话,检查有没有担保机制,总之就是为了将老年代腾出空间,把Eden区的对象复制过去,所以fullgc之后会伴随一次Minor GC。

但是也不是绝对的,因为不一定是Minor GC前触发的fullgc,也有可能是 程序执行了System.gc() (建议jvm执行fullgc,并不一定会执行 )或是 执行了jmap -histo:live pid命令 (这个会立即触发fullgc)

五、GC优化有哪些手段?

1、大对象会直接进入老年代

设置方式:-XX:PretenureSizeThreshold=1m

含义:超过1M的对象直接进行老年代。

为什么这么做:避免年轻代由于对象没有达到回收年龄而频繁复制耗时,每次复制是GC的时候做的(GC在发现Eden区快满的时候,会考虑进行垃圾回收),因此大对象会频繁的使得GC时间变长。

2、对象年龄达到指定阈值也会进入老年代

设置方式:-XX:MaxTenuringThreshold=6,(CMD默认值是6,G1默认值是15)

含义: 对象最多被复制的6次之后仍然存活,则将其放入老年代。设备为6表示最多这么多次,不表示一定是这么多次,比如Survivor中有一半以上的空间都已经被多次GC依然没有被回收的对象所占据了,则不用等到6次,直接放入老年代

为什么这么做:没必要在Survivor区频繁的复制浪费时间

3、尽量不要创建大对象

现状:一般为了不去操作DB,会将一些批量数据提前加载到内存中,但是当加载的数据过大,且定期加载,就会导致占用内存过多,从而触发GC。

解决思路:大对象可否拆成几个更小的对象?可否减少加载的字段?可否通过过滤条件减少加载的数据?

4、大对象拆成小对象,然后使用第三方缓存

这样就避免了JVM被占用大量内存,从而避免垃圾回收的产生。

举个例子:query组件会加载所有用户有哪些设备的读取权限,这里其实有个问题,读取query请求的实际上就几个账号,所以其实没有必要把所有用户加载进来(目前是个大map)。

优化方案:map拆分,每个用户一个对象,且无需初始化加载,用户第一次访问的时候加载,然后缓存到本地或redis,后续就不用加载了,直接从本地的map取就可以了。

六、几种 GC 方式

  1. Minor GC:年轻代,频率高,速度快
  2. Major GC:老年代
  3. Full GC:整个堆(年轻代,老年代)

七、对象的生命历程

       一般新生对象要进入Eden 区,Eden 区被填满时(或新分配的对象在Eden区找不到足够的空间时),要对Eden进行GC(注意,是Eden,不是Survivor ),GC之后还存活的对象将被复制到两个Survivor 区域中的一个。假定该Survivor 为From 区,From 区被填满之后,这个区域也要进行GC,GC 之后存活的的对象将会复制到To 区,From区清空。To区也被填满时,之前从From 区复制过来的那部分对象仍在活动则进入老年代。(From,To 必有一区会是空的)

八、对象年龄与转移时机

  • 通过年龄计数器判断一个对象是否需要转移。对象每经过一个GC 仍然活着,年龄计数器加1。当年龄超过设定的值,则将其通过担保机制转移到老年代。
  • 动态判定,当Survivor中年龄相同的对象超半数,则年龄大于该年龄的对象转移到老年代,无需等待到达设置的最大年龄值。
  • 大对象直接进入老年代。

九、Fullgc

JVM 出现 fullGC 很频繁,怎么去线上排查问题

年轻代与老年代的调优笔记

Java - 对象如何从年轻代到老年代

一、Full GC的原因

我们知道Full GC的触发条件大致情况有以下几种情况:

  1. 程序执行了System.gc() //建议jvm执行fullgc,并不一定会执行
  2. 执行了jmap -histo:live pid命令 //这个会立即触发fullgc
  3. 在执行minor gc的时候进行的一系列检查


二、在执行minor gc的时候进行的一系列安全检查,详情如下:

执行 Minor GC(年轻代GC) 的时候,JVM 会检查老年代中最大连续可用空间是否大于了当前新生代所有对象的总大小

1、如果大于,则直接执行 Minor GC(年轻代GC)(这个时候执行是没有风险的)

2、如果小于,JVM 会检查是否开启了空间分配担保机制,如果没有开启则直接改为执行Full GC 如果开启担保机制,则 JVM 会检查老年代中最大连续可用空间是否大于历次晋升到老年代中的平均大小,如果小于则执行改为执行Full GC 如果大于则会执行 Minor GC(年轻代GC),如果 Minor GC(年轻代GC) 执行失败则会执行 Full GC

3、出现Full GC的时候经常伴随至少一次的Minor GC(如下图,下图其实是先进行了fullgc,只是fullgc的时间比较长,所以后面才进行了累加),但不绝对。Major GC的速度一般会比Minor GC慢10倍以上

十、Minor GC

JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。

十一、eden 和survivor 区比例为什么是8:1?

因为经过统计,每次gc会有90%的对象被回收,所以要预留空间去保存剩下的10% , IBM论文里说据他们统计95%的对象朝生夕死一样存活时间极短

十二、 虚拟机内存越大就越好 ?

答案是。 大概原因是因为:内存越大,JVM 进行 Full GC 所需的时间越久,由于 Full GC 时 stop whole world 特性,如果是用于响应HTTP 请求的服务器,这个时候就表现为停止响应,对于需要低延迟的应用来说,这是不可接受的。对于需要高吞吐量的应用来说,可以不在乎这种停顿,比如一些后台的应用之类的,那么内存可以适当调大一些。需要根据具体情况权衡。

十三、年轻代与年老代内存比例多少合适?

默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 ),即:新生代 ( Young ) = 1/3 的堆空间大小。老年代 ( Old ) = 2/3 的堆空间大小。

我的理解,之所以年老代要比年轻代大,是为了给足年老代足够的空间,减少fullgc。

但是这样会有另外两个问题:

1、年老代太大,导致一旦fullgc,时间会很长;对于低延时要求的请求是不可接受的;

2、年轻代太小,导致频繁的minor gc;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值