文章目录


JDK之运行时数据区(Run-Time Data Areas)_html

1.概述

1.Run-Time Data Areas ,大家可以理解为我们的class代码在运行阶段,在JVM内存中是如何进行存储的;
2.大家注意下:有些是JVM启动的时候存储的,有些是请求调用的过程中线程创建的;

2.Run-Time Data Areas 具体区域

2.1.Java Virtual Machine Stacks 【虚拟机栈】

1.首先,栈是一种数据结构,特点是先进后出;
2
1.线程私有(每一个线程会创建一个栈),虚拟机栈描述的是 Java 方法执行的内存模型,每个方法在执行时会创建一个栈帧,
栈帧中保存有局部变量表、操作数栈、动态链接和方法出口等。
2.粗略来讲 Java 内存区分为堆和栈,实际上『栈』指的往往是虚拟机栈中的局部变量表部分。

局部变量表中存放了编译期可知的各种基本数据类型、对象引用类型和 returnAddress 类型。方法运行期间局部变量表大小不变。

StackOverflowError [栈的深度不够用了]

JDK之运行时数据区(Run-Time Data Areas)_java_02

异常 OutOfMemoryError

JDK之运行时数据区(Run-Time Data Areas)_局部变量_03

栈针结构 frame

关于栈针的详细介绍大家可以参考下面的地址

官网地址

​https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.6​

JDK之运行时数据区(Run-Time Data Areas)_java_04

概念

1.A new frame is created each time a method is invoked.
每次方法被调用的时候,栈针就会被创建;(也就是,每一个方法,就会创建一个栈针)

栈针构成

JDK之运行时数据区(Run-Time Data Areas)_java_05


JDK之运行时数据区(Run-Time Data Areas)_java_06

局部变量表(Local Variable)
1.所谓局部变量就是方法的参数,比如比如caculate(int p1,int p2,int p3),那么局部变量就是p1,p2,p3
2.局部变量是从索引0开始的,比如caculate(int p1,int p2,int p3),那么p1的index就是0,p2的index就是1,p3的index就是2;
3.大家注意下,下面发放中的caculate中result也是一个局部变量

JDK之运行时数据区(Run-Time Data Areas)_局部变量_07

操作树栈(Operand Stacks)
1.操作树栈,主要是执行的运行,实际上就是执行的过程中的操作流程
动态链接(Dynamic Linking)
1.我们可以认为程序在运行的过程中,根据具体的类型去选择具体的常量对象
比如多态
方法返回地址 (Invocation Completion)
1.我们知道一个方法即一个栈针,方法处理有正常处理和异常处理之分
无论是正常处理还是异常处理,都会返回一个栈针的地址,用于标示当前方法处理的位置;

图示说明出栈与入栈

package com.gaoxinfu.demo.jdk;

import org.junit.Test;
public class StackTest {

@Test
public void test(){
c(1);
}

public void c(int p1){
System.out.println("Methord c =" +p1);
this.b(1);
}

public void b(int p1){
System.out.println("Methord b =" +p1);
this.a(1);
}

public void a(int p1){
System.out.println("Methord a =" +p1);
}
}

JDK之运行时数据区(Run-Time Data Areas)_局部变量_08

1.栈的数据结构是先进后出,这个是针对的同一个方法中,如果调用了其他的方法,那么必须等到被调用的方法完成之后,并且出栈之后
接着执行该方法剩下的内容,执行完成出栈;
2.一个方法被调用就表示一个栈针;

Java虚拟机栈在执行方法的时候到底发生了什么?

Person.java
package com.gaoxinfu.demo.jdk;

public class Person {
private String name="gaoxinfu";
private int age;
private final double salary=100;
private static String hobby="volleyball";

public void say(){
System.out.println("Person say ...");
}
public static int caculate(int op1,int op2){
op1=3;
int result=op1+op2;
return result;
}

public static void main(String[] args) {
System.out.println(caculate(1,2));
}

}
之前我们知道通过javac 可以反编译成16进制的编码

JDK之运行时数据区(Run-Time Data Areas)_java_09


JDK之运行时数据区(Run-Time Data Areas)_html_10

