class文件内容
- class文件包含Java程序执行的字节码
- 数据严格按照格式紧凑排列在class文件的二进制流,中间无分割符
- 文件开头有一个0xcafebabe(16进制)特殊的标志
JVM运行时数据区
线程独占: 每个线程都会有它独立的空间,随线程的生命周而创建和销毁
线程共享: 所有线程都能访问这块内存数据,随虚拟机或GC而创建和销毁
方法区
- 方法区是各个线程共享的内存区域
- 用于存储已被虚拟机加载的类信息, 常量,静态变量, 即时编译后的代码等数据
- 虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分, 但它却有一个别名叫Non-Heap, 目的应该是与Java堆区分开来
- Oracle的Hotspot虚拟机在Java7中方法区放在'永久代'(Permanent Generation), Java8放在元数据空间, 并且通过GC机制对这个区域进行管理
- 运行时常量池是方法区的一部分
Java堆
- Java堆是被所有共享的一块内存区域, 在虚拟机启动时创建
- 存放对象的实例
- 垃圾收集器的主要管理区域
- Java堆还可以细分为: 新生代和老年代, 新生代又可以细分为Eden 空间, From Survivor空间 和To Survivor空间
- 空间满了会抛OutOfMemoryError
Java虚拟机栈
- Java虚拟机栈是线程私有的, 它的生命周期与线程相同
- Java虚拟机栈描述的是Java方法执行的内存模型: 每个方法被执行的的时候都会同时创建一个栈帧(栈帧是方法运行时的基础数据结构)用于存储局部变量表, 操作栈, 动态链接, 方法出口等信息.
- 栈内存默认最大是1M, 超出则抛出StackOverFlowError
本地方法栈
- 本地方法栈与虚拟机栈的功能类似, 虚拟机栈是为虚拟机执行Java方法而准备的, 本地方法栈是为虚拟机使用Native本地方法而准备的
- Hotspot虚拟机中虚拟机栈与本地方法栈的实现方式一样, 超出大小后也会抛StackOverFlowError
程序计数器
- 程序计数器是线程私有的一块较小的内存空间
- 记录当前线程执行的字节码位置, 存储的是字节码指令地址, 如果执行Native方法, 则计数器为空
- CPU同一时间, 只会执行一条线程的指令. JVM多线程会轮流切换并分配CPU的执行时间的方式. 为了线程切换后, 需要通过程序计数器来恢复正确的执行位置
查看class文件内容
使用Demo.Java进行测试, 运行javac Demo.java编译成class文件, 然后运行javap -v Demo.class > Demo.txt查看class文件内容
Demo.Java
1public class Demo{ 2 public static void main(String[] args){ 3 int x = 500; 4 int y = 100; 5 int a = x / y; 6 int b = 50; 7 System.out.println(a + b); 8 } 9}
Demo.txt
1Classfile /E:/*/Demo.class 2 Last modified 2019-6-30; size 412 bytes 3 MD5 checksum efd785af33e58aa9fc9834110b74b87b 4 Compiled from "Demo.java" 5public class Demo 6 minor version: 0 //次版本号 7 major version: 52 //主版本号 版本号规则: JDK5,6,7,8分别对应49,50,51,52 8 flags: ACC_PUBLIC, ACC_SUPER //访问标志 9Constant pool: // 常量池 类信息包含的静态常量, 编译之后就能确认 10 #1 = Methodref #5.#14 // java/lang/Object."<init>":()V 11 #2 = Fieldref #15.#16 // java/lang/System.out:Ljava/io/PrintStream; 12 #3 = Methodref #17.#18 // java/io/PrintStream.println:(I)V 13 #4 = Class #19 // Demo 14 #5 = Class #20 // java/lang/Object 15 #6 = Utf8 <init> 16 #7 = Utf8 ()V 17 #8 = Utf8 Code 18 #9 = Utf8 LineNumberTable 19 #10 = Utf8 main 20 #11 = Utf8 ([Ljava/lang/String;)V 21 #12 = Utf8 SourceFile 22 #13 = Utf8 Demo.java 23 #14 = NameAndType #6:#7 // "<init>":()V 24 #15 = Class #21 // java/lang/System 25 #16 = NameAndType #22:#23 // out:Ljava/io/PrintStream; 26 #17 = Class #24 // java/io/PrintStream 27 #18 = NameAndType #25:#26 // println:(I)V 28 #19 = Utf8 Demo 29 #20 = Utf8 java/lang/Object 30 #21 = Utf8 java/lang/System 31 #22 = Utf8 out 32 #23 = Utf8 Ljava/io/PrintStream; 33 #24 = Utf8 java/io/PrintStream 34 #25 = Utf8 println 35 #26 = Utf8 (I)V 36{ 37 public Demo(); // 默认隐式无参的构造函数 38 descriptor: ()V 39 flags: ACC_PUBLIC 40 Code: 41 stack=1, locals=1, args_size=1 42 0: aload_0 43 1: invokespecial #1 // Method java/lang/Object."<init>":()V 44 4: return 45 LineNumberTable: 46 line 1: 0 47 48 public static void main(java.lang.String[]); //程序的入口main方法 49 descriptor: ([Ljava/lang/String;)V 50 flags: ACC_PUBLIC, ACC_STATIC //访问控制 51 Code: 52 stack=3, locals=5, args_size=1 //方法栈栈帧中操作数栈的深度,本地变量数量,参数数量 53 0: sipush 500 //Jvm执行引擎执行这些源码编译过后的指令码, javap翻译出来的 54 3: istore_1 //是操作符, class 文件内存储的是指令码, 前面的数据是偏移量, 55 4: bipush 100 //Jvm根据这个去区分不同的指令. 详情参照'JVM指令码表' 56 6: istore_2 57 7: iload_1 58 8: iload_2 59 9: idiv 60 10: istore_3 61 11: bipush 50 62 13: istore 4 63 15: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 64 18: iload_3 65 19: iload 4 66 21: iadd 67 22: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 68 25: return 69 LineNumberTable: 70 line 3: 0 71 line 4: 4 72 line 5: 7 73 line 6: 11 74 line 7: 15 75 line 8: 25 76} 77SourceFile: "Demo.java" 78
程序完整运行分析
