博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JVM中Perm区持续上涨问题
阅读量:5875 次
发布时间:2019-06-19

本文共 4770 字,大约阅读时间需要 15 分钟。

hot3.png

公司一位大牛在微博上的一条,打算消化一下,毕竟今后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方法,如果常量池中已经存在一个,则返回,否则添加早常量池中。

刚开始的例子如下:

1
2
3
4
5
6
7
8
9
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掉了。

1
2
3
4
5
6
7
8
9
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());
            
}
        
}
}
1
2
3
Exception in thread 
"main" 
java.lang.OutOfMemoryError: PermGen space
    
at java.lang.String.intern(Native Method)
    
at PermTest.main(PermTest.java:
10
)

 

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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{
    
}
}
1
2
3
4
5
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信息):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import 
static 
com.sun.btrace.BTraceUtils.*;
import 
com.sun.btrace.annotations.*;
public 
class 
BtraceAll {
    
    
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方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
===========================================================================
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

 

转载于:https://my.oschina.net/stevenliuit/blog/517484

你可能感兴趣的文章
Tengine新增nginx upstream模块的使用
查看>>
多媒体工具Mediainfo
查看>>
1-小程序
查看>>
CentOS图形界面和命令行切换
查看>>
HTML5通信机制与html5地理信息定位(gps)
查看>>
Mind_Manager_2
查看>>
手动升级 Confluence - 规划你的升级
查看>>
汽车常识全面介绍 - 悬挂系统
查看>>
电子政务方向:We7.Cloud政府云门户
查看>>
虚拟机Centos7连接Internet
查看>>
ansible 基本操作(初试)
查看>>
更改tomcat的根目录路径
查看>>
51nod 1292 字符串中的最大值V2(后缀自动机)
查看>>
加快ALTER TABLE 操作速度
查看>>
学习笔记之软考数据库系统工程师教程(第一版)
查看>>
基本网络概念
查看>>
将 ASP.NET Core 2.0 项目升级至 ASP.NET Core 2.1 RC 1
查看>>
js提交图片转换为base64
查看>>
学习CodeIgniter框架之旅(二)继承自定义类
查看>>
Y2161 Hibernate第三次考试 2016年8月18日 试卷分析
查看>>