JVM性能调优(1) —— JVM内存模型和类加载运行机制
一、JVM内存模型
运行一个 Java 应用程序,必须要先安装 JDK 或者 JRE 包。因为 Java 应用在编译后会变成字节码,通过字节码运行在 JVM 中,而 JVM 是 JRE 的核心组成部分。JVM 不仅承担了 Java 字节码的分析和执行,同时也内置了自动内存分配管理机制。这个机制可以大大降低手动分配回收机制可能带来的内存泄露和内存溢出风险,使 Java 开发人员不需要关注每个对象的内存分配以及回收,从而更专注于业务本身。
在 Java 中,JVM 内存模型主要分为堆、方法区、程序计数器、虚拟机栈和本地方法栈。其中,堆和方法区被所有线程共享,虚拟机栈、本地方法栈、程序计数器是线程私有的。
1、堆
堆是 JVM 内存中最大的一块内存空间,该内存被所有线程共享,几乎所有对象和数组都被分配到了堆内存中。堆被划分为新生代和老年代,新生代又被进一步划分为 Eden 和 Survivor 区,最后 Survivor 由 From Survivor 和 To Survivor 组成。
但需要注意的是,这些区域的划分因不同的垃圾收集器而不同。大部分垃圾收集器都是基于分代收集理论设计的,就会采用这种分代模型。而一些新的垃圾收集器不采用分代设计,比如 G1 收集器就是把堆内存拆分为多个大小相等的 Region。
2、方法区
在 jdk8 之前,HotSopt 虚拟机的方法区又被称为永久代,由于永久代的设计容易导致内存溢出等问题,jdk8 之后就没有永久代了,取而代之的是元空间(MetaSpace)。元空间并没有处于堆内存上,而是直接占用的本地内存,因此元空间的最大大小受本地内存限制。
方法区与堆空间类似,是所有线程共享的。方法区主要是用来存放已被虚拟机加载的类型信息、常量、静态变量等数据。方法区是一个逻辑分区,包含元空间、运行时常量池、字符串常量池,元空间物理上使用的本地内存,运行时常量池和字符串常量池是在堆中开辟的一块特殊内存区域。这样做的好处之一是可以避免运行时动态生成的常量的复制迁移,可以直接使用堆中的引用。要注意的是,字符串常量池在 jvm 中只有一个,而运行时常量池是和类型数据绑定的,每个 Class 一个。
1)类型信息(类或接口):
- 这个类型的全限定名
- 这个类型的直接超类的全限定名(只有 java.lang.Object 没有超类)
- 这个类型的访问修饰符(public、abstract、final)
- 这个类型是接口类型还是类类型
- 任何直接超接口的的全限定名的有序列表
2)运行时常量池:
- Class 文件被装载进虚拟机后,Class 常量池表中的字面量和符号引用都会存放到运行时常量池中,平时我们说的常量池一般指运行时常量池。
- 运行时常量池相比Class常量池具备动态性,运行时可以将新的常量放入池中,比如调用 String.intern() 方法使字符串驻留。
3)字段信息:
- 字段名
- 字段的类型(包括 void)
- 字段的修饰符(public、private、protected、static、final、volatile、transient)
4)方法信息:
- 方法名
- 方法的返回类型
- 方法参数的数量和类型
- 方法的修饰符(public、private、protected、static、final、synchronized、native、abstract)
- 方法的字节码
- 操作数栈和该方法的栈帧中的局部变量的大小
- 异常表
5)指向类加载器的引用:
- jvm 使用类加载器来加载一个类,这个类加载器是和这个类型绑定的,因此会在类型信息中存储这个类加载器的引用
6)指向 Class 类的引用:
- 每一个被加载的类型,jvm 都会在堆中创建一个 java.lang.Class 的实例,类型信息中会存储 Class 实例的引用
- 在代码中,可以使用 Class 实例访问方法区保存的信息,如类加载器、类名、接口等
3、虚拟机栈
每当启动一个新的线程,虚拟机都会在虚拟机栈里为它分配一个线程栈,线程栈与线程同生共死。线程栈以 栈帧 为单位保存线程的运行状态,虚拟机只会对线程栈执行两种操作:以栈帧为单位的压栈或出栈。每个方法在执行的同时都会创建一个栈帧,每个方法从调用开始到结束,就对应着一个栈帧在线程栈中压栈和出栈的过程。方法可以通过两种方式结束,一种通过 return 正常返回,一种通过抛出异常而终止。方法返回后,虚拟机都会弹出当前栈帧然后释放掉。
当虚拟机调用一个Java方法时.它从对应类的类型信息中得到此方法的局部变量区和操作数栈的大小,并据此分配栈帧内存,然后压入Java栈中。
栈帧由三部分组成:局部变量区、操作数栈、帧数据区。
1)局部变量区:
- 局部变量区是一个数组结构,主要存放对应方法的参数和局部变量。
- 如果是实例方法,局部变量表第一个参数是一个 reference 引用类型,存放的是当前对象本身 this。
2)操作数栈:
- 操作数栈也是一个数组结构,但并不是通过索引来访问的,而是栈的压栈和出栈操作。
- 操作数栈是虚拟机的工作区,大多数指令都要从这里弹出数据、执行运算、然后把结果压回操作数栈。
3)帧数据区:主要保存常量池入口、异常表、正常方法返回的信息
- 常量池入口引用:某些指令要从常量池取数据,获取类、字段信息等
- 异常表引用:当方法抛出异常时,虚拟机根据异常表来决定如何处理。如果在异常表找到了匹配的 catch 子句,就会把控制权转交给 catch 子句的代码。没有则立即异常中止,然后恢复发起调用的方法的栈帧,然后在发起调用的方法的上下文中重新抛出同样的异常。
- 方法返回信息:方法正常返回时,虚拟机通过这些信息恢复发起调用的方法的栈帧,设置PC寄存器指向发起调用的方法。方法如果有返回值,还会把返回结果压入到发起调用的方法的操作数栈。
4、本地方法栈
本地方法栈与虚拟机栈所发挥的作用是相似的,当线程调用Java方法时,会创建一个栈帧并压入虚拟机栈;而调用本地方法时,虚拟机会保持栈不变,不会压入新的栈帧,虚拟机只是简单的动态链接并直接调用指定的本地方法,使用的是某种本地方法栈。比如某个虚拟机实现的本地方法接口是使用C连接模型,那么它的本地方法栈就是C栈。
本地方法可以通过本地方法接口来访问虚拟机的运行时数据区,它可以做任何他想做的事情,本地方法不受虚拟机控制。
5、程序计数器
每一个运行的线程都会有它的程序计数器(PC寄存器),与线程的生命周期一样。执行某个方法时,PC寄存器的内容总是下一条将被执行的地址,这个地址可以是一个本地指针,也可以是在方法字节码中相对于该方法起始指令的偏移量。如果该线程正在执行一个本地方法,那么此时PC寄存器的值是 undefined。
程序计数器是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。多线程环境下,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储。
二、类加载机制
写好的源代码,需要编译后加载到虚拟机才能运行。java 源文件编译成 class 文件后,jvm 通过类加载器把 class 文件加载到虚拟机,然后经过类连接(类连接又包括验证、准备、解析三个阶段),最后经过初始化,字节码就可以被解释执行了。对于一些热点代码,虚拟机还存在一道即时编译,会把字节码编译成本地平台相关的机器码,以提高热点代码的执行效率。
装载、验证、准备、初始化这几个阶段的顺序是确定的,类型的加载过程必须按照这种顺序开始,而解析阶段可以在初始化阶段之后再开始,一般是在第一次使用到这个对象时才会开始解析。这些阶段通常都是互相交叉地混合进行的,会在一个阶段执行的过程中调用、激活另一个阶段,比如发现引用了另一个类,那么就会先触发另一个类的加载过程。
接下来通过如下类和代码来详细分析下类加载的过程:
1 package com.lyyzoo.jvm.test01; 2 3 public class Person<T> { 4 5 public static final String SEX_MAN = "Male"; 6 public static final String SEX_WOMAN = "Female"; 7 8 static { 9 System.out.println("Person static init"); 10 System.out.println("SEX_MAN: " + SEX_MAN); 11 } 12 13 public void sayHello(T str) { 14 System.out.println("Person say hello: " + str); 15 } 16 } 17 18 19 ///////////////////////////////////////////////////////////////////// 20 21 22 package com.lyyzoo.jvm.test01; 23 24 import java.io.Serializable; 25 26 public class User extends Person<String> implements Serializable { 27 private static final long serialVersionUID = -4482416396338787067L; 28 29 // 静态常量 30 public static final String FIELD_NAME = "username"; 31 public static final int AGE_MAX = 100; 32 33 // 静态变量 34 private static String staticName = "Rambo"; 35 private static int staticAge = 20; 36 37 // 类属性 38 private String name = "兰博"; 39 private int age = 25; 40 41 // 静态代码块 42 static { 43 System.out.println("user static init"); 44 System.out.println("staticName=" + staticName); 45 System.out.println("staticAge=" + staticAge); 46 } 47 48 public User() { 49 } 50 51 public User(String name, int age) { 52 this.name = name; 53 this.age = age; 54 } 55 56 // 实例方法 57 public void printInfo() { 58 System.out.println("name:" + name + ", age:" + age); 59 } 60 61 // 静态方法 62 public static void staticPrintInfo() { 63 System.out.println("FIELD_NAME:" + FIELD_NAME + ", AGE_MAX:" + AGE_MAX); 64 } 65 66 // 泛型方法重载 67 @Override 68 public void sayHello(String str) { 69 super.sayHello(str); 70 System.out.println("User say hello: " + str); 71 } 72 73 // 方法将抛出异常 74 public int willThrowException() { 75 int i = 0; 76 try { 77 int r = 10 / i; 78 return r; 79 } catch (Exception e) { 80 System.out.println("catch exception"); 81 return i; 82 } finally { 83 System.out.println("finally handle"); 84 } 85 } 86 } 87 88 89 ///////////////////////////////////////////////////////////////////// 90 91 92 package com.lyyzoo.jvm.test01; 93 94 public class Main { 95 96 public static void main(String[] args) { 97 System.out.println("FIELD_NAME: " + User.FIELD_NAME); 98 99 User.staticPrintInfo(); 100 101 User user = new User(); 102 user.printInfo(); 103 } 104 }
三、类编译和Class 文件结构
*.java 文件被编译成 *.class 文件的过程,这个编译一般称为前端编译,主要使用 javac 来完成前端编译。Java class文件是8位字节的二进制流,数据项按顺序存储在class文件中,相邻的项之间没有任何间隔,这样可以使class文件紧凑。class 文件主要包含 版本信息、常量池、类型索引、字段表、方法表、属性表等信息。
将 User 类编译成 class 文件后,再通过 javap 反编译 class 文件,可以看到一个 class 文件大体包含的结构:
1 说明:用“【】”标识的是手动添加的注释 2 3 Mechrevo@hello-world MINGW64 /e/repo-study/test-concurrent/target/classes/com/lyyzoo/jvm/test01 4 【javap -v 命令反编译 Class】 5 $ javap -v User.class 6 Classfile /E:/repo-study/test-concurrent/target/classes/com/lyyzoo/jvm/test01/User.class 7 Last modified 2020-9-3; size 2389 bytes 8 【魔数】 9 MD5 checksum ec5a961c2a46926522bafddcb3204fb9 10 Compiled from "User.java" 11 public class com.lyyzoo.jvm.test01.User extends com.lyyzoo.jvm.test01.Person<java.lang.String> implements java.io.Serializable 12 【版本号】 13 minor version: 0 14 major version: 52 15 flags: ACC_PUBLIC, ACC_SUPER 16 【常量池】 17 Constant pool: 18 #1 = Methodref #29.#76 // com/lyyzoo/jvm/test01/Person."<init>":()V 19 #2 = String #77 // 兰博 20 #3 = Fieldref #14.#78 // com/lyyzoo/jvm/test01/User.name:Ljava/lang/String; 21 #4 = Fieldref #14.#79 // com/lyyzoo/jvm/test01/User.age:I 22 #5 = Fieldref #80.#81 // java/lang/System.out:Ljava/io/PrintStream; 23 #6 = Class #82 // java/lang/StringBuilder 24 #7 = Methodref #6.#76 // java/lang/StringBuilder."<init>":()V 25 #8 = String #83 // name: 26 #9 = Methodref #6.#84 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 27 #10 = String #85 // , age: 28 #11 = Methodref #6.#86 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 29 #12 = Methodref #6.#87 // java/lang/StringBuilder.toString:()Ljava/lang/String; 30 #13 = Methodref #88.#89 // java/io/PrintStream.println:(Ljava/lang/String;)V 31 #14 = Class #90 // com/lyyzoo/jvm/test01/User 32 #15 = String #91 // FIELD_NAME:username, AGE_MAX:100 33 #16 = Methodref #29.#92 // com/lyyzoo/jvm/test01/Person.sayHello:(Ljava/lang/Object;)V 34 #17 = String #93 // User say hello: 35 #18 = String #94 // finally handle 36 #19 = Class #95 // java/lang/Exception 37 #20 = String #96 // catch exception 38 #21 = Class #97 // java/lang/String 39 #22 = Methodref #14.#98 // com/lyyzoo/jvm/test01/User.sayHello:(Ljava/lang/String;)V 40 #23 = String #99 // Rambo 41 #24 = Fieldref #14.#100 // com/lyyzoo/jvm/test01/User.staticName:Ljava/lang/String; 42 #25 = Fieldref #14.#101 // com/lyyzoo/jvm/test01/User.staticAge:I 43 #26 = String #102 // user static init 44 #27 = String #103 // staticName= 45 #28 = String #104 // staticAge= 46 #29 = Class #105 // com/lyyzoo/jvm/test01/Person 47 #30 = Class #106 // java/io/Serializable 48 #31 = Utf8 serialVersionUID 49 #32 = Utf8 J 50 #33 = Utf8 ConstantValue 51 #34 = Long -4482416396338787067l 52 #36 = Utf8 FIELD_NAME 53 #37 = Utf8 Ljava/lang/String; 54 #38 = String #107 // username 55 #39 = Utf8 AGE_MAX 56 #40 = Utf8 I 57 #41 = Integer 100 58 #42 = Utf8 staticName 59 #43 = Utf8 staticAge 60 #44 = Utf8 name 61 #45 = Utf8 age 62 #46 = Utf8 <init> 63 #47 = Utf8 ()V 64 #48 = Utf8 Code 65 #49 = Utf8 LineNumberTable 66 #50 = Utf8 LocalVariableTable 67 #51 = Utf8 this 68 #52 = Utf8 Lcom/lyyzoo/jvm/test01/User; 69 #53 = Utf8 (Ljava/lang/String;I)V 70 #54 = Utf8 MethodParameters 71 #55 = Utf8 printInfo 72 #56 = Utf8 staticPrintInfo 73 #57 = Utf8 sayHello 74 #58 = Utf8 (Ljava/lang/String;)V 75 #59 = Utf8 str 76 #60 = Utf8 willThrowException 77 #61 = Utf8 ()I 78 #62 = Utf8 r 79 #63 = Utf8 e 80 #64 = Utf8 Ljava/lang/Exception; 81 #65 = Utf8 i 82 #66 = Utf8 StackMapTable 83 #67 = Class #90 // com/lyyzoo/jvm/test01/User 84 #68 = Class #95 // java/lang/Exception 85 #69 = Class #108 // java/lang/Throwable 86 #70 = Utf8 (Ljava/lang/Object;)V 87 #71 = Utf8 <clinit> 88 #72 = Utf8 Signature 89 #73 = Utf8 Lcom/lyyzoo/jvm/test01/Person<Ljava/lang/String;>;Ljava/io/Serializable; 90 #74 = Utf8 SourceFile 91 #75 = Utf8 User.java 92 #76 = NameAndType #46:#47 // "<init>":()V 93 #77 = Utf8 兰博 94 #78 = NameAndType #44:#37 // name:Ljava/lang/String; 95 #79 = NameAndType #45:#40 // age:I 96 #80 = Class #109 // java/lang/System 97 #81 = NameAndType #110:#111 // out:Ljava/io/PrintStream; 98 #82 = Utf8 java/lang/StringBuilder 99 #83 = Utf8 name: 100 #84 = NameAndType #112:#113 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 101 #85 = Utf8 , age: 102 #86 = NameAndType #112:#114 // append:(I)Ljava/lang/StringBuilder; 103 #87 = NameAndType #115:#116 // toString:()Ljava/lang/String; 104 #88 = Class #117 // java/io/PrintStream 105 #89 = NameAndType #118:#58 // println:(Ljava/lang/String;)V 106 #90 = Utf8 com/lyyzoo/jvm/test01/User 107 #91 = Utf8 FIELD_NAME:username, AGE_MAX:100 108 #92 = NameAndType #57:#70 // sayHello:(Ljava/lang/Object;)V 109 #93 = Utf8 User say hello: 110 #94 = Utf8 finally handle 111 #95 = Utf8 java/lang/Exception 112 #96 = Utf8 catch exception 113 #97 = Utf8 java/lang/String 114 #98 = NameAndType #57:#58 // sayHello:(Ljava/lang/String;)V 115 #99 = Utf8 Rambo 116 #100 = NameAndType #42:#37 // staticName:Ljava/lang/String; 117 #101 = NameAndType #43:#40 // staticAge:I 118 #102 = Utf8 user static init 119 #103 = Utf8 staticName= 120 #104 = Utf8 staticAge= 121 #105 = Utf8 com/lyyzoo/jvm/test01/Person 122 #106 = Utf8 java/io/Serializable 123 #107 = Utf8 username 124 #108 = Utf8 java/lang/Throwable 125 #109 = Utf8 java/lang/System 126 #110 = Utf8 out 127 #111 = Utf8 Ljava/io/PrintStream; 128 #112 = Utf8 append 129 #113 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder; 130 #114 = Utf8 (I)Ljava/lang/StringBuilder; 131 #115 = Utf8 toString 132 #116 = Utf8 ()Ljava/lang/String; 133 #117 = Utf8 java/io/PrintStream 134 #118 = Utf8 println 135 { 136 【字段表集合】 137 public static final java.lang.String FIELD_NAME; 138 descriptor: Ljava/lang/String; 139 flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL 140 ConstantValue: String username 141 142 public static final int AGE_MAX; 143 descriptor: I 144 flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL 145 ConstantValue: int 100 146 147 【方法表】 148 public com.lyyzoo.jvm.test01.User(); 149 【描述符索引】 150 descriptor: ()V 151 【访问标志】 152 flags: ACC_PUBLIC 153 【方法体代码指令】 154 Code: 155 【方法栈大小】 156 stack=2, locals=1, args_size=1 157 0: aload_0 158 1: invokespecial #1 // Method com/lyyzoo/jvm/test01/Person."<init>":()V 159 4: aload_0 160 5: ldc #2 // String 兰博 161 7: putfield #3 // Field name:Ljava/lang/String; 162 10: aload_0 163 11: bipush 25 164 13: putfield #4 // Field age:I 165 16: return 166 【属性表,方法局部变量】 167 LineNumberTable: 168 line 27: 0 169 line 17: 4 170 line 18: 10 171 line 28: 16 172 【本地变量表,方法入参】 173 LocalVariableTable: 174 Start Length Slot Name Signature 175 0 17 0 this Lcom/lyyzoo/jvm/test01/User; 176 177 public com.lyyzoo.jvm.test01.User(java.lang.String, int); 178 descriptor: (Ljava/lang/String;I)V 179 flags: ACC_PUBLIC 180 Code: 181 stack=2, locals=3, args_size=3 182 0: aload_0 183 1: invokespecial #1 // Method com/lyyzoo/jvm/test01/Person."<init>":()V 184 4: aload_0 185 5: ldc #2 // String 兰博 186 7: putfield #3 // Field name:Ljava/lang/String; 187 10: aload_0 188 11: bipush 25 189 13: putfield #4 // Field age:I 190 16: aload_0 191 17: aload_1 192 18: putfield #3 // Field name:Ljava/lang/String; 193 21: aload_0 194 22: iload_2 195 23: putfield #4 // Field age:I 196 26: return 197 LineNumberTable: 198 line 30: 0 199 line 17: 4 200 line 18: 10 201 line 31: 16 202 line 32: 21 203 line 33: 26 204 LocalVariableTable: 205 Start Length Slot Name Signature 206 【可以看出,对象实例方法的第一个参数始终都是 this,这也是为什么我们可以在方法内调用 this 的原因】 207 0 27 0 this Lcom/lyyzoo/jvm/test01/User; 208 0 27 1 name Ljava/lang/String; 209 0 27 2 age I 210 MethodParameters: 211 Name Flags 212 name 213 age 214 215 public void printInfo(); 216 descriptor: ()V 217 flags: ACC_PUBLIC 218 Code: 219 stack=3, locals=1, args_size=1 220 0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 221 3: new #6 // class java/lang/StringBuilder 222 6: dup 223 7: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V 224 10: ldc #8 // String name: 225 12: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 226 15: aload_0 227 16: getfield #3 // Field name:Ljava/lang/String; 228 19: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 229 22: ldc #10 // String , age: 230 24: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 231 27: aload_0 232 28: getfield #4 // Field age:I 233 31: invokevirtual #11 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 234 34: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 235 37: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 236 40: return 237 LineNumberTable: 238 line 37: 0 239 line 38: 40 240 LocalVariableTable: 241 Start Length Slot Name Signature 242 0 41 0 this Lcom/lyyzoo/jvm/test01/User; 243 244 public static void staticPrintInfo(); 245 descriptor: ()V 246 【访问标志】 247 flags: ACC_PUBLIC, ACC_STATIC 248 Code: 249 stack=2, locals=0, args_size=0 250 0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 251 3: ldc #15 // String FIELD_NAME:username, AGE_MAX:100 252 5: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 253 8: return 254 LineNumberTable: 255 line 42: 0 256 line 43: 8 257 【注意,静态方法第一个参数就不再是 this 了】 258 259 260 public void sayHello(java.lang.String); 261 descriptor: (Ljava/lang/String;)V 262 flags: ACC_PUBLIC 263 Code: 264 stack=3, locals=2, args_size=2 265 0: aload_0 266 1: aload_1 267 2: invokespecial #16 // Method com/lyyzoo/jvm/test01/Person.sayHello:(Ljava/lang/Object;)V 268 5: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 269 8: new #6 // class java/lang/StringBuilder 270 11: dup 271 12: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V 272 15: ldc #17 // String User say hello: 273 17: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 274 20: aload_1 275 21: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 276 24: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 277 27: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 278 30: return 279 LineNumberTable: 280 line 48: 0 281 line 49: 5 282 line 50: 30 283 LocalVariableTable: 284 Start Length Slot Name Signature 285 【第一个参数为 this】 286 0 31 0 this Lcom/lyyzoo/jvm/test01/User; 287 0 31 1 str Ljava/lang/String; 288 MethodParameters: 289 Name Flags 290 str 291 292 public int willThrowException(); 293 descriptor: ()I 294 flags: ACC_PUBLIC 295 Code: 296 stack=2, locals=5, args_size=1 297 0: iconst_0 298 1: istore_1 299 2: bipush 10 300 4: iload_1 301 5: idiv 302 6: istore_2 303 7: iload_2 304 8: istore_3 305 9: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 306 12: ldc #18 // String finally handle 307 14: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 308 17: iload_3 309 18: ireturn 310 19: astore_2 311 20: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 312 23: ldc #20 // String catch exception 313 25: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 314 28: iload_1 315 29: istore_3 316 30: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 317 33: ldc #18 // String finally handle 318 35: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 319 38: iload_3 320 39: ireturn 321 40: astore 4 322 42: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 323 45: ldc #18 // String finally handle 324 47: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 325 50: aload 4 326 52: athrow 327 【方法异常表】 328 Exception table: 329 from to target type 330 2 9 19 Class java/lang/Exception 331 2 9 40 any 332 19 30 40 any 333 40 42 40 any 334 LineNumberTable: 335 line 54: 0 336 line 56: 2 337 line 57: 7 338 line 62: 9 339 line 57: 17 340 line 58: 19 341 line 59: 20 342 line 60: 28 343 line 62: 30 344 line 60: 38 345 line 62: 40 346 line 63: 50 347 LocalVariableTable: 348 Start Length Slot Name Signature 349 7 12 2 r I 350 20 20 2 e Ljava/lang/Exception; 351 0 53 0 this Lcom/lyyzoo/jvm/test01/User; 352 2 51 1 i I 353 StackMapTable: number_of_entries = 2 354 frame_type = 255 /* full_frame */ 355 offset_delta = 19 356 locals = [ class com/lyyzoo/jvm/test01/User, int ] 357 stack = [ class java/lang/Exception ] 358 frame_type = 84 /* same_locals_1_stack_item */ 359 stack = [ class java/lang/Throwable ] 360 361 public void sayHello(java.lang.Object); 362 descriptor: (Ljava/lang/Object;)V 363 【重载泛型方法时,会多出 ACC_BRIDGE、ACC_SYNTHETIC 两个标志,ACC_BRIDGE代表是jvm自动生成的桥接方法,ACC_SYNTHETIC代表是jvm生成的不可见方法】 364 flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC 365 Code: 366 stack=2, locals=2, args_size=2 367 0: aload_0 368 1: aload_1 369 2: checkcast #21 // class java/lang/String 370 5: invokevirtual #22 // Method sayHello:(Ljava/lang/String;)V 371 8: return 372 LineNumberTable: 373 line 5: 0 374 LocalVariableTable: 375 Start Length Slot Name Signature 376 0 9 0 this Lcom/lyyzoo/jvm/test01/User; 377 MethodParameters: 378 Name Flags 379 str synthetic 380 381 【静态代码块】 382 static {}; 383 descriptor: ()V 384 flags: ACC_STATIC 385 Code: 386 stack=3, locals=0, args_size=0 387 0: ldc #23 // String Rambo 388 2: putstatic #24 // Field staticName:Ljava/lang/String; 389 5: bipush 20 390 7: putstatic #25 // Field staticAge:I 391 10: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 392 13: ldc #26 // String user static init 393 15: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 394 18: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 395 21: new #6 // class java/lang/StringBuilder 396 24: dup 397 25: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V 398 28: ldc #27 // String staticName= 399 30: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 400 33: getstatic #24 // Field staticName:Ljava/lang/String; 401 36: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 402 39: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 403 42: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 404 45: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 405 48: new #6 // class java/lang/StringBuilder 406 51: dup 407 52: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V 408 55: ldc #28 // String staticAge= 409 57: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 410 60: getstatic #25 // Field staticAge:I 411 63: invokevirtual #11 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 412 66: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 413 69: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 414 72: return 415 LineNumberTable: 416 line 13: 0 417 line 14: 5 418 line 22: 10 419 line 23: 18 420 line 24: 45 421 line 25: 72 422 } 423 Signature: #73 // Lcom/lyyzoo/jvm/test01/Person<Ljava/lang/String;>;Ljava/io/Serializable; 424 SourceFile: "User.java"