一、JVM运行时数据区

image.png

线程独占:每个线程都会有它独立的空间,随线程生命周期而创建和销毁
线程共享:所有线程能访问这块内存数据,随虚拟机或者GC而创建和销毁

1、方法区

JVM用来存储加载的类信息、常量、静态变量、编译后的代码等数据
虚拟机规范中这是一个逻辑区划。具体实现根据不同虚拟机来实现。
如:Oracle的HotSpot在java7中方法区放在永久代,java8放在元数据空间,并且通过GC机制对这个区域进行管理

2、堆内存

堆内存还可以细分为:老年代、新生代(Eden、From Survivor、To Survivor)
JVM启动时创建,存放对象的实例。垃圾回收器主要就是管理堆内存。
如果满了,就会出现OutOfMemroyError

3、虚拟机栈

虚拟机栈,每个线程都在这个空间有一个私有的空间。
线程栈由多个栈帧(Stack Frame)组成。
一个线程会执行一个或多个方法,一个方法对应一个栈帧
栈帧内容包含:局部变量表、操作数栈、动态链接、方法返回地址、附加信息等。
栈内存默认最大是1M,超出则抛出StackOverflowError

4、本地方法栈

和虚拟机栈功能类似,虚拟机栈是为虚拟机执行JAVA方法而准备的,本地方法栈是为虚拟机使用Native本地方法而准备的。
虚拟机规范没有规定具体的实现,由不同的虚拟机厂商去实现。
HotSpot虚拟机中虚拟机栈和本地方法栈的实现是一样的。同样,超出大小以后也会抛出StackOverFlowError。

5、程序计数器

程序计数器(Program Counter Register)记录当前线程执行字节码的位置,存储的是字节码指令地址,如果执行Native方法,则计数器值为空。
每个线程都在这个空间有一个私有的空间,占用内存空间很少。
CPU同一时间,只会执行一条线程中的指令。JVM多线程会轮流切换并分配CPU执行实现的方式。为了线程切换后,需要通过程序计数器,来恢复正确的执行位置。

二、查看class文件内容

使用Demo01.java进行测试,编译成class,使用javap命令的解析结果说明,查看Demo01.txt文件

JDK环境:

  1. java version "1.8.0_221"
  2. Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
  3. Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)

Demo01.java

  1. public class Demo01 {
  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. }

//编译
javac Demo01.java
//javap查看内容
javap Demo01.class>Demo01.txt

1、版本号/访问控制

  1. public class Demo01
  2. minor version: 0 //次版本号
  3. major version: 52 //主版本号
  4. flags: ACC_PUBLIC, ACC_SUPER //访问控制

版本号规则:JDK5,6,7,8分别对应49,50,51,52
访问标志:
image.png

2、常量池

  1. Constant pool:
  2. #1 = Methodref #5.#14 // java/lang/Object."<init>":()V
  3. #2 = Fieldref #15.#16 // java/lang/System.out:Ljava/io/PrintStream;
  4. #3 = Methodref #17.#18 // java/io/PrintStream.println:(I)V
  5. #4 = Class #19 // Demo01
  6. #5 = Class #20 // java/lang/Object
  7. #6 = Utf8 <init>
  8. #7 = Utf8 ()V
  9. #8 = Utf8 Code
  10. #9 = Utf8 LineNumberTable
  11. #10 = Utf8 main
  12. #11 = Utf8 ([Ljava/lang/String;)V
  13. #12 = Utf8 SourceFile
  14. #13 = Utf8 Demo01.java
  15. #14 = NameAndType #6:#7 // "<init>":()V
  16. #15 = Class #21 // java/lang/System
  17. #16 = NameAndType #22:#23 // out:Ljava/io/PrintStream;
  18. #17 = Class #24 // java/io/PrintStream
  19. #18 = NameAndType #25:#26 // println:(I)V
  20. #19 = Utf8 Demo01
  21. #20 = Utf8 java/lang/Object
  22. #21 = Utf8 java/lang/System
  23. #22 = Utf8 out
  24. #23 = Utf8 Ljava/io/PrintStream;
  25. #24 = Utf8 java/io/PrintStream
  26. #25 = Utf8 println
  27. #26 = Utf8 (I)V

类信息包含的静态常量,编译之后就能确认
image.png

3、构造方法

  1. public Demo01();
  2. descriptor: ()V
  3. flags: ACC_PUBLIC
  4. Code:
  5. stack=1, locals=1, args_size=1
  6. 0: aload_0
  7. 1: invokespecial #1 // Method java/lang/Object."<init>":()V
  8. 4: return
  9. LineNumberTable:
  10. line 1: 0

Demo01这个示例中,我们并没有写构造函数。
由此可见,没有定义构造函数时,会有隐式的无参构造函数

4、程序入口main方法

  1. public static void main(java.lang.String[]);
  2. descriptor: ([Ljava/lang/String;)V
  3. flags: ACC_PUBLIC, ACC_STATIC
  4. Code:
  5. stack=3, locals=5, args_size=1
  6. 0: sipush 500
  7. 3: istore_1
  8. 4: bipush 100
  9. 6: istore_2
  10. 7: iload_1
  11. 8: iload_2
  12. 9: idiv
  13. 10: istore_3
  14. 11: bipush 50
  15. 13: istore 4
  16. 15: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
  17. 18: iload_3
  18. 19: iload 4
  19. 21: iadd
  20. 22: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
  21. 25: return

flags: ACC_PUBLIC, ACC_STATIC描述了方法的访问控制
stack=3, locals=5, args_size=1描述了方法的本地变量数量、参数数量、方法对应栈帧中操作数栈的深度

JVM执行引擎去执行这些源码编译后的指令码。
javap翻译出来是操作符,class文件内存储的是指令码。
前面的数字是偏移量(字节),jvm根据这个去区分不同的指令。

三、程序运行流程分析

1、加载到方法区

image.png

2、JVM创建线程来执行代码

image.png