文档章节

性能分析工具之-- Eclipse Memory Analyzer tool(MAT)(三)

a
 allantaylor81
发布于 2015/07/26 09:05
字数 1740
阅读 16
收藏 0

继 性能分析工具之-- Eclipse Memory Analyzer tool(MAT)(一)性能分析工具之-- Eclipse Memory Analyzer tool(MAT)(二)两篇文章之后,接下来该讲述Perm gen引起的内存泄露问题的分析过程。

perm gen


我们在上2篇文章中知道,perm gen是个异类,里面存储了类和方法数据(与class loader有关)以及interned strings(字符串驻留)。在heap dump中没有包含太多的perm gen信息。那么我们就用这些少量的信息来解决问题吧。

看下面的代码,利用interned strings把perm gen撑破了。

/**
 * OOMPermTest class
 * 
@author  rosen jiang
 
*/
package org.rosenjiang.test;

public class OOMPermTest {
    
public static void main(String[] args){
        oom();
    }
    
    
private static void oom(){
        Object[] array 
= new Object[10000000];
        
for(int i=0; i<10000000; i++){
            String d 
= String.valueOf(i).intern();
            array[i]
=d;
        }
    }
}


控制台打印如下的信息,然后把java_pid1824.hprof文件导入到MAT。其实在MAT里,看到的状况应该和“OutOfMemoryError: Java heap space”差不多(用了数组),因为heap dump并没有包含interned strings方面的任何信息。只是在这里需要强调,使用intern()方法的时候应该多加注意。

java.lang.OutOfMemoryError: PermGen space
Dumping heap to java_pid1824.hprof 
Heap dump file created 
[121273334 bytes in 2.845 secs]
Exception in thread 
"main" java.lang.OutOfMemoryError: PermGen space



