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变化对比。
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();
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);
}
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则存储class和method对象。就配置而言,永久域是一个独立域并且不认为是堆的一部分。 下面介绍如何控制这些域的大小。可使用-Xms和-Xmx 控制整个堆的原始大小或最大值。 下面的命令是把初始大小设置...
其中有个loadClass(String name, boolean resolve)方法,该方法为ClassLoader的入口点,在jdk1.2以后,loadClass方法将缺省调用findClass方法,详细内容可以参考API文档,我们编写的ClassLoader主要就是为了覆盖以上...
Class在被 Load的时候被放入PermGen space区域,它和和存放Instance的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的APP会LOAD很多CLASS的话,就很可能出现Perm...
学计算机,多看英文版原著对自身有帮助的,本教程为全英文.
用于存放Class和Meta的信息,Class在被 Load的时候被放入PermGen space区域,它和和存放Instance的 Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的APP 会LOAD很多...
其实就是JVM运行的第一步 讲class文件加载的过程 这个过程分为三步 加载 链接 初始化 也就是简图中的第一步 加载load 加载都做了什么? 1.通过类的全限定名获取定义此类的二进制字节流 ----> 其实就是把磁盘上 ...
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 ...
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....
其中包含了两个资源: 1. loadClass方法d 流程图 2. Launcher的具体源码
中进行线性代数和绘图。 与 Matlab/Octave 的相似之处将使将课程内容从 Octave 移植到 Scalala 变得容易。 好处是可以使用通用编程语言 (Scala)、坚如磐石的生产就绪运行时环境 (JVM) 和高性能数值库 (MTJ 和 BLAS) ...
// Load the native in the JVMNativeAgent agent = AgentFactory . createNativeAgent(); // Create new dynamic agent// Redefine some classesagent . redefineClasses( new ClassDef ( TestClass . class, ...
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
75、描述一下JVM加载class文件的原理机制? 76、heap和stack有什么区别。 77、GC是什么? 为什么要有GC? 78、垃圾回收的优点和原理。并考虑2种回收机制。 79、垃圾回收器的基本原理是什么?垃圾回收器可以马上回收...
│ 高并发编程第二阶段53讲、ClassLoader打破双父亲委托机制,重写loadClass实战练习.mp4 │ 高并发编程第二阶段54讲、ClassLoader命名空间,运行时包,类卸载详细介绍.mp4 │ 高并发编程第二阶段55讲、线程上下文...
│ 高并发编程第二阶段53讲、ClassLoader打破双父亲委托机制,重写loadClass实战练习.mp4 │ 高并发编程第二阶段54讲、ClassLoader命名空间,运行时包,类卸载详细介绍.mp4 │ 高并发编程第二阶段55讲、线程上下文...
75、描述一下JVM加载class文件的原理机制? 52 76、heap和stack有什么区别。 52 77、GC是什么? 为什么要有GC? 52 78、垃圾回收的优点和原理。并考虑2种回收机制。 52 79、垃圾回收器的基本原理是什么?垃圾回收器可以...
75、描述一下JVM加载class文件的原理机制? 52 76、heap和stack有什么区别。 52 77、GC是什么? 为什么要有GC? 52 78、垃圾回收的优点和原理。并考虑2种回收机制。 52 79、垃圾回收器的基本原理是什么?垃圾回收器可以...
75、描述一下JVM加载class文件的原理机制? 52 76、heap和stack有什么区别。 52 77、GC是什么? 为什么要有GC? 52 78、垃圾回收的优点和原理。并考虑2种回收机制。 52 79、垃圾回收器的基本原理是什么?垃圾回收器可以...