上面这种对应着的是一些编译后的class的二进制(16进制),可读性不好,然后jdk又给我们提供了另外一种形式 javap -C Person.class
通过javap -C 去反编译查询class文件 (这里一定要注意,后面的文件是Person.class不是Person.java文件)
localhost:jdk gaoxinfu$ ls -la
total 16
drwxr-xr-x 4 gaoxinfu staff 128 Feb 20 12:26 .
drwxr-xr-x 3 gaoxinfu staff 96 Feb 20 10:53 ..
-rw-r--r-- 1 gaoxinfu staff 489 Feb 20 10:58 Person.java
-rw-r--r-- 1 gaoxinfu staff 421 Feb 20 12:26 StackTest.java
localhost:jdk gaoxinfu$ pwd
/Users/gaoxinfu/demo-idea/demo-jdk/src/main/java/com/gaoxinfu/demo/jdk
localhost:jdk gaoxinfu$ javac Person.java
localhost:jdk gaoxinfu$ ls -la
total 24
drwxr-xr-x 5 gaoxinfu staff 160 Feb 20 16:11 .
drwxr-xr-x 3 gaoxinfu staff 96 Feb 20 10:53 ..
-rw-r--r-- 1 gaoxinfu staff 862 Feb 20 16:11 Person.class
-rw-r--r-- 1 gaoxinfu staff 489 Feb 20 10:58 Person.java
-rw-r--r-- 1 gaoxinfu staff 421 Feb 20 12:26 StackTest.java
localhost:jdk gaoxinfu$ javap -c Person.class
Compiled from "Person.java"
public class com.gaoxinfu.demo.jdk.Person {
public com.gaoxinfu.demo.jdk.Person();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String gaoxinfu
7: putfield #3 // Field name:Ljava/lang/String;
10: aload_0
11: ldc2_w #4 // double 100.0d
14: putfield #6 // Field salary:D
17: return

public void say();
Code:
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #8 // String Person say ...
5: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return

public static int caculate(int, int);
Code:
0: iconst_3 #(Push the int constant <i> (-1, 0, 1, 2, 3, 4 or 5) onto the operand stack.
) 将常量值 3加载到操作树栈中
1: istore_0
2: iload_0
3: iload_1
4: iadd
5: istore_2
6: iload_2
7: ireturn

public static void main(java.lang.String[]);
Code:
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
3: iconst_1
4: iconst_2
5: invokestatic #10 // Method caculate:(II)I
8: invokevirtual #11 // Method java/io/PrintStream.println:(I)V
11: return

static {};
Code:
0: ldc #12 // String volleyball
2: putstatic #13 // Field hobby:Ljava/lang/String;
5: return
}
localhost:jdk gaoxinfu$
解析 {参考官网}

直接在下面的地址进行搜索关键词,如iconst_3 ,则搜索iconst,不要带后面_下划线和数字
​​​https://docs.oracle.com/javase/specs/jvms/se8/html/index.html​

JDK之运行时数据区(Run-Time Data Areas)_html_11

1.上面解析出来的代码,实际上是字节码顺序执行的指令;
2.关于每一个指令是什么意思,大家可以参考下面的地址
https://docs.oracle.com/javase/specs/jvms/se8/html/index.html
大家注意下,如iconst_3 其后面的3是参数
把反编译后的上面的指令输入的文件Person.txt文件中
localhost:jdk gaoxinfu$ javap -c Person.class >Person.txt
localhost:jdk gaoxinfu$

JDK之运行时数据区(Run-Time Data Areas)_java_12


JDK之运行时数据区(Run-Time Data Areas)_java_13

通过caculate()方法 我们去图示演示整个流程

欢迎大家下载

​https://www.processon.com/diagraming/5e43aea1e4b06b291a6b4b70​

JDK之运行时数据区(Run-Time Data Areas)_局部变量_14


JDK之运行时数据区(Run-Time Data Areas)_html_15


JDK之运行时数据区(Run-Time Data Areas)_html_16

2.2.The pc Register 【程序计数器】

2.3.Heap 【堆】

JDK之运行时数据区(Run-Time Data Areas)_html_17

1.堆存储的资源,都是一些公共的资源,所有的线程可以共享;
2.堆存储的是class对象以及数组(数组本质上也是对象)

异常 OutOfMemoryError

JDK之运行时数据区(Run-Time Data Areas)_局部变量_18

2.4.Method Area 【方法区】

​https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5​

JDK之运行时数据区(Run-Time Data Areas)_java_19

1.方法区存储的资源,都是一些公共的资源,所有的线程可以共享;
2.方法区存储资源:
2.1.类的结构信息:----->静态存储结构
2.2.常量,静态变量

JDK之运行时数据区(Run-Time Data Areas)_局部变量_20

异常 OutOfMemoryError

JDK之运行时数据区(Run-Time Data Areas)_局部变量_21

2.5.Run-Time Constant Pool 【运行时常量池】

1.运行时常量池位于JAVA虚拟机的方法区;
2.

2.6.Native Method Stacks 【本地方法栈】

3.内存模型与运行时内存区的一个对应关系

JDK之运行时数据区(Run-Time Data Areas)_html_22


JDK之运行时数据区(Run-Time Data Areas)_局部变量_23