Cglib 动态代理,会动态创建类 如果使用不当会导致生成大量的类元数据 塞满metaspace

试验代码

依赖包

  <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
  </dependency>
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * 模拟元数据区被塞满导致oom
 *
 * @author holysu 2020/8/11
 */
public class MetaspaceOOM {

    /**
     -XX:+UseParNewGC
     -XX:+UseConcMarkSweepGC
     -XX:MetaspaceSize=10M
     -XX:MaxMetaspaceSize=10M
     -XX:+PrintGCDetails
     -Xloggc:gc.log
     -XX:+HeapDumpOnOutOfMemoryError
     -XX:HeapDumpPath=./
     * @param args
     */
    public static void main(String[] args) {
        int counter = 0;
        while (true) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(Car.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    if (method.getName().equals("run")) {
                        System.out.println("running, enhanced");
                        return methodProxy.invokeSuper(o, objects);
                    } else {
                        return methodProxy.invokeSuper(o, objects);
                    }
                }
            });

            Car car = (Car) enhancer.create();
            car.run();

            System.out.println("目前创建了 " + (counter++) + " 个Car的动态子类");
        }
    }

    static class Car {
        public void run() {
            System.out.println("running...");
        }
    }
}

异常信息

执行报错
java.lang.OutOfMemoryError: Metaspace
Dumping heap to ./\java_pid13748.hprof ...
Heap dump file created [2923143 bytes in 0.037 secs]
Exception in thread "main" net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:348)
at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:117)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:294)
at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)
at MetaspaceOOM.main(MetaspaceOOM.java:42)
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:459)
at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:339)
... 6 more
Caused by: java.lang.OutOfMemoryError: Metaspace
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
... 11 more

gc日志

4.669: [GC (Allocation Failure) 4.669: [ParNew: 34944K->2104K(39296K), 0.0033046 secs] 34944K->2104K(126720K), 0.0035844 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
5.218: [GC (Allocation Failure) 5.218: [ParNew: 37048K->2256K(39296K), 0.0051226 secs] 37048K->2256K(126720K), 0.0053311 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
5.633: [GC (Allocation Failure) 5.633: [ParNew: 37200K->2664K(39296K), 0.0101116 secs] 37200K->2664K(126720K), 0.0102989 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]

Gc日志还是比较清晰的打印出 Metadata 到达阈值引起的fullgc
5.973: [Full GC (Metadata GC Threshold)5.973: [CMS: 0K->2302K(87424K), 0.0538876 secs] 28792K->2302K(126720K), [Metaspace: 9186K->9186K(1058816K)], 0.0543441 secs] [Times: user=0.08 sys=0.03, real=0.06 secs]
6.027: [Full GC (Last ditch collection) 6.027: [CMS: 2302K->1410K(87424K), 0.0168118 secs] 2302K->1410K(126848K), [Metaspace: 9186K->9186K(1058816K)], 0.0169643 secs] [Times: user=0.03 sys=0.00, real=0.02 secs]
6.046: [GC (CMS Initial Mark) [1 CMS-initial-mark: 1410K(87424K)] 1410K(126848K), 0.0003738 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
6.046: [CMS-concurrent-mark-start]
6.089: [CMS-concurrent-mark: 0.014/0.043 secs] [Times: user=0.06 sys=0.00, real=0.04 secs]
6.089: [CMS-concurrent-preclean-start]
6.091: [CMS-concurrent-preclean: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
6.091: [GC (CMS Final Remark) [YG occupancy: 687 K (39424 K)]6.091: [Rescan (parallel) , 0.0003632 secs]6.091: [weak refs processing, 0.0001684 secs]6.091: [class unloading, 0.0022085 secs]6.094: [scrub symbol table, 0.0013801 secs]6.095: [scrub string table, 0.0003217 secs][1 CMS-remark: 1410K(87424K)] 2097K(126848K), 0.0048304 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
6.096: [CMS-concurrent-sweep-start]
6.096: [CMS-concurrent-sweep: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
6.097: [CMS-concurrent-reset-start]
6.099: [CMS-concurrent-reset: 0.002/0.002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
par new generation total 39424K, used 1038K [0x0000000081200000, 0x0000000083cc0000, 0x0000000095ec0000)
eden space 35072K, 2% used [0x0000000081200000, 0x0000000081303890, 0x0000000083440000)
from space 4352K, 0% used [0x0000000083440000, 0x0000000083440000, 0x0000000083880000)
to space 4352K, 0% used [0x0000000083880000, 0x0000000083880000, 0x0000000083cc0000)
concurrent mark-sweep generation total 87424K, used 1410K [0x0000000095ec0000, 0x000000009b420000, 0x0000000100000000)
Metaspace used 9214K, capacity 10134K, committed 10240K, reserved 1058816K
class space used 790K, capacity 841K, committed 896K, reserved 1048576K

到底是什么东西在元数据?

通过mat分析oom时候自动dump的文件

首先发现应用程序类加载器 AppClassLoader 加载了很多东西
实验: metaspace区域OOM发现有大量 CGLib 相关的类以及 生成的Car类 class MetaspaceOOM\(Car\)\(EnhancerByCGLib\)xxx

实验: metaspace区域OOM

cglib 总共动态生成了 668个类
实验: metaspace区域OOM list outgoing objects 进去看,这个名字怪怪的类 父类是 Car, 关联了一堆 cglib的代理对象 也就是说肯定是通过cglib加强 car 的时候有问题
实验: metaspace区域OOM

相关文章: