JDK之运行时数据区(Run-Time Data Areas)
原创
©著作权归作者所有:来自51CTO博客作者东山富哥的原创作品,请联系作者获取转载授权,否则将追究法律责任
文章目录
1.概述
1.Run-Time Data Areas ,大家可以理解为我们的class代码在运行阶段,在JVM内存中是如何进行存储的;
2.大家注意下:有些是JVM启动的时候存储的,有些是请求调用的过程中线程创建的;
2.Run-Time Data Areas 具体区域
2.1.Java Virtual Machine Stacks 【虚拟机栈】
1.线程私有(每一个线程会创建一个栈),虚拟机栈描述的是 Java 方法执行的内存模型,每个方法在执行时会创建一个栈帧,
栈帧中保存有局部变量表、操作数栈、动态链接和方法出口等。
2.粗略来讲 Java 内存区分为堆和栈,实际上『栈』指的往往是虚拟机栈中的局部变量表部分。
局部变量表中存放了编译期可知的各种基本数据类型、对象引用类型和 returnAddress 类型。方法运行期间局部变量表大小不变。
StackOverflowError [栈的深度不够用了]
异常 OutOfMemoryError
栈针结构 frame
关于栈针的详细介绍大家可以参考下面的地址
官网地址
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.6
概念
1.A new frame is created each time a method is invoked.
每次方法被调用的时候,栈针就会被创建;(也就是,每一个方法,就会创建一个栈针)
栈针构成
局部变量表(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也是一个局部变量
操作树栈(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);
}
}
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进制的编码
上面这种对应着的是一些编译后的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
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$
通过caculate()方法 我们去图示演示整个流程
欢迎大家下载
https://www.processon.com/diagraming/5e43aea1e4b06b291a6b4b70
2.2.The pc Register 【程序计数器】
2.3.Heap 【堆】
1.堆存储的资源,都是一些公共的资源,所有的线程可以共享;
2.堆存储的是class对象以及数组(数组本质上也是对象);
异常 OutOfMemoryError
2.4.Method Area 【方法区】
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5
1.方法区存储的资源,都是一些公共的资源,所有的线程可以共享;
2.方法区存储资源:
2.1.类的结构信息:----->静态存储结构
2.2.常量,静态变量
异常 OutOfMemoryError
2.5.Run-Time Constant Pool 【运行时常量池】
2.6.Native Method Stacks 【本地方法栈】
3.内存模型与运行时内存区的一个对应关系