05-Runtime_data_area

instruction 英 [ɪnˈstrʌkʃn] n. 用法说明;指示;(计算机的)指令,命令,adj. 说明用法的

specification 英 [ˌspesɪfɪˈkeɪʃ(ə)n] n. 规格,规范,明细单,说明书;明确说明,详述;(申请专利用的)发明物说明书

evolution 英 [ˌiːvəˈluːʃn] n. 进化;演变;发展;渐进

permanent 英 [ˈpɜːmənənt] adj. 永久的;永恒的;长久的 n. 烫发(等于permanent wave)

operand 英 [ˈɒpərænd] n. [计] 操作数;[计] 运算对象

1. runtime_data_area

  • 运行时数据区
image-20230412223758167
  1. Program_Counter
  2. JVM_stacks
    • include frame
  3. Native_method_stacks
  4. Method_area
    • include run-time constant pool
  5. Heap
  6. Direct_Memory

1. a class life cycle

  1. .class
    • loading, linking, initializing
  2. JVM
    • run engine
  3. runtime_data_area
  4. GC

2. 参考资料

  1. jvms(Java_Virtual_Machine Specification):JVM标准书,JVM官方文档,一切结论都以这个为基准
  2. jls(Java Language Specification):Java标准书,Java语言规范
/**
 * 1. Person => method_area
 * 2. per => jvm_stack
 * 3. new Person(); => heap
 */
Person per = new Person();

1. Program_Counter

  • Each Java Virtual Machine thread has its own pc (program counter) register.
  • At any point, each Java Virtual Machine thread is executing the code of a single method, namely the current method for that thread.
  • If that method is not native , the pc register contains the address of the Java Virtual Machine instruction currently being executed.
  1. PC程序计数器
  2. 存放下一条指令位置
  3. JVM运行,类似于这样的循环
while( not end ) {
    1.PC中的位置,找到对应位置的指令;
    2. 执行该指令;
    3. PC++;
}

2. JVM_stack

  • Each Java Virtual Machine thread has a private Java Virtual Machine stack, created at the same time as the thread.
  • A Java Virtual Machine stack stores frames
  • stack栈 = JVM_stacks + native_method_stacks
  • 每个线程对应一个stack,每个方法对应一个stack_frame

1. stack_frame栈桢

A frame is used to store data and partial results, as well as to perform dynamic linking, return values for methods, and dispatch exceptions.

  1. Local_variable_table:局部变量表
  2. Operand_stack:操作数栈
  3. Dynamic_linking:动态链接
  4. Return_address:返回地址

1. Local_Variable_Table

局部变量表

image-20230412193047287

2. Operand_Stack

  1. 操作数栈
  2. 每一个栈桢都有自己的操作数栈
  • 对于long的处理(store and load),多数VM的实现都是原子的
  • 《jls 17.7》,没必要加volatile

3. Dynamic_Linking

4. Return_address

a() => b(),b方法的返回值放在什么地方

2. instructions

  • instruction_set:指令集
  • JVM栈的运行和指令是分不开的
  • 每个线程对应一个stack,每个方法对应一个stack_frame
  • 《jvms6.5. Instructions》文档里找最全的指令

1. 指令分析

  1. <clinit>,static
  2. <init>,构造方法
  3. _store,出栈
  4. _load,压栈
  5. invoke_XXX,调用方法

  1. 只有遇到 = 才会进行 istore_ 出栈操作,与LocalVariableTable关联
  2. dup 将new出来的Obj引用压栈,invoke_ 出栈调用方法

  1. 入栈:将变量push到Operand_Stack
    1. iconst:(-1~5)
    2. bipush:(-128~127)
    3. sipush:(-32768~32767)
    4. ldc:(-2147483648~2147483647)
  2. istore_1:pop出Operand_Stack变量,store到index为1的LocalVariableTable中
  3. iload_2:index为2的LocalVariableTable中变量push到Operand_Stack中
  4. iinc 2 by 1:index为2的LocalVariableTable中变量 + 1
  5. if_icmpne:if int compare not equal
