转载

后端精进笔记01:JVM运行的核心逻辑

Java代码的一生大致如下图所示,其中,JVM运行时数据区是我们需要重点关注的部分:

后端精进笔记01:JVM运行的核心逻辑

1.1 所有线程共享部分

方法区:存储JVM加载的类信息、常量、静态变量、编译后的代码等数据。在虚拟机规范中,对于方法区的定义是一个不严谨的、逻辑上的概念,实际上各虚拟机会有不同的实现。

堆内存:JVM启动时创建该区域,主要用于存储对象的实例,GC垃圾回收主要是处理堆内存。

1.2 各线程独有部分

栈内存:或称为虚拟机栈,由多个栈帧组成,一个JVM线程会执行一个或多个方法,而__一个方法对应一个栈帧__。一个栈帧包括:局部变量表、操作数栈等部分。每个线程的栈内存大小默认是1M,超出则报StackOverflowError。

本地方法栈:顾名思义,是为JVM执行native方法准备的。

程序计数器:由于CPU在单位轮片时间内,只执行一个线程的指令。每个线程的程序计数器记录当前线程执行的字节码位置,存储的是字节码的地址(如果执行的native方法,则计数器数值为空)。这样在线程切换出去再切换回来时,就能找到上次执行的指令位置,然后继续执行。

二、小试牛刀:class字节码文件内容追踪

2.1 准备工作

2.1.1 Demo.java

public class Demo01 { public static void main(String[] args) { int a = 200; int b = 100; int c = a / b; int d = 3; System.err.println(c + d); } } 复制代码

2.1.2 编译(javac)并解析(javap) Demo.class

# 编译 javac Demo01.java # 解析。并将指令内容输出到Demo01.txt中 javap -v Demo01.class > Demo01.txt 复制代码

2.1.3 分析class文件中的指令

解析后的指令内容如下:

Classfile /Users/zephyrlai/IdeaProjects/zephyr/java-arch/src/cn/zephyr/ch1/Demo01.class Last modified 2020-3-10; size 429 bytes MD5 checksum 5c8846f77018cc43a36eeba64ff68cbd Compiled from "Demo01.java" public class cn.zephyr.ch1.Demo01 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.err:Ljava/io/PrintStream; #3 = Methodref #17.#18 // java/io/PrintStream.println:(I)V #4 = Class #19 // cn/zephyr/ch1/Demo01 #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 Demo01.java #14 = NameAndType #6:#7 // "<init>":()V #15 = Class #21 // java/lang/System #16 = NameAndType #22:#23 // err:Ljava/io/PrintStream; #17 = Class #24 // java/io/PrintStream #18 = NameAndType #25:#26 // println:(I)V #19 = Utf8 cn/zephyr/ch1/Demo01 #20 = Utf8 java/lang/Object #21 = Utf8 java/lang/System #22 = Utf8 err #23 = Utf8 Ljava/io/PrintStream; #24 = Utf8 java/io/PrintStream #25 = Utf8 println #26 = Utf8 (I)V { public cn.zephyr.ch1.Demo01(); 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 9: 0 public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=5, args_size=1 0: bipush 200 2: istore_1 3: sipush 100 6: istore_2 7: iload_1 8: iload_2 9: idiv 10: istore_3 11: iconst_3 12: istore 4 14: getstatic #2 // Field java/lang/System.err:Ljava/io/PrintStream; 17: iload_3 18: iload 4 20: iadd 21: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 24: return LineNumberTable: line 11: 0 line 12: 3 line 13: 7 line 14: 11 line 15: 14 line 16: 24 } SourceFile: "Demo01.java" 复制代码

可以看到大致可以分为如下4块

  • 前4行:无关代码内容的描述信息

  • 5~8行:版本号、访问标志

    • (major-verion)版本号:jdk5、6、7、8分别是49、50、51、52

    • 访问标志:

      标志名称 标志值 含义
      ACC_PUBLIC 0x0001 标记为public类型
      ACC_FINAL 0x0010 标记被声明为final(仅类能设置)
      ACC_SUPER 0x0020 jdk1.2之后为true(是否使用invokespecial字节码指令)
      ACC_INTERFACE 0x0200 标记为接口
      ACC_ABSTRACT 0x0400 标记为abstract类型(对于接口、抽象类则为true)
      ACC_SYNTHETIC 0x1000 标记这个类不是用户产生的
      ACC_ANNOTATION 0x2000 标记为注解类
      ACC_ENUM 0x4000 标记为枚举类
  • 以Constant pool为起点的常量池(区别于String常量池):主要存储类信息中包含的静态常量,即编译当前类之后,所用到的常量。例如类名、方法名等。常见的常量类型如下:

    后端精进笔记01:JVM运行的核心逻辑
  • {} 包裹的业务代码,而这块业务代码包含了2块:

    • 默认无参构造器
    • main方法
      stack=3, locals=5, args_size=1 

2.2 程序运行分析

2.2.1 加载class文件

主要加载各种类信息、以及类中用到的常量、静态变量等,存储在方法区。创建类的实例,存储在堆内存中。

原文  https://juejin.im/post/5e67c04251882549724e3770
正文到此结束
Loading...