【java程序运行原理解析】—— java程序的运行过程

x33g5p2x  于2022-01-04 转载在 Java  
字(3.8k)|赞(0)|评价(0)|浏览(1177)

java程序运行过程

一个java程序的执行按照以下的步骤进行

  1. 编辑源代码(.java)
  2. 将源代码.java编译成字节码文件.class
  3. java虚拟机对字节码文件进行解析执行输出结果。

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

JVM运行时数据区中各部分的作用:

  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执行时间的方式。为了线程切换后,需要通过程序计数器,来恢复正确的执行位置。

字节码文件解析

编写一个示例源代码Procedure.java。
Procedure.java

package test;

public class Procedure {
    public static void main(String[] args) {
        int a = 1;
        int b = 2;
        System.out.println(a + b);
    }
}

使用javac Procedure.java将源代码Procedure.java编译成Procedure.class字节码文件。
sublime可直接查看.class文件内容

cafe babe 0000 0034 001b 0a00 0500 0e09
000f 0010 0a00 1100 1207 0013 0700 1401
0006 3c69 6e69 743e 0100 0328 2956 0100
0443 6f64 6501 000f 4c69 6e65 4e75 6d62
6572 5461 626c 6501 0004 6d61 696e 0100
1628 5b4c 6a61 7661 2f6c 616e 672f 5374
7269 6e67 3b29 5601 000a 536f 7572 6365
4669 6c65 0100 0e50 726f 6365 6475 7265
2e6a 6176 610c 0006 0007 0700 150c 0016
0017 0700 180c 0019 001a 0100 0e74 6573
742f 5072 6f63 6564 7572 6501 0010 6a61
7661 2f6c 616e 672f 4f62 6a65 6374 0100
106a 6176 612f 6c61 6e67 2f53 7973 7465
6d01 0003 6f75 7401 0015 4c6a 6176 612f
696f 2f50 7269 6e74 5374 7265 616d 3b01
0013 6a61 7661 2f69 6f2f 5072 696e 7453
7472 6561 6d01 0007 7072 696e 746c 6e01
0004 2849 2956 0021 0004 0005 0000 0000
0002 0001 0006 0007 0001 0008 0000 001d
0001 0001 0000 0005 2ab7 0001 b100 0000
0100 0900 0000 0600 0100 0000 0700 0900
0a00 0b00 0100 0800 0000 3200 0300 0300
0000 0e04 3c05 3db2 0002 1b1c 60b6 0003
b100 0000 0100 0900 0000 1200 0400 0000
0900 0200 0a00 0400 0b00 0d00 0c00 0100
0c00 0000 0200 0d

然后使用javap -v Procedure.class>查看字节码文件解析内容。

Procedure.class

Classfile /E:/workspace/IdeaProject/java/src/test/Procedure.class
  Last modified 2021-12-29; size 407 bytes
  MD5 checksum 1ac66dc40972a468ce317bf3232655a7
  Compiled from "Procedure.java"
public class test.Procedure
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#14         // java/lang/Object."<init>":()V
   #2 = Fieldref           #15.#16        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #17.#18        // java/io/PrintStream.println:(I)V
   #4 = Class              #19            // test/Procedure
   #5 = Class              #20            // java/lang/Object
   #6 = Utf8               <init>
   #7 = Utf8               ()V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               main
  #11 = Utf8               ([Ljava/lang/String;)V
  #12 = Utf8               SourceFile
  #13 = Utf8               Procedure.java
  #14 = NameAndType        #6:#7          // "<init>":()V
  #15 = Class              #21            // java/lang/System
  #16 = NameAndType        #22:#23        // out:Ljava/io/PrintStream;
  #17 = Class              #24            // java/io/PrintStream
  #18 = NameAndType        #25:#26        // println:(I)V
  #19 = Utf8               test/Procedure
  #20 = Utf8               java/lang/Object
  #21 = Utf8               java/lang/System
  #22 = Utf8               out
  #23 = Utf8               Ljava/io/PrintStream;
  #24 = Utf8               java/io/PrintStream
  #25 = Utf8               println
  #26 = Utf8               (I)V
{
  public test.Procedure();
    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 7: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=3, args_size=1
         0: iconst_1
         1: istore_1
         2: iconst_2
         3: istore_2
         4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         7: iload_1
         8: iload_2
         9: iadd
        10: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        13: return
      LineNumberTable:
        line 9: 0
        line 10: 2
        line 11: 4
        line 12: 13
}
SourceFile: "Procedure.java"

字节码文件解析内容分析

1、版本号/访问控制

版本号对应关系
版本号规则: JDK5,6,7,8分别对应49,50,51,52

访问标志


2、常量池

类型解析

3、构造方法

4、程序入口main方法


JVM指令码参考指令码表:https://blog.csdn.net/weixin_43598687/article/details/122219111

相关文章