public class T9_Instructions {
    public static void main(String[] args) {
        int i = 8;
        int j = 9;
        ++i;
        ++i;

        i = j++;
        i = ++j;
        System.out.println(i);
    }
}
 0 bipush 8       // 1. 将变量push到Operand_Stack
 2 istore_1       // 2. pop出Operand_Stack变量,store到index为1的LocalVariableTable中
 3 bipush 9
 5 istore_2
 6 iinc 1 by 1    // 3. index为1的LocalVariableTable中变量+1
 9 iinc 1 by 1
// ---------------------------------------------------------
12 iload_2        // 4. index为2的LocalVariableTable中变量push到Operand_Stack
13 iinc 2 by 1
16 istore_1
// ---------------------------------------------------------
17 iinc 2 by 1
20 iload_2
21 istore_1

22 getstatic #2 <java/lang/System.out : Ljava/io/PrintStream;>
25 iload_1
26 invokevirtual #3 <java/io/PrintStream.println : (I)V>
29 return

2. 指令集

  1. 基于栈的简单(JVM)
  2. 基于寄存器的复杂,可是快(硬件)
  3. 硬件层面都是基于寄存器的

3. Stack执行流程

image-20220712095709264
  1. 100进入Operand_Stacks
  2. 100出栈,赋值Local_Variables
  3. return结束

image-20220712095802636

非static方法。局部变量表里的第一个是this,因此在方法中可以直接使用


image-20220712095811260
image-20220712095822275
  1. new:在Heap中开辟空间,并赋默认值
  2. dup:copy内存pointer,并压栈
  3. invokespecial(调用特殊方法):不消耗Operand_Stacks里的内存pointer。执行init方法,并赋初始值
  4. invokevirtual(调用方法):消耗Operand_Stacks

image-20220712095830695

if_icmpne:if int compare not equal

image-20220712095837915

4. 内存溢出

  1. Heap内存溢出
  2. Stack内存溢出
  3. 直接内存溢出
  4. 方法区溢出

5. invoke

invoke指令,会消耗Operand_Stack元素。还没人问

1. InvokeStatic
public class T4_InvokeStatic {
    public static void main(String[] args) {
        m();
    }

    public static void m() {
    }
}
0 invokestatic #2 <com/listao/jvm/c4_InstructionSet/T4_InvokeStatic.m : ()V>
3 return
// ------------------------------
0 return
2. InvokeVirtual

会消耗Operand_Stack

public class T5_InvokeVirtual {
    public static void main(String[] args) {
        new T5_InvokeVirtual().m();
    }

    public void m() {
    }
}
 0 new #2 <com/listao/jvm/c4_InstructionSet/T5_InvokeVirtual>
 3 dup
 4 invokespecial #3 <com/listao/jvm/c4_InstructionSet/T5_InvokeVirtual.<init> : ()V>
 7 invokevirtual #4 <com/listao/jvm/c4_InstructionSet/T5_InvokeVirtual.m : ()V>
10 return
// -------------------------------
0 return
3. InvokeInterface

不会消耗Operand_Stack

public class T7_InvokeInterface {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("hello");

        ArrayList<String> list2 = new ArrayList<>();
        list2.add("hello2");
    }
}
 0 new #2 <java/util/ArrayList>
 3 dup
 4 invokespecial #3 <java/util/ArrayList.<init> : ()V>
 7 astore_1

 8 aload_1
 9 ldc #4 <hello>
11 invokeinterface #5 <java/util/List.add : (Ljava/lang/Object;)Z> count 2
16 pop

17 new #2 <java/util/ArrayList>
20 dup
21 invokespecial #3 <java/util/ArrayList.<init> : ()V>
24 astore_2

25 aload_2
26 ldc #6 <hello2>
28 invokevirtual #7 <java/util/ArrayList.add : (Ljava/lang/Object;)Z>
31 pop
32 return
4. InovkeSpecial
  • invokespecial.init,不会消耗Operand_Stack
  • invokespecial.private,会消耗Operand_Stack
public class T6_InvokeSpecial {
    public static void main(String[] args) {
        T6_InvokeSpecial t = new T6_InvokeSpecial();
        t.m();
        t.n();
    }

    public final void m() {
    }

    private void n() {
    }
}
 0 new #2 <com/listao/jvm/c4_InstructionSet/T6_InvokeSpecial>
 3 dup
 4 invokespecial #3 <com/listao/jvm/c4_InstructionSet/T6_InvokeSpecial.<init> : ()V>
 7 astore_1

 8 aload_1
 9 invokevirtual #4 <com/listao/jvm/c4_InstructionSet/T6_InvokeSpecial.m : ()V>

