Java 程序员平时最常遇到的故障:系统OOM (二)

Metaspace区域是如何因为类太多而发生内存溢出的

Metaspace区域是如何触发内存溢出的?

好,我们通过之前的学习都知道,在启动一个JVM时是可以设置很多参数的,其中有一些参数就是专门用来设置Metaspace区域的内存大小的,大家如果有遗忘的回顾一下之前的文章即可。
如下两个参数就是用来设置Metaspace区域大小的:

-XX:MetaspaceSize=512m 
-XX:MaxMetaspaceSize=512m

我们看下图,图中我们就限定了Metaspace区域的内存大小为512m。

所以实际上来说,在一个JVM中,Metaspace区域的大小是固定的,比如512MB。
那么一旦JVM不停地加载类,加载了很多很多的类,然后Metaspace区域放满了,此时会如何?大家看下图。

大家如果还记得之前我们说过的频繁Full GC触发的几个问题,其中之一就是Metaspace区域满就会触发Full GC,Full GC会带着一块进行Old GC就是回收老年代的,也会带着回收年轻代的Young GC。
当然,Full GC的时候,必然会尝试回收Metaspace区域中的类,如下图所示。

所以一旦Metaspace区域满了,此时会触发Full GC,连带着回收Metaspace里的类。
那么什么样的类才是可以被回收的呢?
这个条件是相当的苛刻,包括不限于以下一些:比如这个类的类加载器先要被回收,比如这个类的所有对象实例都要被回收,等等。
所以一旦你的Metaspace区域满了,未必能回收掉里面很多的类
那么一旦回收不了多少类,此时你的JVM还在拼命的加载类放到Metaspace里去,你觉得此时会发生什么事情?
显而易见,一旦你尝试回收了Metaspace中的类之后发现还是没能腾出来太多空间,此时还要继续往Metaspace中塞入更多的类,直接就会引发内存溢出的问题。因为此时Metaspace区域的内存空间不够了。
一旦发生了内存溢出就说明JVM已经没办法继续运行下去了,此时可能你的系统就直接崩溃了,这就是Metaspace区域发生内存溢出的一个根本的原理。

到底什么情况下会发生Metaspace内存溢出?

平心而论,Metaspace这块区域一般很少发生内存溢出,如果发生内存溢出一般都是因为两个原因:

第一种原因,很多工程师他不懂JVM的运行原理,在上线系统的时候对Metaspace区域直接用默认的参数,即根本不设置其大小
这会导致默认的Metaspace区域可能才几十MB而已,此时对于一个稍微大型一点的系统,因为他自己有很多类,还依赖了很多外部的jar包有有很多的类,几十MB的Metaspace很容易就不够了

第二种原因,就是很多人写系统的时候会用cglib之类的技术动态生成一些类,一旦代码中没有控制好,导致你生成的类过于多的时候,就很容易把Metaspace给塞满,进而引发内存溢出

对于第一种问题,通常来说,有经验的工程师上线系统往往会设置对应的Metaspace大小,推荐的值在512MB那样,一般都是足够的。
对于第二种问题,我们下周就会用模拟代码给大家演示那种不停的生成大量的类的情况,让大家亲眼看到这种情况下是如何触发Metaspace内存溢出的。

posted @ 2020-01-09 14:17  klvchen  阅读(257)  评论(0)    收藏  举报