文档章节

JVM中Perm区持续上涨问题

stevenliu
 stevenliu
发布于 2015/10/15 12:53
字数 1017
阅读 103
收藏 6

公司一位大牛在微博上的一条,打算消化一下,毕竟今后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 {
     @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方法

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

 

本文转载自:http://blog.csdn.net/wisgood/article/details/16344885

stevenliu

stevenliu

粉丝 60
博文 33
码字总数 11069
作品 0
昌平
程序员
私信 提问
加载中

评论(2)

stevenliu
stevenliu 博主

引用来自“tuoniao”的评论

牛+1
tuoniao
tuoniao
JVM内存模型以及垃圾回收

JVM内存模型以及垃圾回收 JAVA堆的描述如下: 内存由 Perm 和 Heap 组成. 其中 Heap = {Old + NEW = { Eden , from, to } } JVM内存模型中分两大块,一块是 NEW Generation, 另一块是Old Gen...

晨曦之光
2012/04/25
572
0
JVM的方法区和永久带是什么关系?

群里面有小伙伴问到这个问题,说在网上看了很多文章,但是还是没弄明白这俩是啥关系,下面我们就来详细的解释一下: 什么是方法区? 方法区(Method Area)是jvm规范里面的运行时数据区的一个...

若鱼1919
2018/07/26
0
0
java虚拟机内存监控工具jps,jinfo,Jstack,jstat,jmap,jhat使用

基于Sun HotSpot JVM 这里将介绍几款sun hotspot jvm 自带的监控工具: 请确保java_home/bin配置到path环境变量下,因为这些工具都在jdk的bin目录下 jps(JVM Process Status Tool):JVM机进程...

我类个擦
2014/09/12
6.9K
1
Android 之 内存管理

概述 在android的开发中,要时刻主要内存的分配和垃圾回收,因为系统为每一个dalvik虚拟机分配的内存是有限的,在google的G1中,分配的最大堆大小只有16M,后来的机器一般都为24M,实在是少的...

等待流星
2014/03/08
66
0
优化JVM参数提升Eclipse运行速度

首先建立评估体系,将workspace里所有的项目close掉,关闭eclipse。优化的用例就是启动eclipse,open一个项目,eclipse会自动build这个项目,保证没有感觉到明显的卡,也就是没有full GC。 ...

紫鹰王
2012/11/01
429
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周日乱弹 —— 别问,问就是没空

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @tom_tdhzz :#今日歌曲推荐# 分享容祖儿/彭羚的单曲《心淡》: 《心淡》- 容祖儿/彭羚 手机党少年们想听歌,请使劲儿戳(这里) @wqp0010 :周...

小小编辑
今天
168
4
golang微服务框架go-micro 入门笔记2.1 micro工具之micro api

micro api micro 功能非常强大,本文将详细阐述micro api 命令行的功能 重要的事情说3次 本文全部代码https://idea.techidea8.com/open/idea.shtml?id=6 本文全部代码https://idea.techidea8....

非正式解决方案
今天
5
0
Spring Context 你真的懂了吗

今天介绍一下大家常见的一个单词 context 应该怎么去理解,正确的理解它有助于我们学习 spring 以及计算机系统中的其他知识。 1. context 是什么 我们经常在编程中见到 context 这个单词,当...

Java知其所以然
昨天
5
0
Spring Boot + Mybatis-Plus 集成与使用(二)

前言: 本章节介绍MyBatis-Puls的CRUD使用。在开始之前,先简单讲解下上章节关于Spring Boot是如何自动配置MyBatis-Plus。 一、自动配置 当Spring Boot应用从主方法main()启动后,首先加载S...

伴学编程
昨天
8
0
用最通俗的方法讲spring [一] ──── AOP

@[TOC](用最通俗的方法讲spring [一] ──── AOP) 写这个系列的目的(可以跳过不看) 自己写这个系列的目的,是因为自己是个比较笨的人,我曾一度怀疑自己的智商不适合干编程这个行业.因为在我...

小贼贼子
昨天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部