12 aload_1
13 invokespecial #5 <com/listao/jvm/c4_InstructionSet/T6_InvokeSpecial.n : ()V>
16 return
// ---------------------------------------
0 return
// ---------------------------------------
0 return
5. InvokeDynamic
  • JVM最难的指令
  • lambda表达式或者反射或者其他动态语言Scala、Kotlin,或者CGLib、ASM,动态产生的Class,会用到的指令

3. Native_method_stacks

An implementation of the Java Virtual Machine may use conventional stacks called native method stacks.

Java调用了JNI,JVM里C和C++写的native方法,没有办法调优,也没有办法管理

4. Method_Area

  • The Java Virtual Machine has a method area that is shared among all Java Virtual Machine threads.
  • It stores per-class structures.
image-20230412195039739

1. 内部结构

  1. 类型信息。方法区需要存储每个加载的类(类、接口、枚举、注解)的以下类型信息
    • 完整名称(包类、类名)
    • 这个类的直接父类的完整名称(接口和java.long.object没有父类)
    • 这个类型的修饰符(public, abstract, final的某个子集)
    • 这个类型直接接口的一个有序列表
  2. 域(属性)信息
    • JVM需要保存类型的域信息和域的声明顺序
    • 域名称、域类型、域修饰符(public, private, protected, static, final, volatile, transient的某个子集)
  3. 方法信息。JVM需要保存所有方法的信息及其声明的顺序
    • 方法的名称,返回类型,参数(数量类型,按顺序),修饰符
    • 方法的字节码(bytecodes)、操作数栈、局部变量表及大小(abstract和native方法除外)
    • 异常表(abstract和native方法除外)
    • 每个异常处理的开始位置、结束位置、代码处理在PC中的偏移地址、被捕获的异常类的常量池索引
  4. non-final的类变量(static)
    • 静态变量和类关联在一起,随着类的加载而加载,成为类数据在逻辑上的一部分
    • 类变量被类的所有实例共享,即使没有类实例时,也可以访问它
    • 补充说明:全局常量(static final)被声明为final的类变量的处理方法则不同,每个全局常量在编译时就被分配了

2. evolution

JDK永久代
JDK1.6有永久代(permanet),静态变量存储在永久代上
JDK1.7有永久代,但已经逐步“去永久代”,字符串常量池、静态变量移除,保存到堆中
JDK1.8无永久代。类型信息、字段、方法,常量保存在本地内存的元空间,但字符串常量池、静态变量仍然在堆中
image-20230412203956873
image-20230412204008870
image-20230412204026244

永久代。逻辑上的概念,下面的是具体实现

  1. Perm_Gen(Permanent_Generation永久区)< JDK7
    • 字符串常量位于PermSpace
    • FGC才触发
    • 大小启动时指定,不能变
  2. MetaSpace(元数据区)>= JDK1.8
    • 字符串常量位于Heap
    • 会触发FGC清理
    • 不设定的话,最大就是物理内存

2. Constant_Pool

  1. 常量池:字节码文件中的一部分
  2. 运行时常量池:在方法区中

1. 常量池

image-20230413092520164

一个Java源文件中的类、接口,编译后产生一个字节码文件。而字节码需要数据支持,通常这种数据很大以至于不能直接存到字节码里。换另一种方式,存到常量池,字节码包含了指向常量池的引用。常量池、可以看做是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等类型

  • 存放内容:
    1. 字面量
    2. 字符串值
    3. 类引用
    4. 字段引用
    5. 方法引用

2. Run-Time_Constant_Pool

A run-time constant pool is a per-class or per-interface run-time representation of the constant_pool table in a class file.

  1. 当类加载到内存中后,JVM就会将《字节码常量池》中的内容存放到运行时常量池中,运行时常量池也是每个类都有一个
  2. 相对于Class文件常量池的另一重要特征是:具备动态性

5. Direct_Memory

  1. JVM可以直接访问的内核空间的内存 (OS管理的内存)
  2. NIO,提高效率,实现zero_copy(省去了内核内存、用户内存copy过程)

6. Heap

  • The Java Virtual Machine has a heap that is shared among all Java Virtual Machine threads.
  • The heap is the run-time data area from which memory for all class instances and arrays is allocated.