加入收藏 | 设为首页 | 会员中心 | 我要投稿 衡阳站长网 (https://www.0734zz.cn/)- 数据集成、设备管理、备份、数据加密、智能搜索!
当前位置: 首页 > 综合聚焦 > 资源网站 > 空间 > 正文

记一次隐藏很深的 JVM 线上惨案的分析、排查、解决

发布时间:2019-09-10 14:35:19 所属栏目:空间 来源:Java的小本家
导读:1、本文背景 本文会给大家讲解一个比较特殊的JVM优化案例,这个优化案例本身是因为新手工程师对JVM优化可能了解了一个半吊子,然后不知道从哪里找来了一个非常特殊的JVM参数错误的设置了一下,就导致线上系统频繁的出现Full GC的问题。 但是我们后续大量的

比如上面的那个类,如果你查阅一些资料,很容易就会搞明白,那个类大概是在你使用Java中的反射时加载的,所谓反射代码类似如下所示。

  1. Method method = XXX.class.getDeclaredMethod(xx,xx); 
  2. method.invoke(target,params); 

友情提示一下,反射是Java中最最基础的一个概念,不懂的朋友自己查一下资料。

简单来说,就是通过XXX.class获取到某个类,然后通过geteDeclaredMethod获取到那个类的方法。

这个方法就是一个Method对象,接着通过Method.invoke可以去调用那个类的某个对象的方法,大概就这个意思。

在执行这种反射代码时,JVM会在你反射调用一定次数之后就动态生成一些类,就是我们之前看到的那种莫名其妙的类

下次你再执行反射的时候,就是直接调用这些类的方法,这是JVM的一个底层优化的机制。

看到这里,有的小伙伴是不是有点蒙?

其实这倒无所谓,这段话看的蒙丝毫不影响你进行JVM优化的

你只要记住一个结论:如果你在代码里大量用了类似上面的反射的东西,那么JVM就是会动态的去生成一些类放入Metaspace区域里的。

所以上面看到的那些奇怪的类,就是由于不停的执行反射的代码才生成的,如下图所示。

记一次隐藏很深的 JVM 线上惨案的分析、排查、解决

8、JVM创建的奇怪类有什么玄机?

那么接下来我们就很奇怪一件事情,就是JVM为什么要不停的创建那些奇怪的类然后放入Metaspace中去?

其实这就要从一个点入手来分析一下了,因为上面说的那种JVM自己创建的奇怪的类,他们的Class对象都是SoftReference,也就是软引用的。

大家可千万别说连类的Class是什么都没听说过?简单来说,每个类其实本身自己也是一个对象,就是一个Class对象,一个Class对象就代表了一个类。同时这个Class对象代表的类,可以派生出来很多实例对象。

举例来说,Class Student,这就是一个类,他本身是由一个Class类型的对象表示的。

但是如果你走一个Student student = new Student(),这就是实例化了这个Student类的一个对象,这是一个Student类型的实例对象。

所以我们这里所说的Class对象,就是JVM在发射过程中动态生成的类的Class对象,他们都是SoftReference软引用的。

所谓的软引用,最早我们再一篇文章里说过,正常情况下不会回收,但是如果内存比较紧张的时候就会回收这些对象。

那么SoftReference对象到底在GC的时候要不要回收是通过什么公式来判断的呢?

是如下的一个公式:

clock - timestamp <= freespace * SoftRefLRUPolicyMSPerMB。

这个公式的意思就是说,“clock - timestamp”代表了一个软引用对象他有多久没被访问过了,freespace代表JVM中的空闲内存空间,SoftRefLRUPolicyMSPerMB代表每一MB空闲内存空间可以允许SoftReference对象存活多久。

举个例子,假如说现在JVM创建了一大堆的奇怪的类出来,这些类本身的Class对象都是被SoftReference软引用的。

然后现在JVM里的空间内存空间有3000MB,SoftRefLRUPolicyMSPerMB的默认值是1000毫秒,那么就意味着,此时那些奇怪的SoftReference软引用的Class对象,可以存活3000 * 1000 = 3000秒,就是50分钟左右。

当然上面都是举例而已,大家都知道,一般来说发生GC时,其实JVM内部或多或少总有一些空间内存的,所以基本上如果不是快要发生OOM内存溢出了,一般软引用也不会被回收。

所以大家就知道了,按理说JVM应该会随着反射代码的执行,动态的创建一些奇怪的类,他们的Class对象都是软引用的,正常情况下不会被回收,但是也不应该快速增长才对。

9、为什么JVM创建的奇怪的类会不停的变多?

那么究竟为什么JVM创建的那些奇怪的类会不停的变多呢?

原因很简单,因为文章开头那个新手工程师不知道从哪里扒出来了SoftRefLRUPolicyMSPerMB这个JVM启动参数,他直接把这个参数设置为0了。

他想的是,一旦这个参数设置为0,任何软引用对象就可以尽快释放掉,不用留存,尽量给内存释放空间出来,这样不就可以提高内存利用效率了么?

真是想的很傻很天真。

实际上一旦这个参数设置为0之后,直接导致clock - timestamp <= freespace * SoftRefLRUPolicyMSPerMB这个公式的右半边是0,就导致所有的软引用对象,比如JVM生成的那些奇怪的Class对象,刚创建出来就可能被一次Young GC给带着立马回收掉一些。

如下图所示。

记一次隐藏很深的 JVM 线上惨案的分析、排查、解决

比如JVM好不容易给你弄出来100个奇怪的类,结果因为你瞎设置软引用的参数,导致突然一次GC就给你回收掉几十个类

接着JVM在反射代码执行的过程中,就会继续创建这种奇怪的类,在JVM的机制之下,会导致这种奇怪类越来越多。

也许下一次gc又会回收掉一些奇怪的类,但是马上JVM还会继续生成这种类,最终就会导致Metaspace区域被放满了,一旦Metaspace区域被占满了,就会触发Full GC,然后回收掉很多类,接着再次重复上述循环,如下图所示。

记一次隐藏很深的 JVM 线上惨案的分析、排查、解决

其实很多人会有一个疑问,到底为什么软引用的类因为错误的参数设置被快速回收之后,就会导致JVM不停创建更多的新的类呢?

其实大家不用去扣这里的细节,这里有大量的底层JDK源码的实现,异常复杂,要真的说清楚,得好几篇文章才能讲清楚JDK底层源码的这些细节。

大家只要记住这个结论,明白这个道理就好。

10、如何解决这个问题?

虽然底层JDK的一些实现细节我们没分析,但是大致梳理出来了一个思路,大家也很清楚问题所在和原因了

解决方案很简单。在有大量反射代码的场景下,大家只要把

  1. -XX:SoftRefLRUPolicyMSPerMB=0 

这个参数设置大一些即可,千万别让一些新手同学设置为0,可以设置个1000,2000,3000,或者5000毫秒,都可以。

提高这个数值,就是让反射过程中JVM自动创建的软引用的一些类的Class对象不要被随便回收,当时我们优化这个参数之后,就可以看到系统稳定运行了。

基本上Metaspace区域的内存占用是稳定的,不会来回大幅度波动了。

(编辑:衡阳站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

推荐文章
    热点阅读