倒是在思考如何把class loader撑破废了些心思。经过尝试,发现使用ASM来动态生成类才能达到目的。ASM(http://asm.objectweb.org)的主要作用是处理已编译类(compiled class),能对已编译类进行生成、转换、分析(功能之一是实现动态代理),而且它运行起来足够的快和小巧,文档也全面,实属居家必备之良品。ASM提供了core API和tree API,前者是基于事件的方式,后者是基于对象的方式,类似于XML的SAX、DOM解析,但是使用tree API性能会有损失。既然下面要用到ASM,这里不得不啰嗦下已编译类的结构,包括:
    1、修饰符(例如public、private)、类名、父类名、接口和annotation部分。
    2、类成员变量声明,包括每个成员的修饰符、名字、类型和annotation。
    3、方法和构造函数描述,包括修饰符、名字、返回和传入参数类型,以及annotation。当然还包括这些方法或构造函数的具体Java字节码。
    4、常量池(constant pool)部分,constant pool是一个包含类中出现的数字、字符串、类型常量的数组。



已编译类和原来的类源码区别在于,已编译类只包含类本身,内部类不会在已编译类中出现,而是生成另外一个已编译类文件;其二,已编译类中没有注释;其三,已编译类没有package和import部分。
这里还得说说已编译类对Java类型的描述,对于原始类型由单个大写字母表示,Z代表boolean、C代表char、B代表byte、S代表short、I代表int、F代表float、J代表long、D代表double;而对类类型的描述使用内部名(internal name)外加前缀L和后面的分号共同表示来表示,所谓内部名就是带全包路径的表示法,例如String的内部名是java/lang/String;对于数组类型,使用单方括号加上数据元素类型的方式描述。最后对于方法的描述,用圆括号来表示,如果返回是void用V表示,具体参考下图。



下面的代码中会使用ASM core API,注意接口ClassVisitor是核心,FieldVisitor、MethodVisitor都是辅助接口。ClassVisitor应该按照这样的方式来调用:visit visitSource? visitOuterClass? ( visitAnnotation | visitAttribute )*( visitInnerClass | visitField | visitMethod )* visitEnd。就是说visit方法必须首先调用,再调用最多一次的visitSource,再调用最多一次的visitOuterClass方法,接下来再多次调用visitAnnotation和visitAttribute方法,最后是多次调用visitInnerClass、visitField和visitMethod方法。调用完后再调用visitEnd方法作为结尾。

注意ClassWriter类,该类实现了ClassVisitor接口,通过toByteArray方法可以把已编译类直接构建成二进制形式。由于我们要动态生成子类,所以这里只对ClassWriter感兴趣。首先是抽象类原型:

/**
 * 
@author  rosen jiang
 * MyAbsClass class
 
*/
package org.rosenjiang.test;

public abstract class MyAbsClass {
    
int LESS = -1;
    
int EQUAL = 0;
    
int GREATER = 1;
    
abstract int absTo(Object o);
}


其次是自定义类加载器,实在没法,ClassLoader的defineClass方法都是protected的,要加载字节数组形式(因为toByteArray了)的类只有继承一下自己再实现。

/**
 * 
@author  rosen jiang
 * MyClassLoader class
 
*/
package org.rosenjiang.test;

public class MyClassLoader extends ClassLoader {
    
public Class defineClass(String name, byte[] b) {
        
return defineClass(name, b, 0, b.length);
    }
}


最后是测试类。

/**
 * 
@author  rosen jiang
 * OOMPermTest class
 
*/
package org.rosenjiang.test;

import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;

public class OOMPermTest {
    
public static void main(String[] args) {
        OOMPermTest o 
= new OOMPermTest();
        o.oom();
    }

    
private void oom() {
        
try {
            ClassWriter cw 
= new ClassWriter(0);
            cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC 
+ Opcodes.ACC_ABSTRACT,
            
"org/rosenjiang/test/MyAbsClass"null"java/lang/Object",
            
new String[] {});
            cw.visitField(Opcodes.ACC_PUBLIC 
+ Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "LESS""I",
            
nullnew Integer(-1)).visitEnd();
            cw.visitField(Opcodes.ACC_PUBLIC 
+ Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "EQUAL""I",
            
nullnew Integer(0)).visitEnd();
            cw.visitField(Opcodes.ACC_PUBLIC 
+ Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "GREATER""I",
            
nullnew Integer(1)).visitEnd();
            cw.visitMethod(Opcodes.ACC_PUBLIC 
+ Opcodes.ACC_ABSTRACT, "absTo",
            
"(Ljava/lang/Object;)I"nullnull).visitEnd();
            cw.visitEnd();
            
byte[] b = cw.toByteArray();

            List
<ClassLoader> classLoaders = new ArrayList<ClassLoader>();
            
while (true) {
                MyClassLoader classLoader 
= new MyClassLoader();
                classLoader.defineClass(
"org.rosenjiang.test.MyAbsClass", b);
                classLoaders.add(classLoader);
            }
        } 
catch (Exception e) {
            e.printStackTrace();
        }
    }
}


不一会儿,控制台就报错了。

java.lang.OutOfMemoryError: PermGen space
Dumping heap to java_pid3023.hprof 
Heap dump file created 
[92593641 bytes in 2.405 secs]
Exception in thread 
"main" java.lang.OutOfMemoryError: PermGen space


打开java_pid3023.hprof文件,注意看下图的Classes: 88.1k和Class Loader: 87.7k部分,从这点可看出class loader加载了大量的类。



更进一步分析,点击上图中红框线圈起来的按钮,选择Java Basics——Class Loader Explorer功能。打开后能看到下图所示的界面,第一列是class loader名字;第二列是class loader已定义类(defined classes)的个数,这里要说一下已定义类和已加载类(loaded classes)了,当需要加载类的时候,相应的class loader会首先把请求委派给父class loader,只有当父class loader加载失败后,该class loader才会自己定义并加载类,这就是Java自己的“双亲委派加载链”结构;第三列是class loader所加载的类的实例数目。



在Class Loader Explorer这里,能发现class loader是否加载了过多的类。另外,还有Duplicate Classes功能,也能协助分析重复加载的类,在此就不再截图了,可以肯定的是MyAbsClass被重复加载了N多次。

最后

