- 浏览: 3048978 次
- 性别:
- 来自: 海外
文章分类
- 全部博客 (430)
- Programming Languages (23)
- Compiler (20)
- Virtual Machine (57)
- Garbage Collection (4)
- HotSpot VM (26)
- Mono (2)
- SSCLI Rotor (1)
- Harmony (0)
- DLR (19)
- Ruby (28)
- C# (38)
- F# (3)
- Haskell (0)
- Scheme (1)
- Regular Expression (5)
- Python (4)
- ECMAScript (2)
- JavaScript (18)
- ActionScript (7)
- Squirrel (2)
- C (6)
- C++ (10)
- D (2)
- .NET (13)
- Java (86)
- Scala (1)
- Groovy (3)
- Optimization (6)
- Data Structure and Algorithm (3)
- Books (4)
- WPF (1)
- Game Engines (7)
- 吉里吉里 (12)
- UML (1)
- Reverse Engineering (11)
- NSIS (4)
- Utilities (3)
- Design Patterns (1)
- Visual Studio (9)
- Windows 7 (3)
- x86 Assembler (1)
- Android (2)
- School Assignment / Test (6)
- Anti-virus (1)
- REST (1)
- Profiling (1)
- misc (39)
- NetOA (12)
- rant (6)
- anime (5)
- Links (12)
- CLR (7)
- GC (1)
- OpenJDK (2)
- JVM (4)
- KVM (0)
- Rhino (1)
- LINQ (2)
- JScript (0)
- Nashorn (0)
- Dalvik (1)
- DTrace (0)
- LLVM (0)
- MSIL (0)
最新评论
-
mldxs:
虽然很多还是看不懂,写的很好!
虚拟机随谈(一):解释器,树遍历解释器,基于栈与基于寄存器,大杂烩 -
HanyuKing:
Java的多维数组 -
funnyone:
Java 8的default method与method resolution -
ljs_nogard:
Xamarin workbook - .Net Core 中不 ...
LINQ的恶搞…… -
txm119161336:
allocatestlye1 顺序为 // Fields o ...
最近做的两次Java/JVM分享的概要
如何dump出一个Java进程里的类对应的Class文件?
大家可能对JVM很好奇,想看看运行中某时刻上JVM里各种内部数据结构的状态。可能有人想看堆上所有对象都有哪些,分别位于哪个分代之类;可能有人想看当前所有线程的stack trace;可能有人想看一个方法是否被JIT编译过,被编译后的native代码是怎样的。对Sun HotSpot JVM而言,这些需求都有现成的API可以满足——通过Serviceability Agent(下面简称SA)。大家熟悉的jstack、jmap等工具在使用-F参数启动时其实就是通过SA来实现功能的。
这里介绍的是按需把class给dump出来的方法。
为什么我们要dump运行中的JVM里的class呢?直接从classpath上把Class文件找到不就好了么?这样的话只要用ClassLoader.getResourceAsStream(name)就能行了。例如说要找foo.bar.Baz的Class文件,类似这样就行:
用Groovy的交互式解释器shell来演示一下:
这样就在当前目录建了个ArrayList.class文件,把java.util.ArrayList对应的Class文件拿到手了。
问题是,上述方式其实只是借助ClassLoader把它在classpath上能找到的Class文件复制了一份而已。如果我们想dump的类在加载时被修改过(例如说某些AOP的实现会这么做),或者在运行过程中被修改过(通过HotSwap),或者干脆就是运行时才创建出来的,那就没有现成的Class文件了。
需要注意,java.lang.Class<T>这个类虽然实现了java.io.Serializable接口,但直接将一个Class对象序列化是得不到对应的Class文件的。参考src/share/classes/java/lang/Class.java里的注释:
=================================================================
HotSpot有一套私有API提供了对JVM内部数据结构的审视功能,称为Serviceability Agent。它是一套Java API,虽然HotSpot是用C++写的,但SA提供了HotSpot中重要数据结构的Java镜像类,所以可以直接写Java代码来查看一个跑在HotSpot上的Java进程的内部状态。它也提供了一些封装好的工具,可以直接在命令行上跑,包括下面提到的ClassDump工具。
SA的一个重要特征是它是“进程外审视工具”。也就是说,SA并不运行在要审视的目标进程中,而是运行在一个独立的Java进程中,通过操作系统上提供的调试API来连接到目标进程上。这样,SA的运行不会受到目标进程状态的影响,因而可以用于审视一个已经挂起的Java进程,或者是core dump文件。当然,这也就意味这一个SA进程不能用于审视自己。
一个被调试器连接上的进程会被暂停下来。所以在SA连接到目标进程时,目标进程也是一直处于暂停状态的,直到SA解除连接。如果需要在线上使用SA的话需要小心,不要通过SA做过于耗时的分析,宁可先把数据都抓出来,把SA的连接解除掉之后再离线分析。目前的使用经验是,连接上一个小Java进程的话很快就好了,但连接上一个“大”的Java进程(堆比较大、加载的类比较多)可能会在连接阶段卡住好几分钟,线上需要慎用。
目前(JDK6)在Windows上SA没有随HotSpot一起发布,所以无法在Windows上使用;在Linux、Solaris、Mac上使用都没问题。从JDK7 build 64开始Windows版JDK也带上SA,如果有兴趣尝鲜JDK7的话可以试试(http://dlc.sun.com.edgesuite.net/jdk7/binaries/index.html),当前版本是build 103;正式的JDK7今年10月份应该有指望吧。
在Windows版JDK里带上SA的相关bug是:
Bug 6743339: Enable building sa-jdi.jar and sawindbg.dll on Windows with hotspot build
Bug 6755621: Include SA binaries into Windows JDK
前面废话了那么多,接下来回到正题,介绍一下ClassDump工具。
SA自带了一个能把当前在HotSpot中加载了的类dump成Class文件的工具,称为ClassDump。它的全限定类名是sun.jvm.hotspot.tools.jcore.ClassDump,有main()方法,可以直接从命令行执行;接收一个命令行参数,是目标Java进程的进程ID,可以通过JDK自带的jps工具查找Java进程的ID。要执行该工具需要确保SA的JAR包在classpath上,位于$JAVA_HOME/lib/sa-jdi.jar。
默认条件下执行ClassDump会把当前加载的所有Java类都dump到当前目录下,如果有全限定名相同但内容不同的类同时存在于一个Java进程中,那么dump的时候会有覆盖现象,实际dump出来的是同名的类的最后一个(根据ClassDump工具的遍历顺序)。
如果需要指定被dump的类的范围,可以自己写一个过滤器,在启动ClassDump工具时指定-Dsun.jvm.hotspot.tools.jcore.filter=filterClassName,具体方法见下面例子;如果需要指定dump出来的Class文件的存放路径,可以用-Dsun.jvm.hotspot.tools.jcore.outputDir=path来指定,path替换为实际路径。
以下演示在Linux上进行。大家或许已经知道,Sun JDK对反射调用方法有一些特别的优化,会在运行时生成专门的“调用类”来提高反射调用的性能。这次演示就来看看生成的类是什么样子的。
首先编写一个自定义的过滤器。只要实现sun.jvm.hotspot.tools.jcore.ClassFilter接口即可。
InstanceKlass对应于HotSpot中表示Java类的内部对象。Sun JDK为反射调用生成的类的名字形如sun/reflect/GeneratedMethodAccessorN,其中N是一个整数;所以只要看看类名是否以"sun/reflect/GeneratedMethodAccessor"开头就能找出来了。留意到这里包名的分隔符是“/”而不是“.”,这是Java类在JVM中的“内部名称”形式,参考Java虚拟机规范第二版4.2小节。
接下来写一个会引发JDK生成反射调用类的演示程序:
让Demo跑起来,然后先不要让它结束。通过jps工具看看它的进程ID是多少:
接下来执行ClassDump,指定上面自定义的过滤器(过滤器的类要在classpath上,本例中它在./bin):
执行结束后,可以看到dump出了一个Class文件,在./sun/reflect/GeneratedMethodAccessor1.class;.是默认的输出目录,后面的目录结构对应包名。
用javap看看这个Class文件有啥内容:
用Java来表现这个类的话,就是:
这段Java代码跟实际的Class文件最主要的不同的地方在于实际的Class文件是用同一个异常处理器来处理ClassCastException与NullPointerException的。如果用Java 7的多重catch语法来写的话就是:
本来想顺带演示一下用Java反编译器把例子里的Class文件反编译为Java源码的,但用了JD和Jad都无法正确识别这里比较特别的Exceptions属性表,只好人肉反编译写出来……识别不出来也正常,毕竟Java 7之前在Java源码这层是没办法对同一个异常处理器处理指定多个异常类型。
要深究的话,上面人肉反编译的Java文件跟实际Class文件还有些细节差异。
例如说JDK在生成Class文件时为了方便所以把一大堆“很可能会用到”的常量都写到常量池里了,但在代码里可能并没有用到常量池里的所有项;如果用javac编译Java源码就不会出现这种状况。
又例如生成的Class文件里一个局部变量也没用,locals=3之中三个都是参数:第一个是this,第二个是obj,第三个是args。求值的中间结果全部都直接在操作数栈上用掉了。而在Java源码里无法写出这样的代码,像是说try块不能从一个表达式的中间开始之类的。
这次就写到这里吧~
A. Sundararajan有篇不错的文章也是讲如何从Java进程dump出Class文件的,使用的是JVMTI系的API:
Retrieving .class files from a running app
然后也有一篇使用SA从core dump文件中dump出Class文件的文章:
Retrieving .class files from a Java core dump
你说得没错…加上去~
Serviceability Agent在Windows上一直杯具。
如果你在用JDK6的话那没指望,完全用不了SA。
如果你想试用JDK7的话,那可以试试自己用OpenJDK7的源码build一个完整的OpenJDK出来,然后到hotspot/agent/make目录里去根据那边的README.txt提示来把SA的一些JAR包build出来,然后那边就会生成出SwDbgSrv.exe出来。然后试试把它注册成NT服务,重启机器,再试试能不能连接吧。
顺带一说,我以前试过但是没成功…源码里有些地方会编译不过去,修改之后总算能build出SwDbgSrv.exe,但注册成服务却没反应。我肯定是有什么步骤没弄对…
但是我决定不折磨自己,回到Linux上用SA。反正我自己的机器和公司的服务器都是Linux的,现在就只有公司的工作机上还装着Windows…
大家可能对JVM很好奇,想看看运行中某时刻上JVM里各种内部数据结构的状态。可能有人想看堆上所有对象都有哪些,分别位于哪个分代之类;可能有人想看当前所有线程的stack trace;可能有人想看一个方法是否被JIT编译过,被编译后的native代码是怎样的。对Sun HotSpot JVM而言,这些需求都有现成的API可以满足——通过Serviceability Agent(下面简称SA)。大家熟悉的jstack、jmap等工具在使用-F参数启动时其实就是通过SA来实现功能的。
这里介绍的是按需把class给dump出来的方法。
为什么我们要dump运行中的JVM里的class呢?直接从classpath上把Class文件找到不就好了么?这样的话只要用ClassLoader.getResourceAsStream(name)就能行了。例如说要找foo.bar.Baz的Class文件,类似这样就行:
ClassLoader loader = Thread.currentThread().getContextClassLoader(); InputStream in = loader.getResourceAsStream("foo/bar/Baz.class"); // 从in把内容拿出来,然后随便怎么处理
用Groovy的交互式解释器shell来演示一下:
D:\>\sdk\groovy-1.7.2\bin\groovysh Groovy Shell (1.7.2, JVM: 1.6.0_20) Type 'help' or '\h' for help. ----------------------------------------------------------------------------- groovy:000> loader = Thread.currentThread().contextClassLoader ===> org.codehaus.groovy.tools.RootLoader@61de33 groovy:000> stream = loader.getResourceAsStream('java/util/ArrayList.class') ===> sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@5dfaf1 groovy:000> file = new File('ArrayList.class') ===> ArrayList.class groovy:000> file.createNewFile() ===> true groovy:000> file << stream ===> ArrayList.class groovy:000> quit
这样就在当前目录建了个ArrayList.class文件,把java.util.ArrayList对应的Class文件拿到手了。
问题是,上述方式其实只是借助ClassLoader把它在classpath上能找到的Class文件复制了一份而已。如果我们想dump的类在加载时被修改过(例如说某些AOP的实现会这么做),或者在运行过程中被修改过(通过HotSwap),或者干脆就是运行时才创建出来的,那就没有现成的Class文件了。
需要注意,java.lang.Class<T>这个类虽然实现了java.io.Serializable接口,但直接将一个Class对象序列化是得不到对应的Class文件的。参考src/share/classes/java/lang/Class.java里的注释:
package java.lang; import java.io.ObjectStreamField; // ... public final class Class<T> implements java.io.Serializable, java.lang.reflect.GenericDeclaration, java.lang.reflect.Type, java.lang.reflect.AnnotatedElement { /** * Class Class is special cased within the Serialization Stream Protocol. * * A Class instance is written initially into an ObjectOutputStream in the * following format: * <pre> * <code>TC_CLASS</code> ClassDescriptor * A ClassDescriptor is a special cased serialization of * a <code>java.io.ObjectStreamClass</code> instance. * </pre> * A new handle is generated for the initial time the class descriptor * is written into the stream. Future references to the class descriptor * are written as references to the initial class descriptor instance. * * @see java.io.ObjectStreamClass */ private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0]; // ... }
=================================================================
HotSpot有一套私有API提供了对JVM内部数据结构的审视功能,称为Serviceability Agent。它是一套Java API,虽然HotSpot是用C++写的,但SA提供了HotSpot中重要数据结构的Java镜像类,所以可以直接写Java代码来查看一个跑在HotSpot上的Java进程的内部状态。它也提供了一些封装好的工具,可以直接在命令行上跑,包括下面提到的ClassDump工具。
SA的一个重要特征是它是“进程外审视工具”。也就是说,SA并不运行在要审视的目标进程中,而是运行在一个独立的Java进程中,通过操作系统上提供的调试API来连接到目标进程上。这样,SA的运行不会受到目标进程状态的影响,因而可以用于审视一个已经挂起的Java进程,或者是core dump文件。当然,这也就意味这一个SA进程不能用于审视自己。
一个被调试器连接上的进程会被暂停下来。所以在SA连接到目标进程时,目标进程也是一直处于暂停状态的,直到SA解除连接。如果需要在线上使用SA的话需要小心,不要通过SA做过于耗时的分析,宁可先把数据都抓出来,把SA的连接解除掉之后再离线分析。目前的使用经验是,连接上一个小Java进程的话很快就好了,但连接上一个“大”的Java进程(堆比较大、加载的类比较多)可能会在连接阶段卡住好几分钟,线上需要慎用。
目前(JDK6)在Windows上SA没有随HotSpot一起发布,所以无法在Windows上使用;在Linux、Solaris、Mac上使用都没问题。从JDK7 build 64开始Windows版JDK也带上SA,如果有兴趣尝鲜JDK7的话可以试试(http://dlc.sun.com.edgesuite.net/jdk7/binaries/index.html),当前版本是build 103;正式的JDK7今年10月份应该有指望吧。
在Windows版JDK里带上SA的相关bug是:
Bug 6743339: Enable building sa-jdi.jar and sawindbg.dll on Windows with hotspot build
Bug 6755621: Include SA binaries into Windows JDK
前面废话了那么多,接下来回到正题,介绍一下ClassDump工具。
SA自带了一个能把当前在HotSpot中加载了的类dump成Class文件的工具,称为ClassDump。它的全限定类名是sun.jvm.hotspot.tools.jcore.ClassDump,有main()方法,可以直接从命令行执行;接收一个命令行参数,是目标Java进程的进程ID,可以通过JDK自带的jps工具查找Java进程的ID。要执行该工具需要确保SA的JAR包在classpath上,位于$JAVA_HOME/lib/sa-jdi.jar。
默认条件下执行ClassDump会把当前加载的所有Java类都dump到当前目录下,如果有全限定名相同但内容不同的类同时存在于一个Java进程中,那么dump的时候会有覆盖现象,实际dump出来的是同名的类的最后一个(根据ClassDump工具的遍历顺序)。
如果需要指定被dump的类的范围,可以自己写一个过滤器,在启动ClassDump工具时指定-Dsun.jvm.hotspot.tools.jcore.filter=filterClassName,具体方法见下面例子;如果需要指定dump出来的Class文件的存放路径,可以用-Dsun.jvm.hotspot.tools.jcore.outputDir=path来指定,path替换为实际路径。
以下演示在Linux上进行。大家或许已经知道,Sun JDK对反射调用方法有一些特别的优化,会在运行时生成专门的“调用类”来提高反射调用的性能。这次演示就来看看生成的类是什么样子的。
首先编写一个自定义的过滤器。只要实现sun.jvm.hotspot.tools.jcore.ClassFilter接口即可。
import sun.jvm.hotspot.tools.jcore.ClassFilter; import sun.jvm.hotspot.oops.InstanceKlass; public class MyFilter implements ClassFilter { @Override public boolean canInclude(InstanceKlass kls) { String klassName = kls.getName().asString(); return klassName.startsWith("sun/reflect/GeneratedMethodAccessor"); } }
InstanceKlass对应于HotSpot中表示Java类的内部对象。Sun JDK为反射调用生成的类的名字形如sun/reflect/GeneratedMethodAccessorN,其中N是一个整数;所以只要看看类名是否以"sun/reflect/GeneratedMethodAccessor"开头就能找出来了。留意到这里包名的分隔符是“/”而不是“.”,这是Java类在JVM中的“内部名称”形式,参考Java虚拟机规范第二版4.2小节。
接下来写一个会引发JDK生成反射调用类的演示程序:
import java.lang.reflect.Method; public class Demo { public static void main(String[] args) throws Exception { Method p = System.out.getClass().getMethod("println", String.class); for (int i = 0; i < 16; i++) { p.invoke(System.out, "demo"); } System.in.read(); // block the program } }
让Demo跑起来,然后先不要让它结束。通过jps工具看看它的进程ID是多少:
[sajia@sajia class_dump]$ jps 20542 Demo 20554 Jps
接下来执行ClassDump,指定上面自定义的过滤器(过滤器的类要在classpath上,本例中它在./bin):
[sajia@sajia class_dump]$ java -classpath ".:./bin:$JAVA_HOME/lib/sa-jdi.jar" -Dsun.jvm.hotspot.tools.jcore.filter=MyFilter sun.jvm.hotspot.tools.jcore.ClassDump 20542
执行结束后,可以看到dump出了一个Class文件,在./sun/reflect/GeneratedMethodAccessor1.class;.是默认的输出目录,后面的目录结构对应包名。
用javap看看这个Class文件有啥内容:
[sajia@sajia class_dump]$ javap -verbose sun.reflect.GeneratedMethodAccessor1 public class sun.reflect.GeneratedMethodAccessor1 extends sun.reflect.MethodAccessorImpl minor version: 0 major version: 46 Constant pool: const #1 = Asciz sun/reflect/GeneratedMethodAccessor1; const #2 = class #1; // sun/reflect/GeneratedMethodAccessor1 const #3 = Asciz sun/reflect/MethodAccessorImpl; const #4 = class #3; // sun/reflect/MethodAccessorImpl const #5 = Asciz java/io/PrintStream; const #6 = class #5; // java/io/PrintStream const #7 = Asciz println; const #8 = Asciz (Ljava/lang/String;)V; const #9 = NameAndType #7:#8;// println:(Ljava/lang/String;)V const #10 = Method #6.#9; // java/io/PrintStream.println:(Ljava/lang/String;)V const #11 = Asciz invoke; const #12 = Asciz (Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;; const #13 = Asciz java/lang/String; const #14 = class #13; // java/lang/String const #15 = Asciz java/lang/Throwable; const #16 = class #15; // java/lang/Throwable const #17 = Asciz java/lang/ClassCastException; const #18 = class #17; // java/lang/ClassCastException const #19 = Asciz java/lang/NullPointerException; const #20 = class #19; // java/lang/NullPointerException const #21 = Asciz java/lang/IllegalArgumentException; const #22 = class #21; // java/lang/IllegalArgumentException const #23 = Asciz java/lang/reflect/InvocationTargetException; const #24 = class #23; // java/lang/reflect/InvocationTargetException const #25 = Asciz <init>; const #26 = Asciz ()V; const #27 = NameAndType #25:#26;// "<init>":()V const #28 = Method #20.#27; // java/lang/NullPointerException."<init>":()V const #29 = Method #22.#27; // java/lang/IllegalArgumentException."<init>":()V const #30 = Asciz (Ljava/lang/String;)V; const #31 = NameAndType #25:#30;// "<init>":(Ljava/lang/String;)V const #32 = Method #22.#31; // java/lang/IllegalArgumentException."<init>":(Ljava/lang/String;)V const #33 = Asciz (Ljava/lang/Throwable;)V; const #34 = NameAndType #25:#33;// "<init>":(Ljava/lang/Throwable;)V const #35 = Method #24.#34; // java/lang/reflect/InvocationTargetException."<init>":(Ljava/lang/Throwable;)V const #36 = Method #4.#27; // sun/reflect/MethodAccessorImpl."<init>":()V const #37 = Asciz java/lang/Object; const #38 = class #37; // java/lang/Object const #39 = Asciz toString; const #40 = Asciz ()Ljava/lang/String;; const #41 = NameAndType #39:#40;// toString:()Ljava/lang/String; const #42 = Method #38.#41; // java/lang/Object.toString:()Ljava/lang/String; const #43 = Asciz Code; const #44 = Asciz Exceptions; const #45 = Asciz java/lang/Boolean; const #46 = class #45; // java/lang/Boolean const #47 = Asciz (Z)V; const #48 = NameAndType #25:#47;// "<init>":(Z)V const #49 = Method #46.#48; // java/lang/Boolean."<init>":(Z)V const #50 = Asciz booleanValue; const #51 = Asciz ()Z; const #52 = NameAndType #50:#51;// booleanValue:()Z const #53 = Method #46.#52; // java/lang/Boolean.booleanValue:()Z const #54 = Asciz java/lang/Byte; const #55 = class #54; // java/lang/Byte const #56 = Asciz (B)V; const #57 = NameAndType #25:#56;// "<init>":(B)V const #58 = Method #55.#57; // java/lang/Byte."<init>":(B)V const #59 = Asciz byteValue; const #60 = Asciz ()B; const #61 = NameAndType #59:#60;// byteValue:()B const #62 = Method #55.#61; // java/lang/Byte.byteValue:()B const #63 = Asciz java/lang/Character; const #64 = class #63; // java/lang/Character const #65 = Asciz (C)V; const #66 = NameAndType #25:#65;// "<init>":(C)V const #67 = Method #64.#66; // java/lang/Character."<init>":(C)V const #68 = Asciz charValue; const #69 = Asciz ()C; const #70 = NameAndType #68:#69;// charValue:()C const #71 = Method #64.#70; // java/lang/Character.charValue:()C const #72 = Asciz java/lang/Double; const #73 = class #72; // java/lang/Double const #74 = Asciz (D)V; const #75 = NameAndType #25:#74;// "<init>":(D)V const #76 = Method #73.#75; // java/lang/Double."<init>":(D)V const #77 = Asciz doubleValue; const #78 = Asciz ()D; const #79 = NameAndType #77:#78;// doubleValue:()D const #80 = Method #73.#79; // java/lang/Double.doubleValue:()D const #81 = Asciz java/lang/Float; const #82 = class #81; // java/lang/Float const #83 = Asciz (F)V; const #84 = NameAndType #25:#83;// "<init>":(F)V const #85 = Method #82.#84; // java/lang/Float."<init>":(F)V const #86 = Asciz floatValue; const #87 = Asciz ()F; const #88 = NameAndType #86:#87;// floatValue:()F const #89 = Method #82.#88; // java/lang/Float.floatValue:()F const #90 = Asciz java/lang/Integer; const #91 = class #90; // java/lang/Integer const #92 = Asciz (I)V; const #93 = NameAndType #25:#92;// "<init>":(I)V const #94 = Method #91.#93; // java/lang/Integer."<init>":(I)V const #95 = Asciz intValue; const #96 = Asciz ()I; const #97 = NameAndType #95:#96;// intValue:()I const #98 = Method #91.#97; // java/lang/Integer.intValue:()I const #99 = Asciz java/lang/Long; const #100 = class #99; // java/lang/Long const #101 = Asciz (J)V; const #102 = NameAndType #25:#101;// "<init>":(J)V const #103 = Method #100.#102; // java/lang/Long."<init>":(J)V const #104 = Asciz longValue; const #105 = Asciz ()J; const #106 = NameAndType #104:#105;// longValue:()J const #107 = Method #100.#106; // java/lang/Long.longValue:()J const #108 = Asciz java/lang/Short; const #109 = class #108; // java/lang/Short const #110 = Asciz (S)V; const #111 = NameAndType #25:#110;// "<init>":(S)V const #112 = Method #109.#111; // java/lang/Short."<init>":(S)V const #113 = Asciz shortValue; const #114 = Asciz ()S; const #115 = NameAndType #113:#114;// shortValue:()S const #116 = Method #109.#115; // java/lang/Short.shortValue:()S { public sun.reflect.GeneratedMethodAccessor1(); Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: invokespecial #36; //Method sun/reflect/MethodAccessorImpl."<init>":()V 4: return public java.lang.Object invoke(java.lang.Object, java.lang.Object[]) throws java.lang.reflect.InvocationTargetException; Exceptions: throws java.lang.reflect.InvocationTargetException Code: Stack=5, Locals=3, Args_size=3 0: aload_1 1: ifnonnull 12 4: new #20; //class java/lang/NullPointerException 7: dup 8: invokespecial #28; //Method java/lang/NullPointerException."<init>":()V 11: athrow 12: aload_1 13: checkcast #6; //class java/io/PrintStream 16: aload_2 17: arraylength 18: sipush 1 21: if_icmpeq 32 24: new #22; //class java/lang/IllegalArgumentException 27: dup 28: invokespecial #29; //Method java/lang/IllegalArgumentException."<init>":()V 31: athrow 32: aload_2 33: sipush 0 36: aaload 37: checkcast #14; //class java/lang/String 40: invokevirtual #10; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 43: aconst_null 44: areturn 45: invokespecial #42; //Method java/lang/Object.toString:()Ljava/lang/String; 48: new #22; //class java/lang/IllegalArgumentException 51: dup_x1 52: swap 53: invokespecial #32; //Method java/lang/IllegalArgumentException."<init>":(Ljava/lang/String;)V 56: athrow 57: new #24; //class java/lang/reflect/InvocationTargetException 60: dup_x1 61: swap 62: invokespecial #35; //Method java/lang/reflect/InvocationTargetException."<init>":(Ljava/lang/Throwable;)V 65: athrow Exception table: from to target type 12 40 45 Class java/lang/ClassCastException 12 40 45 Class java/lang/NullPointerException 40 43 57 Class java/lang/Throwable }
用Java来表现这个类的话,就是:
package sun.reflect; public class GeneratedMethodAccessor1 extends MethodAccessorImpl { public GeneratedMethodAccessor1() { super(); } public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException { // prepare the target and parameters if (obj == null) throw new NullPointerException(); try { PrintStream target = (PrintStream) obj; if (args.length != 1) throw new IllegalArgumentException(); String arg0 = (String) args[0]; } catch (ClassCastException e) { throw new IllegalArgumentException(e.toString()); } catch (NullPointerException e) { throw new IllegalArgumentException(e.toString()); } // make the invocation try { target.println(arg0); return null; } catch (Throwable t) { throw new InvocationTargetException(t); } } }
这段Java代码跟实际的Class文件最主要的不同的地方在于实际的Class文件是用同一个异常处理器来处理ClassCastException与NullPointerException的。如果用Java 7的多重catch语法来写的话就是:
package sun.reflect; public class GeneratedMethodAccessor1 extends MethodAccessorImpl { public GeneratedMethodAccessor1() { super(); } public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException { // prepare the target and parameters if (obj == null) throw new NullPointerException(); try { PrintStream target = (PrintStream) obj; if (args.length != 1) throw new IllegalArgumentException(); String arg0 = (String) args[0]; } catch (final ClassCastException | NullPointerException e) { throw new IllegalArgumentException(e.toString()); } // make the invocation try { target.println(arg0); return null; } catch (Throwable t) { throw new InvocationTargetException(t); } } }
本来想顺带演示一下用Java反编译器把例子里的Class文件反编译为Java源码的,但用了JD和Jad都无法正确识别这里比较特别的Exceptions属性表,只好人肉反编译写出来……识别不出来也正常,毕竟Java 7之前在Java源码这层是没办法对同一个异常处理器处理指定多个异常类型。
要深究的话,上面人肉反编译的Java文件跟实际Class文件还有些细节差异。
例如说JDK在生成Class文件时为了方便所以把一大堆“很可能会用到”的常量都写到常量池里了,但在代码里可能并没有用到常量池里的所有项;如果用javac编译Java源码就不会出现这种状况。
又例如生成的Class文件里一个局部变量也没用,locals=3之中三个都是参数:第一个是this,第二个是obj,第三个是args。求值的中间结果全部都直接在操作数栈上用掉了。而在Java源码里无法写出这样的代码,像是说try块不能从一个表达式的中间开始之类的。
这次就写到这里吧~
A. Sundararajan有篇不错的文章也是讲如何从Java进程dump出Class文件的,使用的是JVMTI系的API:
Retrieving .class files from a running app
然后也有一篇使用SA从core dump文件中dump出Class文件的文章:
Retrieving .class files from a Java core dump
评论
7 楼
RednaxelaFX
2011-06-30
all_wmh 写道
撒加 貌似JAVA代码 缺少返回值了;)
你说得没错…加上去~
6 楼
all_wmh
2011-06-30
撒加 貌似JAVA代码 缺少返回值了;)
5 楼
RednaxelaFX
2011-06-23
all_wmh 写道
我用windows下操作的时候 报了一个异常:"please start SwDbgSrv.exe" 看了windows的services也没有这个服务 不知道是不是要加载什么类呢?
Serviceability Agent在Windows上一直杯具。
如果你在用JDK6的话那没指望,完全用不了SA。
如果你想试用JDK7的话,那可以试试自己用OpenJDK7的源码build一个完整的OpenJDK出来,然后到hotspot/agent/make目录里去根据那边的README.txt提示来把SA的一些JAR包build出来,然后那边就会生成出SwDbgSrv.exe出来。然后试试把它注册成NT服务,重启机器,再试试能不能连接吧。
顺带一说,我以前试过但是没成功…源码里有些地方会编译不过去,修改之后总算能build出SwDbgSrv.exe,但注册成服务却没反应。我肯定是有什么步骤没弄对…
但是我决定不折磨自己,回到Linux上用SA。反正我自己的机器和公司的服务器都是Linux的,现在就只有公司的工作机上还装着Windows…
4 楼
all_wmh
2011-06-23
我用windows下操作的时候 报了一个异常:"please start SwDbgSrv.exe" 看了windows的services也没有这个服务 不知道是不是要加载什么类呢?
3 楼
stone2083
2010-11-29
一直在找如何dump class。非常感谢。 :)
2 楼
yangguo
2010-09-27
你真是很大的一头牛。
1 楼
lwwin
2010-08-03
很好,这篇给某人去看看^-^~
发表评论
-
The Prehistory of Java, HotSpot and Train
2014-06-02 08:18 0http://cs.gmu.edu/cne/itcore/vi ... -
MSJVM and Sun 1.0.x/1.1.x
2014-05-20 18:50 0当年的survey paper: http://www.sym ... -
Sun JDK1.4.2_28有TieredCompilation
2014-05-12 08:48 0原来以前Sun的JDK 1.4.2 update 28就已经有 ... -
IBM JVM notes (2014 ver)
2014-05-11 07:16 0Sovereign JIT http://publib.bou ... -
class data sharing by Apple
2014-03-28 05:17 0class data sharing is implement ... -
Java 8与静态工具类
2014-03-19 08:43 16276以前要在Java里实现所谓“静态工具类”(static uti ... -
Java 8的default method与method resolution
2014-03-19 02:23 10453先看看下面这个代码例子, interface IFoo { ... -
HotSpot Server VM与Server Class Machine
2014-02-18 13:21 0HotSpot VM历来有Client VM与Server V ... -
Java 8的lambda表达式在OpenJDK8中的实现
2014-02-04 12:08 0三月份JDK8就要发布首发了,现在JDK8 release c ... -
GC stack map与deopt stack map的异同
2014-01-08 09:56 0两者之间不并存在包含关系。它们有交集,但也各自有特别的地方。 ... -
HotSpot Server Compiler与data-flow analysis
2014-01-07 17:41 0http://en.wikipedia.org/wiki/Da ... -
字符串的一般封装方式的内存布局 (1): 元数据与字符串内容,整体还是分离?
2013-11-07 17:44 22395(Disclaimer:未经许可请 ... -
字符串的一般封装方式的内存布局
2013-11-01 12:55 0(Disclaimer:未经许可请 ... -
关于string,内存布局,C++ std::string,CoW
2013-10-30 20:45 0(Disclaimer:未经许可请 ... -
对C语义的for循环的基本代码生成模式
2013-10-19 23:12 21874之前有同学在做龙书(第二版)题目,做到8.4的练习,跟我对答案 ... -
Java的instanceof是如何实现的
2013-09-22 16:57 0Java语言规范,Java SE 7版 http://docs ... -
oop、klass、handle的关系
2013-07-30 17:34 0oopDesc及其子类的实例 oop : oopDesc* ... -
Nashorn各种笔记
2013-07-15 17:03 0http://bits.netbeans.org/netbea ... -
《深入理解Java虚拟机(第二版)》书评
2013-07-08 19:19 0值得推荐的中文Java虚拟机入门书 感谢作者赠与的样书,以下 ... -
豆列:从表到里学习JVM实现
2013-06-13 14:13 48366刚写了个学习JVM用的豆列跟大家分享。 豆列地址:http: ...
相关推荐
java堆栈信息dump文件
javacore 文件是一个文本文件,主要保存的是 Java 应用程序各线程在某一时刻的运行的位置,即执行到哪一个类的哪一个方法哪一个行上。javacore 文件可以帮助我们分析应用程序是否“卡”在某一点上,即在某一点运行的...
Heap dump 文件是一个二进制文件,它保存了某一时刻在 Java 堆中所有对象的状态。这个文件最重要的作用就是分析 Java 堆内存泄露问题,heap analyzer,MAT 等工具都可以分析这种文件。 Java core 文件保存的是 java ...
首先,`javacore`文件是Java虚拟机(JVM)在遇到特定事件(如系统崩溃、异常或者手动触发)时生成的一种日志文件,它包含了JVM在特定时刻的运行状态信息,如线程堆栈、类加载信息、内存使用情况等。`jca`(Java Core...
Java的TDA线程转储分析器是一个用于分析Sun Java VM生成的线程转储和堆信息的小型Swing GUI(目前用1.4测试)。它从提供的日志文件中解析线程转储和类直方图。它提供关于发现的线程转储的统计信息,提供关于锁定监视器...
在C++编程中,"dump文件生成类"是一种用于调试和故障排查的工具。当C++程序因某种原因崩溃时,系统或应用程序可以生成一种叫做dump文件的数据结构,它包含了程序运行时的状态信息,如内存状态、堆栈信息、线程状态等...
4. `tda-bin-2.4.zip`:这是一个二进制版本的TDA,可能包含可执行文件和其他运行时所需的资源,版本号为2.4,表明这是该工具的一个具体发行版本。 使用TDA,开发者可以执行以下操作: 1. **加载线程转储**:导入JVM...
本文将详细介绍class-dump-3.5,这是一个开源工具,用于从 Mach-O 文件(如可执行文件、动态库)中导出Objective-C类、协议、分类等元数据信息。 首先,我们来理解一下什么是Mach-O文件。在苹果的生态系统中,Mach-...
而class-dump-z是class-dump的一个增强版本,它不仅保留了原版的功能,还增加了一些特色特性,如支持ARM64架构,以及更友好的命令行参数等。 使用class-dump-z,开发者可以快速获取到iOS应用中的类定义、方法列表、...
其中,`jstack` 命令是一个 Java 命令行工具,用于生成 Thread Dump。 在 Unix 系统中,可以使用 `kill -3 <pid>` 命令来获取 Thread Dump,其中 `<pid>` 是 JVM 进程的进程 ID。在 Windows 系统中,可以按下 CTRL+...
首先,Java堆内存dump,通常是以.hprof格式存储的文件,包含了在特定时刻JVM内存中的所有对象和类的信息。分析堆内存dump可以揭示哪些对象占用了大量内存,以及这些对象之间的引用关系,从而找出可能导致内存泄漏的...
在实际运行中,往往一次dump的信息,还不足以确认问题,建议产生三次dump信息,如果每次dump都指向同一个问题,我们才确定问题的典型性。 JAVA线程dump分析的步骤包括: 1. 了解线程状态:线程可以是Runnable、...
JavaDump是Java虚拟机在运行时的快照,记录了Java虚拟机的状态和信息,并将其保存到一个文件中。这个文件可以用于后续的分析,以帮助开发者理解程序的运行状况,特别是当程序出现故障或者性能问题时。 JavaDump文件...
总的来说,class-dump是iOS开发者手中的一个强大工具,尽管在iOS9之后遇到了挑战,但通过社区的努力,它依然能帮助我们窥探系统库的内部结构。然而,在实际使用中,我们应该谨慎对待获取的私有信息,并遵守苹果的...
2. **获取dump文件**:根据描述,你已经有了一个与Java应用服务器相关的dump文件。这个文件可能由系统管理员手动生成,或者是在特定错误条件下自动创建的。 3. **解析dump文件**:运行dump分析工具,输入dump文件的...
性能测试,线程的 dump 看到线程的 死锁,等待 运行状态
表示生成一个包含必要信息的dump文件,文件大小约200-500k,具体Flag =MiniDumpNormal|MiniDumpWithThreadInfo|MiniDumpWithHandleData|MiniDumpWithIndirectlyReferencedMemory (2)FullDump: 表示生成一个所有...
《class-dump-z:全平台...这对于iOS开发者、安全研究员以及对逆向工程感兴趣的IT从业者来说,无疑是一个巨大的福音。通过掌握这款工具的使用,我们可以更深入地了解软件内部结构,提高我们的编程技巧和问题解决能力。
JavaCore 文件是一个文本文件,它保存的是 Java 应用程序各线程在某一时刻的运行的位置,即 JVM 执行到哪一个类、哪一个方法、哪一个行上。通过对 JavaCore 文件的分析,可以得到应用是否“卡”在某一点上,即在某...
- **JavaCore**:是一个Java虚拟机(JVM)在某个时间点上的状态快照,包括线程栈、锁信息、类加载情况等,用于分析线程死锁、挂起等问题。 #### 设置环境变量 在AIX系统下的WebSphere应用服务器中,可以通过设置一...