java反射引起的java.lang.OutOfMemoryError: PermGen space

由于之前为了不改hive的代码,用了反射的方式动态修改,导致了另一个问题出现。
我用的反射代码如下:(代码是针对local metastore写的), 主要是为了调用ObjectStore的shutdown()方法。

            Hive hive = Hive.get();
            Method method = Hive.class.getDeclaredMethod("getMSC");
            method.setAccessible(true);
            HiveMetaStoreClient msc = (HiveMetaStoreClient)method.invoke(hive);
            Field field = HiveMetaStoreClient.class.getDeclaredField("client");
            field.setAccessible(true);
            HiveMetaStore.HMSHandler client = (HiveMetaStore.HMSHandler)field.get(msc);
            
            if (client != null) {
                method = HiveMetaStore.HMSHandler.class.getDeclaredMethod("getMS", boolean.class);
                method.setAccessible(true);
                ObjectStore os = (ObjectStore)method.invoke(client, false);
                os.shutdown();
            }
            method = null;
            field = null;
            
            Hive.closeCurrent();

使用反射后,用jprofiler检查发现多了很多这些类
sun.reflect.DelegatingClassLoader
sun.reflect.GeneratedConstructorAccessor77 估计是反射引起的,类越來越多(还没有严谨的测试过,因为perm内存增长很缓慢)
看了几篇文章,没搞懂。。到StackOverflow去问 使用这个参数,通过jni的方式调用反射

-Dsun.reflect.inflationThreshold=0

我理解是使用反射会有两种途径,jni和Java bytecode,(It can use a JNI accessor, or a Java bytecode accessor),使用jni调用会慢一些,但使用Java bytecode会生成一个类和classloader (sun/reflect/GeneratedMethodAccessor class and sun/reflect/DelegatingClassLoader)。JVM刚开始默认使用JNI的方式调用,当同一个类调用次数达到一定值后改为Java bytecode调用。 可能会有少许的性能缺失,但我调用的次数还不算多,应该可以接受。另外发现在线上的对象个数有150个左右,应该有些内存还是被回收了,调用的次数不止那么少。

参考

StackOverflow
Potential native memory use in reflection delegating classloaders

updatedupdated2023-12-062023-12-06