【java】JVM中Perm区持续上涨问题

x33g5p2x  于2022-02-14 转载在 Java  
字(4.3k)|赞(0)|评价(0)|浏览(316)

1.概述

转载:JVM中Perm区持续上涨问题

Java应用Perm区一直呈上涨趋势的原因可以用一个简单的办法排查,就是用btrace去跟踪下是什么地方在调用ClassLoader.defineClass,在大多数情况下这招都是管用的。

Perm区存放的啥信息?

Perm叫做持久代,存放了类的信息、类的静态变量、类中final类型的变量、类的方法信息,Perm是全局共享的,在一定条件下会被GC掉,方所要承载的数据超过内存区域后,就会出现OOM异常。可以通过-XX:PermSize和-XX:MaxPermSize来指定这个区域的最小值和最大值。持久代的回收主要包括两部分,废弃常量和无用的类,废弃的常量比较容易判别,没有任何String类型的对象引用这个就算可以废弃的了,但是无用的类判别比较复杂,(该类所有的实例已经被回收,JVM中没有任何类的实例;加载该类的ClassLoader被回收;该类对应的java.lang.Class没有地方引用)。可以使用-verbose:class以及-XX:TraceClassLoading和-XX:TraceClassUnLoading来查看类的加载和卸载情况。

ClassLoader中的defineClass是干啥的?

该方法用于将二进制的字节码转换为Class对象,对于自定义加载类非常重要,如果二进制的字节码不符合JVM的规范,就会报ClassFormatError,如果生成的类名和二进制中的不符,报NoClassDefFoundError异常,如果加载的class是受保护的,则报SecurityException,如果此类已经在ClassLoader已经加载,会报LinkageError。

例子1:运行时常量溢出

向常量池中添加数据,可以调用String.intern(),这个是native方法,如果常量池中已经存在一个,则返回,否则添加到常量池中。

刚开始的例子如下:

  1. public class PermTest {
  2. public static void main(String[] args) {
  3. int i = 0;
  4. while (true) {
  5. String.valueOf(i++).intern();
  6. System.out.println(i);
  7. }
  8. }
  9. }

发现没有抛出OOM异常,用JConsole查看,(PermSize和MaxPermSize都是10M)在10M的时候曲线又下去了,原来是FullGC把常量池中没有的引用GC掉了,所以没有OOM。

增加了一个List,持有这个引用,就不会被GC掉了。

  1. public class PermTest {
  2. public static void main(String[] args) {
  3. List<String> all = new ArrayList<String>();
  4. int i = 0;
  5. while (true) {
  6. all.add(String.valueOf(i++).intern());
  7. }
  8. }
  9. }
  10. Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
  11. at java.lang.String.intern(Native Method)
  12. at PermTest.main(PermTest.java: 10 )

例子2:用cglib产生代理类,不断的创建类

  1. import java.lang.reflect.Method;
  2. import net.sf.cglib.proxy.Enhancer;
  3. import net.sf.cglib.proxy.MethodInterceptor;
  4. import net.sf.cglib.proxy.MethodProxy;
  5. public class PermTest {
  6. public static void main(String[] args) {
  7. while (true) {
  8. Enhancer enhancer = new Enhancer();
  9. enhancer.setSuperclass(PermTestClass. class);
  10. enhancer.setUseCache(false);
  11. enhancer.setCallback(new MethodInterceptor() {
  12. @Override
  13. public Object intercept(Object obj, Method method, Object[] args,
  14. MethodProxy proxy) throws Throwable {
  15. return proxy.invoke(obj, args);
  16. }
  17. });
  18. enhancer.create();
  19. }
  20. }
  21. static class PermTestClass {
  22. }
  23. }
  24. Caused by: java.lang.OutOfMemoryError: PermGen space
  25. at java.lang.ClassLoader.defineClass1(Native Method)
  26. at java.lang.ClassLoader.defineClassCond(ClassLoader.java: 631)
  27. at java.lang.ClassLoader.defineClass(ClassLoader.java: 615)
  28. ... 8 more

Btrace的脚本文件如下(查看哪里调用了defineClass信息):

  1. import static com.sun.btrace.BTraceUtils.*;
  2. import com.sun.btrace.annotations.*;
  3. @BTrace
  4. public class BtraceAll {
  5. @TLS
  6. private static long beginTime;
  7. @OnMethod (
  8. clazz= "java.lang.ClassLoader",
  9. method= "defineClass"
  10. )
  11. public static void traceMethodBegin() {
  12. beginTime = timeMillis();
  13. }
  14. @OnMethod (
  15. clazz= "java.lang.ClassLoader",
  16. method= "defineClass",
  17. location= @Location(Kind.RETURN)
  18. )
  19. public static void traceMethdReturn(
  20. @Return String result,
  21. @ProbeClassName String clazzName,
  22. @ProbeMethodName String methodName) {
  23. println("===========================================================================");
  24. println(strcat(strcat(clazzName, "."), methodName));
  25. println(strcat("Time taken : ", str(timeMillis() - beginTime)));
  26. println("java thread method trace:---------------------------------------------------");
  27. jstack();
  28. println("----------------------------------------------------------------------------");
  29. println(strcat("Reuslt :", str(result)));
  30. println("============================================================================");
  31. }
  32. }

运行后,发现cglib在创建代理类的时候,确实调用了defineClass方法

  1. ===========================================================================
  2. java.lang.ClassLoader.defineClass
  3. Time taken : 79
  4. java thread method trace:---------------------------------------------------
  5. java.lang.ClassLoader.defineClass(ClassLoader.java: 615)
  6. sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
  7. sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java: 25)
  8. java.lang.reflect.Method.invoke(Method.java: 597)
  9. net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java: 384 )
  10. net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java: 219)
  11. net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java: 377)
  12. net.sf.cglib.proxy.Enhancer.create(Enhancer.java: 285)
  13. PermTest.main(PermTest.java: 19)
  14. ----------------------------------------------------------------------------
  15. Reuslt : class PermTest$PermTestClass$$EnhancerByCGLIB$$1ed16944_8
  16. ============================================================================

至此,问题场景以及如何排查,清晰了。。。

相关文章

最新文章

更多