Java.lang.OutOfMemoryError: PermGen space:Java反射

8

我在代码中使用Java反射,代码如下:

Method method = LogFactory.class.getDeclaredMethod("getContextClassLoader");
method.setAccessible(true);
ClassLoader classLoader = (ClassLoader)method.invoke(null);
LogFactory.release(classLoader);

我使用 jprofiler 可以看到很多类,比如这个: sun.reflect.GeneratedMethodAccessor11

每次调用都会增加这些类的数量。

sun.reflect.BootstrapConstructorAccessorImpl
sun.reflect.NativeConstructorAccessorImpl
sun.reflect.DelegatingConstructorAccessorImpl
sun.reflect.DelegatingClassLoader

我认为这就是导致PermGen空间增加的原因,如何清理这些类呢?

为什么不创建更多的Perm空间? - StarPinkER
也许我应该做更多的测试,我使用Eclipse调试代码并使用JProfiler进行附加。 - Fatkun
@JermaineXu 默认的权限空间为128MB,但一个月后就会用完。 - Fatkun
2
https://dev59.com/l2w15IYBdhLWcg3wmc8J - Perception
欢迎来到SO。 ^-^ @Fatkun - StarPinkER
@Perception 感谢您,Perception。它有效。 - Fatkun
2个回答

12

有一篇非常不错的文章讨论了反射委托类加载器中潜在的本地内存使用问题。

在使用Java反射时,JVM有两种访问被反射类信息的方法。它可以使用JNI访问器或Java字节码访问器。如果它使用Java字节码访问器,则需要有它自己的Java类和类加载器(sun/reflect/GeneratedMethodAccessor类和sun/reflect/DelegatingClassLoader)。这些类和类加载器使用本地内存。访问器字节码也可能被JIT编译,这将进一步增加本地内存的使用。如果频繁使用Java反射,这可能会导致大量的本地内存使用。JVM将首先使用JNI访问器,然后在同一类上进行某些访问之后,将更改为使用Java字节码访问器。当JVM从JNI访问器更改为字节码访问器时,称为膨胀(inflation)。幸运的是,我们可以通过一个Java属性来控制这个过程。sun.reflect.inflationThreshold属性告诉JVM使用JNI访问器的次数。如果设置为0,则始终使用JNI访问器。由于字节码访问器比JNI访问器使用更多的本地内存,如果我们看到很多的Java反射,我们将希望使用JNI访问器。为此,我们只需要将inflationThreshold属性设置为零。

如果您正在使用Oracle JVM,则只需要设置:

-Dsun.reflect.inflationThreshold=2147483647

如果您使用的是IBM JVM,那么您需要设置:

-Dsun.reflect.inflationThreshold=0
请注意,两种JVM在解释方式上存在差异。

2
-Dsun.reflect.inflationThreshold=0 将使所有调用使用 GeneratedMethodAccessor(jdk6u51 macosx mountain lion)。将其设置为一个大值(Integer.MAX_VALUE)即可。 - Han Zheng

2
如果您使用的是Oracle JVM,则只需要设置以下内容:
sun.reflect.inflationThreshold=2147483647 

如果您使用IBM JVM,则需要设置:
-Dsun.reflect.inflationThreshold=0

请注意,这两个JVM在解释方式上存在差异。
更多细节请参考: 通货膨胀系统属性 本机内存使用

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接