2018-5-15 by MartinDelophy
上回,我们很简单的对node-jvm进行了一定的分析,但问题还是停留在表面,仅仅只是介绍了node 里面的一些特点,和java并无很多的关系,今天让我们接着来讲后续的内容。 要想很清晰的知道后续的内容,我们首先要对java class类文件做一定的了解。 我想在网上这样的文章算是比较多了,我推荐几个链接可以去看看 1.https://blog.csdn.net/u010425776/article/details/51245055 2.https://www.cnblogs.com/Qian123/p/5707562.html 3.https://blog.csdn.net/u013309870/article/details/72975536 4.http://www.blogjava.net/DLevin/archive/2011/09/05/358033.html 5.http://www.blogjava.net/DLevin/archive/2011/09/05/358034.html
看了上面几个文章之后,我们开始实战来分析一下下面的代码 首先看到的是用来针对fib 进行计算的java文件
Main.java
public class Main { public static long fib(int n) { if (n <= 1) return n; return fib(n - 1) + fib(n - 2); } public static void main(String[] args) { if (args.length == 0) { System.out.print("help: java Fibonacci {Number}"); return; } int N = Integer.parseInt(args[0]); long start = System.currentTimeMillis(); System.out.format("Fibonacci from 1 to %s:\n", N); for (int i = 1; i <= N; i++) { System.out.println(i + ": " + fib(i)); } long stop = System.currentTimeMillis(); System.out.println("time: " + (stop - start) + "ms"); System.out.println("done."); } }
该部分代码,主要是实现了最基本的fib 功能,并且调用。
那么接下来我用make 生成Main.class文件,然后通过 javap -v -verbose Main 分析一下改该类文件中的内容,内容如下:
Last modified May 7, 2018; size 1286 bytes MD5 checksum 115352bb394b31575a6288f7bdf82f35 Compiled from "Main.java" public class Main minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #9.#35 // java/lang/Object."<init>":()V #2 = Methodref #23.#36 // Main.fib:(I)J #3 = Fieldref #37.#38 // java/lang/System.out:Ljava/io/PrintStream; #4 = String #39 // help: java Fibonacci {Number} #5 = Methodref #40.#41 // java/io/PrintStream.print:(Ljava/lang/String;)V #6 = Methodref #42.#43 // java/lang/Integer.parseInt:(Ljava/lang/String;)I #7 = Methodref #37.#44 // java/lang/System.currentTimeMillis:()J #8 = String #45 // Fibonacci from 1 to %s:\n #9 = Class #46 // java/lang/Object #10 = Methodref #42.#47 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer; #11 = Methodref #40.#48 // java/io/PrintStream.format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; #12 = Class #49 // java/lang/StringBuilder #13 = Methodref #12.#35 // java/lang/StringBuilder."<init>":()V #14 = Methodref #12.#50 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; #15 = String #51 // : #16 = Methodref #12.#52 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #17 = Methodref #12.#53 // java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; #18 = Methodref #12.#54 // java/lang/StringBuilder.toString:()Ljava/lang/String; #19 = Methodref #40.#55 // java/io/PrintStream.println:(Ljava/lang/String;)V #20 = String #56 // time: #21 = String #57 // ms #22 = String #58 // done. #23 = Class #59 // Main #24 = Utf8 <init> #25 = Utf8 ()V #26 = Utf8 Code #27 = Utf8 LineNumberTable #28 = Utf8 fib #29 = Utf8 (I)J #30 = Utf8 StackMapTable #31 = Utf8 main #32 = Utf8 ([Ljava/lang/String;)V #33 = Utf8 SourceFile #34 = Utf8 Main.java #35 = NameAndType #24:#25 // "<init>":()V #36 = NameAndType #28:#29 // fib:(I)J #37 = Class #60 // java/lang/System #38 = NameAndType #61:#62 // out:Ljava/io/PrintStream; #39 = Utf8 help: java Fibonacci {Number} #40 = Class #63 // java/io/PrintStream #41 = NameAndType #64:#65 // print:(Ljava/lang/String;)V #42 = Class #66 // java/lang/Integer #43 = NameAndType #67:#68 // parseInt:(Ljava/lang/String;)I #44 = NameAndType #69:#70 // currentTimeMillis:()J #45 = Utf8 Fibonacci from 1 to %s:\n #46 = Utf8 java/lang/Object #47 = NameAndType #71:#72 // valueOf:(I)Ljava/lang/Integer; #48 = NameAndType #73:#74 // format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; #49 = Utf8 java/lang/StringBuilder #50 = NameAndType #75:#76 // append:(I)Ljava/lang/StringBuilder; #51 = Utf8 : #52 = NameAndType #75:#77 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #53 = NameAndType #75:#78 // append:(J)Ljava/lang/StringBuilder; #54 = NameAndType #79:#80 // toString:()Ljava/lang/String; #55 = NameAndType #81:#65 // println:(Ljava/lang/String;)V #56 = Utf8 time: #57 = Utf8 ms #58 = Utf8 done. #59 = Utf8 Main #60 = Utf8 java/lang/System #61 = Utf8 out #62 = Utf8 Ljava/io/PrintStream; #63 = Utf8 java/io/PrintStream #64 = Utf8 print #65 = Utf8 (Ljava/lang/String;)V #66 = Utf8 java/lang/Integer #67 = Utf8 parseInt #68 = Utf8 (Ljava/lang/String;)I #69 = Utf8 currentTimeMillis #70 = Utf8 ()J #71 = Utf8 valueOf #72 = Utf8 (I)Ljava/lang/Integer; #73 = Utf8 format #74 = Utf8 (Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; #75 = Utf8 append #76 = Utf8 (I)Ljava/lang/StringBuilder; #77 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder; #78 = Utf8 (J)Ljava/lang/StringBuilder; #79 = Utf8 toString #80 = Utf8 ()Ljava/lang/String; #81 = Utf8 println { public Main(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 public static long fib(int); descriptor: (I)J flags: ACC_PUBLIC, ACC_STATIC Code: stack=4, locals=1, args_size=1 0: iload_0 1: iconst_1 2: if_icmpgt 8 5: iload_0 6: i2l 7: lreturn 8: iload_0 9: iconst_1 10: isub 11: invokestatic #2 // Method fib:(I)J 14: iload_0 15: iconst_2 16: isub 17: invokestatic #2 // Method fib:(I)J 20: ladd 21: lreturn LineNumberTable: line 4: 0 line 5: 5 line 6: 8 StackMapTable: number_of_entries = 1 frame_type = 8 /* same */ public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=6, locals=6, args_size=1 0: aload_0 1: arraylength 2: ifne 14 5: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 8: ldc #4 // String help: java Fibonacci {Number} 10: invokevirtual #5 // Method java/io/PrintStream.print:(Ljava/lang/String;)V 13: return 14: aload_0 15: iconst_0 16: aaload 17: invokestatic #6 // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I 20: istore_1 21: invokestatic #7 // Method java/lang/System.currentTimeMillis:()J 24: lstore_2 25: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 28: ldc #8 // String Fibonacci from 1 to %s:\n 30: iconst_1 31: anewarray #9 // class java/lang/Object 34: dup 35: iconst_0 36: iload_1 37: invokestatic #10 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 40: aastore 41: invokevirtual #11 // Method java/io/PrintStream.format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; 44: pop 45: iconst_1 46: istore 4 48: iload 4 50: iload_1 51: if_icmpgt 94 54: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 57: new #12 // class java/lang/StringBuilder 60: dup 61: invokespecial #13 // Method java/lang/StringBuilder."<init>":()V 64: iload 4 66: invokevirtual #14 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 69: ldc #15 // String : 71: invokevirtual #16 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 74: iload 4 76: invokestatic #2 // Method fib:(I)J 79: invokevirtual #17 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 82: invokevirtual #18 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 85: invokevirtual #19 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 88: iinc 4, 1 91: goto 48 94: invokestatic #7 // Method java/lang/System.currentTimeMillis:()J 97: lstore 4 99: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 102: new #12 // class java/lang/StringBuilder 105: dup 106: invokespecial #13 // Method java/lang/StringBuilder."<init>":()V 109: ldc #20 // String time: 111: invokevirtual #16 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 114: lload 4 116: lload_2 117: lsub 118: invokevirtual #17 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 121: ldc #21 // String ms 123: invokevirtual #16 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 126: invokevirtual #18 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 129: invokevirtual #19 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 132: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 135: ldc #22 // String done. 137: invokevirtual #19 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 140: return LineNumberTable: line 10: 0 line 11: 5 line 12: 13 line 15: 14 line 17: 21 line 18: 25 line 19: 45 line 20: 54 line 19: 88 line 22: 94 line 23: 99 line 25: 132 line 26: 140 StackMapTable: number_of_entries = 3 frame_type = 14 /* same */ frame_type = 254 /* append */ offset_delta = 33 locals = [ int, long, int ] frame_type = 250 /* chop */ offset_delta = 45 } SourceFile: "Main.java"
那么,首先,我们看到的是对于java文件信息的一些标识、本文件的版本信息等信息,这些不是很重要,所以省略。 我们重点来说说该处的常量池。
#1 = Methodref #9.#35 // java/lang/Object."<init>":()V
#1
主要表示的是常量池的程序计数器(从1开始的),Methodref 表示的是用于记录方法信息 从#9 计数器进入 #35处被调用。而下面的
{ public Main(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 ...
表示的是类名、方法名,descriptor用来区分是类还是方法,flags 来表示方法是什么样的类型,code则是代码段, LineNumberTable 表示的是这个方法在原java的第几行。 那么我们着重分析一下
0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return
上面的三条指令:
aload_0:这个操作码是aload格式操作码中的一个。它们用来把对象引用加载到操作码栈。 表示正在被访问的局部变量数组的位置,但只能是0、1、2、3 中的一个。还有一些其它类似的操作码用来载入非对象引用的数据,如iload, lload, float 和 dload。其中 i 表示 int,l 表示 long,f 表示 float,d 表示 double。局部变量数组位置大于 3 的局部变量可以用 iload, lload, float, dload 和 aload 载入。这些操作码都只需要一个操作数,即数组中的位置。 invokespecial:这些操作码属于一组函数调用的操作码,包括:invokedynamic、invokeinterface、invokespecial、invokestatic、invokevirtual。在这个 class 文件中,invokespecial 和 invokevirutal 两个指令都用到了,两者的区别是,invokevirutal 指令调用一个对象的实例方法,invokespecial 指令调用实例初始化方法、私有方法、父类方法。 return:这个操作码属于ireturn、lreturn、freturn、dreturn、areturn 和 return 操作码组。每个操作码返回一种类型的返回值,其中 i 表示 int,l 表示 long,f 表示 float,d 表示 double,a 表示 对象引用。没有前缀类型字母的 return 表示返回 void。 对于上述的指令,都会有操作数栈来进行配合。如果操作数没用完则return ,则等待系统进行垃圾回收。若用完未取到则会跑出异常。 详细的指令信息可以在:https://docs.oracle.com/javase/specs/jvms/se7/html/index.html 中见到
通过上述对于class类的简要分析,我们对jvm class文件有了一定的了解。接下来将讲述详细的内容以及如何去应用。