(1) Metaspace 区域发生内存溢出的原理

  • 一旦 JVM 不停加载类,加载了很多很多的类,然后 Metaspace 区域放满了;
  • 一旦 Metaspace 区域满了,会触发 Full GC,连带着回收 Metaspace 里的类;
    • 类要满足被回收的条件,是相当苛刻的,比如:这个类的类加载器先要被回收,比如:这个类的所有对象实例都要被回收,等等;
  • 一旦回收了 Metaspace 中的类之后,发现还是没能腾出太多空间,仍然要往 Metaspace 中放入更多的类,进入导致内存溢出的问题,因为 Metaspace 空间的内存空间不够;

image.png

(2) 两种常见的触发 Metaspace 内存溢出的场景

  • Metaspace 直接使用默认参数,根本不设置其大小,进而导致默认的 Metaspace 可能才几十MB而已,对大一点的系统而言根本不够用,推荐值 512MB,一般足够的;
  • 系统中太多使用 cglib 之类的技术动态生成一些类,一旦代码中没有控制好,导致生成的类过于多的时候,很容易把 Metaspace 塞满,进而引发内存溢出;

(3) 模拟 Metaspace 内存溢出的场景

  • JVM 参数

    1. -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
  • 一段 CGLIB 动态生成类的代码示例

    • 添加依赖 cglib

      <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>3.3.0</version>
      </dependency>
      
      public class Demo1 {
      public static void main(String[] args) {
         long counter = 0;
         while (true){
             System.out.println("目前创建了"+(++counter)+"个Car类的子类");
             Enhancer enhancer = new Enhancer();
             enhancer.setSuperclass(Car.class);
             enhancer.setUseCache(false);
             enhancer.setCallback(new MethodInterceptor() {
                 @Override
                 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                     if(method.getName().equals("run")){
                         System.out.println("汽车启动之前,先进行自动的安全检查....");
                         return methodProxy.invokeSuper(o, objects);
                     }else {
                         return methodProxy.invokeSuper(o, objects);
                     }
      
                 }
             });
             Car car = (Car) enhancer.create();
             car.run();
         }
      }
      static class Car{
         public void run(){
             System.out.println("汽车启动,开始行驶....");
         }
      }
      }
      
  • 以上代码相当于手动创建 Car 的子类:

    public lass SageCar extends Car {
      public void run(){
          System.out.println("汽车启动之前,先进行自动的安全检查....");
          super.run();
      }
    }
    
  • 异常日志:

    目前创建了260个Car类的子类
    Exception in thread "main" net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
    ...
    Caused by: java.lang.OutOfMemoryError: Metaspace
      at java.lang.ClassLoader.defineClass1(Native Method)
      at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
      ... 11 more