JVM中Perm区持续上涨问题
来源:互联网 发布:易企秀制作软件 编辑:程序博客网 时间:2024/04/30 11:14
公司一位大牛在微博上的一条,打算消化一下,毕竟今后Perm区的上涨还是有可能遇到的。“Java应用Perm区一直呈上涨趋势的原因可以用一个简单的办法排查,就是用btrace去跟踪下是什么地方在调用ClassLoader.defineClass,在大多数情况下这招都是管用的。”
(1)Perm区存放的啥信息?
Perm叫做持久代,存放了类的信息、类的静态变量、类中final类型的变量、类的方法信息,Perm是全局共享的,在一定条件下会被GC掉,方所要承载的数据超过内存区域后,就会出现OOM异常。可以通过-XX:PermSize和-XX:MaxPermSize来指定这个区域的最小值和最大值。持久代的回收主要包括两部分,废弃常量和无用的类,废弃的常量比较容易判别,没有任何String类型的对象引用这个就算可以废弃的了,但是无用的类判别比较复杂,(该类所有的实例已经被回收,JVM中没有任何类的实例;加载该类的ClassLoader被回收;该类对应的java.lang.class没有地方引用)。可以使用-verbose:class以及-XX:TraceClassLoading和-XX:TraceClassUnLoading来查看类的加载和卸载情况。
(2)ClassLoader中的defineClass是干啥的?
该方法用于将二进制的字节码转换为Class对象,对于自定义加载类非常重要,如果二进制的字节码不符合JVM的规范,就会报ClassFormatError,如果生成的类名和二进制中的不符,报NoClassDefFoundError异常,如果加载的class是受保护的,则报SecurityException,如果此类已经在ClassLoader已经加载,会报LinkageError。
例子1:运行时常量溢出
向常量池中添加数据,可以调用String.intern(),这个是native方法,如果常量池中已经存在一个,则返回,否则添加早常量池中。
刚开始的例子如下:
public
class
PermTest {
public
static
void
main(String[] args) {
int
i =
0
;
while
(
true
){
String.valueOf(i++).intern();
System.out.println(i);
}
}
}
发现没有抛出OOM异常,用JConsole查看,(PermSize和MaxPermSize都是10M)在10M的时候曲线又下去了,原来是FullGC把常量池中没有的引用GC掉了,所以没有OOM。
增加了一个List,持有这个引用,就不会被GC掉了。
public
class
PermTest {
public
static
void
main(String[] args) {
List<String> all =
new
ArrayList<String>();
int
i =
0
;
while
(
true
){
all.add(String.valueOf(i++).intern());
}
}
}
Exception in thread
"main"
java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
at PermTest.main(PermTest.java:
10
)
例子2:用cglib产生代理类,不断的创建类
import
java.lang.reflect.Method;
import
net.sf.cglib.proxy.Enhancer;
import
net.sf.cglib.proxy.MethodInterceptor;
import
net.sf.cglib.proxy.MethodProxy;
public
class
PermTest {
public
static
void
main(String[] args) {
while
(
true
){
Enhancer enhancer =
new
Enhancer();
enhancer.setSuperclass(PermTestClass.
class
);
enhancer.setUseCache(
false
);
enhancer.setCallback(
new
MethodInterceptor() {
@Override
public
Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy)
throws
Throwable {
return
proxy.invoke(obj, args);
}
});
enhancer.create();
}
}
static
class
PermTestClass{
}
}
Caused by: java.lang.OutOfMemoryError: PermGen space
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClassCond(ClassLoader.java:
631
)
at java.lang.ClassLoader.defineClass(ClassLoader.java:
615
)
...
8
more
Btrace的脚本文件如下(查看哪里调用了defineClass信息):
import
static
com.sun.btrace.BTraceUtils.*;
import
com.sun.btrace.annotations.*;
@BTrace
public
class
BtraceAll {
@TLS
private
static
long
beginTime;
@OnMethod
(
clazz=
"java.lang.ClassLoader"
,
method=
"defineClass"
)
public
static
void
traceMethodBegin(){
beginTime = timeMillis();
}
@OnMethod
(
clazz=
"java.lang.ClassLoader"
,
method=
"defineClass"
,
location=
@Location
(Kind.RETURN)
)
public
static
void
traceMethdReturn(
@Return
String result,
@ProbeClassName
String clazzName,
@ProbeMethodName
String methodName){
println(
"==========================================================================="
);
println(strcat(strcat(clazzName,
"."
), methodName));
println(strcat(
"Time taken : "
, str(timeMillis() - beginTime)));
println(
"java thread method trace:---------------------------------------------------"
);
jstack();
println(
"----------------------------------------------------------------------------"
);
println(strcat(
"Reuslt :"
,str(result)));
println(
"============================================================================"
);
}
}
运行后,发现cglib在创建代理类的时候,确实调用了defineClass方法
===========================================================================
java.lang.ClassLoader.defineClass
Time taken :
79
java thread method trace:---------------------------------------------------
java.lang.ClassLoader.defineClass(ClassLoader.java:
615
)
sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
25
)
java.lang.reflect.Method.invoke(Method.java:
597
)
net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:
384
)
net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:
219
)
net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:
377
)
net.sf.cglib.proxy.Enhancer.create(Enhancer.java:
285
)
PermTest.main(PermTest.java:
19
)
----------------------------------------------------------------------------
Reuslt :
class
PermTest$PermTestClass$$EnhancerByCGLIB$$1ed16944_8
============================================================================
至此,问题场景以及如何排查,清晰了。。。
参考书籍:
1、分布式java应用基础
2、深入理解JVM
- JVM中Perm区持续上涨问题
- JVM中Perm区持续上涨问题
- JVM中Perm区持续上涨问题
- JVM中Perm区持续上涨问题
- JVM中Perm区持续上涨问题
- JVM的Perm区持续增长导致OOM问题记录
- 房价上涨难以动摇,2016年持续上涨的局面。
- 房价上涨还会持续下去吗?
- 国际市场不断萎缩,经营成本持续上涨
- mongo 3.0连接数持续上涨
- perm
- Perm
- HELLOWIN的问题,持续中
- 专家撰文:有理由相信股市上涨可以持续
- JVM性能调优总结(持续更新中)
- jvm一点内存的知识(持续积累中)
- jvm中一些有用的系统变量(持续更新)
- JVM划分Eden Space、Survivor Space、Tenured Gen,Perm Gen
- Java程序优化的一些最佳实践
- 进程,服务,端口
- iOS学习笔记其2-基本运算与程序控制流程
- zoj2674 Strange Limit 欧拉函数的应用
- 利用Android的Canvas绘制正弦函数图像
- JVM中Perm区持续上涨问题
- MFC About Time
- PowerDesigner使用教程 —— 概念数据模型
- 浅谈server端基本的设计模型及部分问题
- 解决eclipse无法启动
- Java 理论与实践: 用弱引用堵住内存泄漏
- 机器分配
- 【好东西一定要转】关于Solaris安全配置的转贴和讨论
- Mysql show indexes 查看索引状态