其实MAT工具非常的强大,上面故弄玄虚的范例代码根本用不上MAT的其他分析功能,所以就不再描述了。其实对于OOM不只我列举的两种溢出错误,还有多种其他错误,但我想说的是,对于perm gen,如果实在找不出问题所在,建议使用JVM的-verbose参数,该参数会在后台打印出日志,可以用来查看哪个class loader加载了什么类,例:“[Loaded org.rosenjiang.test.MyAbsClass from org.rosenjiang.test.MyClassLoader]”。
全文完。


参考资料

memoryanalyzer Blog
java类加载器体系结构
ClassLoader

本文转载自:http://blog.csdn.net/rachel_luo/article/details/8992720

a
粉丝 2
博文 120
码字总数 2912
作品 0
东城
私信 提问
Eclipse中安装MemoryAnalyzer插件及使用

Eclipse中安装MemoryAnalyzer插件 一、简介   Eclipse作为JAVA非常好用的一款IDE,其自带的可扩展插件非常有利于JAVA程序员的工作效率提升。   MemoryAnalyzerTool(也叫MAT)是一款JAVA虚...

gdy
2017/08/02
0
0
DDMS的使用、内存溢出的调试和模拟器的启动命令参数

http://blog.csdn.net/qeqeqe236/article/details/7338608 1. 如果是eclipse自动生成的.hprof文件,可以使用MAT插件直接打开(可能是比较新的ADT才支持); 2. 如果eclipse自动生成的.hprof文...

塔塔米
2014/02/28
0
0
[Android]App 内存泄漏检查工具MAT

Android App发生内存泄漏,常见的有Bitmap 使用后沒有recycle(),Drawable 使用后沒有setCallback(null)等。 Eclipse 有个插件工具MAT(Memory Analyzer Tool)可以帮助定位内存泄漏的对象。 ...

清水湾2012
2013/08/01
0
1
Java监控和分析--Memory Analyzer

Memory Analyzer (Eclipse MAT)是一个跨平台的开源工具,您不仅可以用它来分析内存问题,也可以用来监控整个 Java 应用程序的状态和行为。通过读取应用程序运行时由 Java 运行时环境生成的转...

匿名
2011/07/05
14.2K
0
使用 Eclipse Memory Analyzer 进行堆转储文件分析

简介: Eclipse Memory Analyzer(MAT)是著名的跨平台集成开发环境 Eclipse Galileo 版本的 33 个组成项目中之一,它是一个功能丰富的 JAVA 堆转储文件分析工具,可以帮助你发现内存漏洞和减...

红薯
2010/07/26
1K
0

没有更多内容

加载失败,请刷新页面

加载更多

蚂蚁区块链BaaS:开放在云端,落地于实体

自 2018 年 6 月上线以来,蚂蚁区块链 BaaS 不断在技术上实现突破,形成自身独特的优势,并做为行业领军者在不同领域的几十个场景实现落地。在区块链的商用时代加速到来之际,通过开放自身的...

阿里云官方博客
25分钟前
3
0
Idea cannot access【好使】

Idea中无法访问类中public方法。。 解决: idea点击左下角电脑图标,打开右边栏各种功能键 右边栏Maven Projects中点一下刷新,就是那个两个蓝的箭头组成的环 ----------------------------...

Airship
26分钟前
3
0
Spark内置图像数据源初探

作者:林武康,花名知瑕, 阿里巴巴计算平台事业部EMR团队的高级开发工程师,Apache HUE Contributor, 参与了多个开源项目的研发工作,对于分布式系统设计应用有较丰富的经验,目前主要专注于...

阿里云云栖社区
27分钟前
1
0
【面试被虐】游戏中的敏感词过滤是如何实现的?

小秋今天去面试了,面试官问了一个与敏感词过滤算法相关的问题,然而小秋对敏感词过滤算法一点也没听说过。于是,有了以下事情的发生….. 面试官开怼 面试官:玩过王者荣耀吧?了解过敏感词过...

爱编程的浪子
29分钟前
4
0
springboot之maven属性引入

<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-resources-plugin</artifactId><version>2.7</version><configuration><delimiters><delimite......

Online_Reus
30分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部