JVM GC概念,问题及调优

一、JVM内存概念

JVM区域总体分两类,heap区和非heap区。

heap区又分为:Eden Space(伊甸园)、Survivor Space(幸存者区)、Old Gen(老年代)。

非heap区又分:Code Cache(代码缓存区)、Perm Gen(永久代)、Jvm Stack(java虚拟机栈)、Local Method Statck(本地方法栈);

下面我们对每一个内存区域做详细介绍:

        Eden SpaceSurvivor Space都属于新生代,新生代中执行的垃圾回收被称之为Minor GC(因为是对新生代进行垃圾回收,所以又被称为Young GC),每一次Young GC后留下来的对象age加1。

        Eden Space字面意思是伊甸园,对象被创建的时候首先放到这个区域,进行垃圾回收后,不能被回收的对象被放入到空的survivor区域。

        Survivor Space幸存者区,用于保存在eden space内存区域中经过垃圾回收后没有被回收的对象。Survivor有两个,分别为To Survivor、 From Survivor,这个两个区域的空间大小是一样的。执行垃圾回收的时候Eden区域不能被回收的对象被放入到空的survivor(也就是To Survivor,同时Eden区域的内存会在垃圾回收的过程中全部释放),另一个survivor(即From Survivor)里不能被回收的对象也会被放入这个survivor(即To Survivor),然后To Survivor 和 From Survivor的标记会互换,始终保证一个survivor是空的。

         Old Gen老年代,用于存放新生代中经过多次垃圾回收仍然存活的对象,也有可能是新生代分配不了内存的大对象会直接进入老年代。经过多次垃圾回收都没有被回收的对象,这些对象的年代已经足够old了,就会放入到老年代。当老年代被放满的之后,虚拟机会进行垃圾回收,称之为Major GC。由于Major GC除并发GC外均需对整个堆进行扫描和回收,因此又称为Full GC。

二、内存分配策略

Java 提供的自动内存管理,可以归结为解决了对象的内存分配和回收的问题。

前面已经介绍了内存回收,下面介绍几条最普遍的内存分配策略:

1)对象优先在 Eden 区分配: 大多数情况下,对象在先新生代 Eden 区中分配。当 Eden 区没有足够空间进行分配时,虚拟机将发起一次 Young GC。

2)大对象之间进入老年代: JVM 提供了一个对象大小阈值参数(-XX:PretenureSizeThreshold,默认值为 0,代表不管多大都是先在 Eden 中分配内存)。大于参数设置的阈值值的对象直接在老年代分配,这样可以避免对象在 Eden 及两个 Survivor 直接发生大内存复制。

3)长期存活的对象将进入老年代: 对象每经历一次垃圾回收,且没被回收掉,它的年龄就增加 1,大于年龄阈值参数(-XX:MaxTenuringThreshold,默认 15)的对象,将晋升到老年代中。

4)空间分配担保: 当进行 Young GC 之前,JVM 需要预估:老年代是否能够容纳 Young GC 后新生代晋升到老年代的存活对象,以确定是否需要提前触发 GC 回收老年代空间,基于空间分配担保策略来计算。

continueSize,老年代最大可用连续空间:

三、GC事件分类

根据垃圾收集回收的区域不同,垃圾收集主要分为:Young GC、Old GC、Full GC、Mixed GC

  • Young GC:新生代内存的垃圾收集事件称为 Young GC(又称 Minor GC),当 JVM 无法为新对象分配在新生代内存空间时总会触发 Young GC。
  • 比如 Eden 区占满时,新对象分配频率越高,Young GC 的频率就越高。
  • Old GC: 只清理老年代空间的 GC 事件,只有 CMS 的并发收集是这个模式。
  • Full GC: 清理整个堆的 GC 事件,包括新生代、老年代、元空间等 。
  • Mixed GC: 清理整个新生代以及部分老年代的 GC,只有 G1 有这个模式。

四、什么时候会触发GC

        Young GC的触发时机:Young GC其实一般就是在新生代的Eden区域满了之后就会触发,采用复制算法来回收新生代的垃圾

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对老年代进行垃圾回收。

        在很多JVM的实现机制里,其实在上述几种条件达到的时候,他触发的实际上就是Full GC,这个Full GC会包含Young GC、Old GC和永久代的GC。也就是说触发Full GC的时候,可能就会去回收年轻代、老年代和永久代三个区域的垃圾对象。但是这个东西其实没办法给大家一个准确的定义,说到底触发Full GC的时候,是先执行Young GC?还是先执行OldGC。因为不同的Full GC触发条件其实是不一样的,而且不同的JVM版本的实现机制也不同。

        所以很多时候,我们也只能给大家笼统的概括一句:上述条件满足时触发Full GC,Full GC一般会带上一次Young GC去回收新生代,同时也会有Old GC也回收老年代,还会去回收永久代。如果永久代真的放满了,回收之后发现没腾出来更多的地方,此时只能抛出内存不够(OOM)的异常了。

五、GC时间过长导致的系统问题

        GC 每次都会引起全线停顿(Stop-The-World),暂停所有的应用线程,停顿时间相对老年代 GC 造成的停顿,几乎可以忽略不计。GC 调优中,GC 导致的应用暂停时间影响系统响应速度,GC 处理线程的 CPU 使用率影响系统吞吐量。

        长时间,频繁的GC会导致全线线程停顿时间过长,外显就是dubbo超时、redis超时,db访问等依赖线程池的访问超时,及其cpu利用率飙高(因为GC消耗系统资源)

六、什么操作会导致频繁、长时间GC

4.1 堆空间不够

如果应用程序需要的内存比我们执行的Xmx还要大,也会导致频繁的垃圾回收,甚至OOM。由于堆空间不足,对象分配失败,JVM就需要调用GC尝试回收已经分配的空间,但是GC并不能释放更多的空间,从而又回导致GC,进入恶性循环。

4.2 参数异常

jvm参数设置中年轻代与老年代空间设置不符合实际要求

4.3 内存中存在大对象

打印大日志等行为导致内存中存在大对象,大对象直接进入老年代引发Mix GC,且无法释放。导致频繁,长时间GC

4.4 内存中存在过多小对象

编码不规范,使用new新对象的方式使用单例对象,导致GC

参考:

老大难的 GC 原理及调优,这下全说清楚了 - AIQ

JVM内存区域详解(Eden Space、Survivor Space、Old Gen、Code Cache和Perm Gen)_Sam.Shi的博客-CSDN博客_eden space

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值