`

JVM中Load过多Class的分析

 
阅读更多

JVM中Load过多Class的分析  

2013-01-27 16:48:53|  分类: jvm |  标签: |字号 订阅

 
 

http://blog.colinsage.info/?p=123

 

问题描述:
     应用运行一段时候后,出现应用卡死的情况。使用jvisualvm观测JVM,发现load的class过多,可能是造成问题的原因。
场景重现:
     在测试环境,运行应用,并用jvisualvm观测。用httpclient写了个客户端,连续访问某个应用url。
PermGen的状态轨迹是31.361、31.362、31.364、31.371、…
Load Class的变化轨迹是7876、7948、7959、7961、7967、7971、7981、7985
 (图1)
 
从上图也可以看出,PermGen不断增加,loaded class 不断增加。
其中一个请求处理前后的PermGen变化对比。
PermGen
前:
 (图2)
 
后:
(图3)
dump堆内存,分析发现有大量的GeneratedMethodAccessor
 (图4)
 
测试环境,停掉应用。加上参数 -XX:+TraceClassLoading,观测控制台,发现创建了大量的大量的sun.reflect.GeneratedMethodAccessor,sun.reflect.GeneratedConstructorAccessor,GeneratedSerializationConstructorAccessor等Class,和dump分析结果相同。
结论:就是这些类导致PermGen不断增加
为什么要创建这些类呢?
问题分析:
这还要从反射的执行开始说起。
使用反射来执行方法调用,可以使用类名得到Class对象,再由方法名,参数类型(数目不定)拿到Method对象。
再调用Method的invoke()方法,即可执行方法的调用。
那么invoke()的内部是什么样的呢?
下面是其部分代码:
public Object invoke(Object obj, Object… args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
          … …
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
        return ma.invoke(obj, args);
从上可以看出方法的invoke实际上是由MethodAccessor执行的。这个MethodAccesor又是由acquireMethodAccessor()方法拿到的。
其代码如下:
private MethodAccessor acquireMethodAccessor() {
        // First check to see if one has been created yet, and take it
        // if so
        MethodAccessor tmp = null;
        if (root != null) tmp = root.getMethodAccessor();
        if (tmp != null) {
            methodAccessor = tmp;
        } else {
            // Otherwise fabricate one and propagate it up to the root
            tmp = reflectionFactory.newMethodAccessor(this);
            setMethodAccessor(tmp);
        }
        return tmp;
    }
这个MethodAccessor是由ReflectionFactory的newMethodAccessor()方法来创建的。
这个方法的内部如下所示:
    public MethodAccessor newMethodAccessor(Method method) {
checkInitted();

if (noInflation) {
return new MethodAccessorGenerator().
generateMethod(method.getDeclaringClass(),
method.getName(),
method.getParameterTypes(),
method.getReturnType(),
method.getExceptionTypes(),
method.getModifiers());
} else {
NativeMethodAccessorImpl acc =
new NativeMethodAccessorImpl(method);
DelegatingMethodAccessorImpl res =
new DelegatingMethodAccessorImpl(acc);
acc.setParent(res);
return res;
}
}

这个方法内有个变量noInflation变量,用于控制MethodAccessor的创建。如果noInflation为true,由MethodAccessorGenerator运行时生成java版的MethodAccessor,否则创建native版的MethodAccessor。
我们看一下NativeMethodAccessorImpl的invoke()内部的样子。
public Object invoke(Object obj, Object[] args)
throws IllegalArgumentException, InvocationTargetException
{
if (++numInvocations > ReflectionFactory.inflationThreshold()) {
MethodAccessorImpl acc = (MethodAccessorImpl)
new MethodAccessorGenerator().
generateMethod(method.getDeclaringClass(),
method.getName(),
method.getParameterTypes(),
method.getReturnType(),
method.getExceptionTypes(),
method.getModifiers());
parent.setDelegate(acc);
}

return invoke0(method, obj, args);
}

从上可以看出,调用次数较少的情况下执行的是invoke0.如果调用次数超过一个阀值(inflationThreshold = 15),则还是生成一个java版的MethodAccessor。
generateMethod()内部又调用的是generate()方法,该方法运行时生成了一个类字节码的字节数组。这个创建的类被classloader加载后,这些字节码会被放到方法区,所以看上去PermGen的占用空间越来越大。
为何要这样呢?据说java版的启动慢,但是执行快(编译器可以优化);native版的启动快,但是执行慢。所以hotspot的jdk做了个优化,调用次数少时用native版的,当发现调用次数多时,创建个java版的MethodAccessor替换掉native版的。(native版的可以从jvm源码中找到相应代码,java版麻烦点但是也能弄出来的)
结论:
1、PermGen的增加是正常现象,只要配置合理的PermSize\MaxPermSize即可避免PermGen Space溢出的出现。PermSize\MaxPermSize也不能设置的太大,否则会造成Full GC时间过长,也会影响应用。
2、sun.reflect.GeneratedMethodAccessor,sun.reflect.GeneratedConstructorAccessor,GeneratedSerializationConstructorAccessor等的数目与应用中类的数量和方法的数量成正比。例如GeneratedMethodAccessor最多是应用中所有method的总数。
分享到:
评论

相关推荐

    探索JVM底层奥秘ClassLoader源码分析与案例讲解

    探索JVM底层奥秘ClassLoader源码分析与案例讲解.....................................

    resin-jvm 调优

    在永久域中jvm则存储class和method对象。就配置而言,永久域是一个独立域并且不认为是堆的一部分。 下面介绍如何控制这些域的大小。可使用-Xms和-Xmx 控制整个堆的原始大小或最大值。 下面的命令是把初始大小设置...

    Java加载。jar包

    其中有个loadClass(String name, boolean resolve)方法,该方法为ClassLoader的入口点,在jdk1.2以后,loadClass方法将缺省调用findClass方法,详细内容可以参考API文档,我们编写的ClassLoader主要就是为了覆盖以上...

    Tomcat内存溢出的三种情况及解决办法分析

    Class在被 Load的时候被放入PermGen space区域,它和和存放Instance的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的APP会LOAD很多CLASS的话,就很可能出现Perm...

    c语言圣经 1.0

    学计算机,多看英文版原著对自身有帮助的,本教程为全英文.

    eclipse 内存溢出解决办法

    用于存放Class和Meta的信息,Class在被 Load的时候被放入PermGen space区域,它和和存放Instance的 Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的APP 会LOAD很多...

    【隐匿的学习笔记】JVM(1) 类加载子系统

    其实就是JVM运行的第一步 讲class文件加载的过程 这个过程分为三步 加载 链接 初始化 也就是简图中的第一步 加载load 加载都做了什么? 1.通过类的全限定名获取定义此类的二进制字节流 ----> 其实就是把磁盘上 ...

    Android代码-openrndr

    A Kotlin/JVM library for creative coding, real-time and interactive graphics. Can currently be used on Windows, macOS and Linux/x64 to create stand alone graphical applications. Usage A very basic ...

    jsp探针 ver0.1

    loadAddress(); try { java.awt.image.BufferedImage bufferedImage = new java.awt.image.BufferedImage( 10, 10, java.awt.image.BufferedImage.TYPE_INT_RGB); java.awt.Graphics2D g = bufferedImage....

    类的加载器资料.zip

    其中包含了两个资源: 1. loadClass方法d 流程图 2. Launcher的具体源码

    matlab加法运算程序代码-ml-class:斯坦福大学机器学习课程的讲座、练习和作业,在Scalala中

    中进行线性代数和绘图。 与 Matlab/Octave 的相似之处将使将课程内容从 Octave 移植到 Scalala 变得容易。 好处是可以使用通用编程语言 (Scala)、坚如磐石的生产就绪运行时环境 (JVM) 和高性能数值库 (MTJ 和 BLAS) ...

    DynamicNativeAgent:在没有工具或自定义类加载器的情况下重新定义Java类

    // Load the native in the JVMNativeAgent agent = AgentFactory . createNativeAgent(); // Create new dynamic agent// Redefine some classesagent . redefineClasses( new ClassDef ( TestClass . class, ...

    gradle-javadoc-notimestamp-error:带有-doctitle,-notimestamp和自定义doclet的Gradle 6.0 javadoc任务错误的公共再现

    SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBind

    Java面试宝典2010版

    75、描述一下JVM加载class文件的原理机制? 76、heap和stack有什么区别。 77、GC是什么? 为什么要有GC? 78、垃圾回收的优点和原理。并考虑2种回收机制。 79、垃圾回收器的基本原理是什么?垃圾回收器可以马上回收...

    汪文君高并发编程实战视频资源全集

    │ 高并发编程第二阶段53讲、ClassLoader打破双父亲委托机制,重写loadClass实战练习.mp4 │ 高并发编程第二阶段54讲、ClassLoader命名空间,运行时包,类卸载详细介绍.mp4 │ 高并发编程第二阶段55讲、线程上下文...

    汪文君高并发编程实战视频资源下载.txt

    │ 高并发编程第二阶段53讲、ClassLoader打破双父亲委托机制,重写loadClass实战练习.mp4 │ 高并发编程第二阶段54讲、ClassLoader命名空间,运行时包,类卸载详细介绍.mp4 │ 高并发编程第二阶段55讲、线程上下文...

    最新Java面试宝典pdf版

    75、描述一下JVM加载class文件的原理机制? 52 76、heap和stack有什么区别。 52 77、GC是什么? 为什么要有GC? 52 78、垃圾回收的优点和原理。并考虑2种回收机制。 52 79、垃圾回收器的基本原理是什么?垃圾回收器可以...

    Java面试笔试资料大全

    75、描述一下JVM加载class文件的原理机制? 52 76、heap和stack有什么区别。 52 77、GC是什么? 为什么要有GC? 52 78、垃圾回收的优点和原理。并考虑2种回收机制。 52 79、垃圾回收器的基本原理是什么?垃圾回收器可以...

    Java面试宝典-经典

    75、描述一下JVM加载class文件的原理机制? 52 76、heap和stack有什么区别。 52 77、GC是什么? 为什么要有GC? 52 78、垃圾回收的优点和原理。并考虑2种回收机制。 52 79、垃圾回收器的基本原理是什么?垃圾回收器可以...

Global site tag (gtag.js) - Google Analytics