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
- 运行时数据区
- Program_Counter
- JVM_stacks
- include frame
- Native_method_stacks
- Method_area
- include run-time constant pool
- Heap
- Direct_Memory
1. a class life cycle
.class
- loading, linking, initializing
- JVM
- run engine
- runtime_data_area
- GC
2. 参考资料
- jvms(Java_Virtual_Machine Specification):JVM标准书,JVM官方文档,一切结论都以这个为基准
- 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.
- PC程序计数器
- 存放下一条指令位置
- 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.
- Local_variable_table:局部变量表
- Operand_stack:操作数栈
- Dynamic_linking:动态链接
- Return_address:返回地址
1. Local_Variable_Table
局部变量表
2. Operand_Stack
- 操作数栈
- 每一个栈桢都有自己的操作数栈
- 对于
long
的处理(store and load),多数VM的实现都是原子的 - 《jls 17.7》,没必要加volatile
3. Dynamic_Linking
- java Dynamic Linking
- 《jvms 2.6.3》
- 可以理解为指向constants_pool
4. Return_address
a()
=> b()
,b方法的返回值放在什么地方
2. instructions
- instruction_set:指令集
- JVM栈的运行和指令是分不开的
- 每个线程对应一个stack,每个方法对应一个stack_frame
- 《jvms6.5. Instructions》文档里找最全的指令
1. 指令分析
<clinit>
,static<init>
,构造方法_store
,出栈_load
,压栈invoke_XXX
,调用方法
- 只有遇到
=
才会进行istore_
出栈操作,与LocalVariableTable关联 dup
将new出来的Obj引用压栈,invoke_
出栈调用方法
- 入栈:将变量push到Operand_Stack
iconst
:(-1~5)bipush
:(-128~127)sipush
:(-32768~32767)ldc
:(-2147483648~2147483647)
istore_1
:pop出Operand_Stack变量,store到index为1的LocalVariableTable中iload_2
:index为2的LocalVariableTable中变量push到Operand_Stack中iinc 2 by 1
:index为2的LocalVariableTable中变量 + 1if_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. 指令集
- 基于栈的简单(JVM)
- 基于寄存器的复杂,可是快(硬件)
- 硬件层面都是基于寄存器的
3. Stack执行流程
- 100进入Operand_Stacks
- 100出栈,赋值Local_Variables
- return结束
非static方法。局部变量表里的第一个是this,因此在方法中可以直接使用
- new:在Heap中开辟空间,并赋默认值
- dup:copy内存pointer,并压栈
- invokespecial(调用特殊方法):不消耗Operand_Stacks里的内存pointer。执行init方法,并赋初始值
- invokevirtual(调用方法):消耗Operand_Stacks
if_icmpne
:if int compare not equal
4. 内存溢出
- Heap内存溢出
- Stack内存溢出
- 直接内存溢出
- 方法区溢出
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_Stackinvokespecial.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.
1. 内部结构
- 类型信息 => 运行时常量池 => 静态变量 => JIT代码缓存 => 域信息 => 方法信息
- 方法区(Method Area)详解
- 类型信息。方法区需要存储每个加载的类(类、接口、枚举、注解)的以下类型信息
- 完整名称(包类、类名)
- 这个类的直接父类的完整名称(接口和
java.long.object
没有父类) - 这个类型的修饰符(
public, abstract, final
的某个子集) - 这个类型直接接口的一个有序列表
- 域(属性)信息
- JVM需要保存类型的域信息和域的声明顺序
- 域名称、域类型、域修饰符(
public, private, protected, static, final, volatile, transient
的某个子集)
- 方法信息。JVM需要保存所有方法的信息及其声明的顺序
- 方法的名称,返回类型,参数(数量类型,按顺序),修饰符
- 方法的字节码(bytecodes)、操作数栈、局部变量表及大小(abstract和native方法除外)
- 异常表(abstract和native方法除外)
- 每个异常处理的开始位置、结束位置、代码处理在PC中的偏移地址、被捕获的异常类的常量池索引
- non-final的类变量(static)
- 静态变量和类关联在一起,随着类的加载而加载,成为类数据在逻辑上的一部分
- 类变量被类的所有实例共享,即使没有类实例时,也可以访问它
- 补充说明:全局常量(static final)被声明为final的类变量的处理方法则不同,每个全局常量在编译时就被分配了
2. evolution
JDK | 永久代 |
---|---|
JDK1.6 | 有永久代(permanet),静态变量存储在永久代上 |
JDK1.7 | 有永久代,但已经逐步“去永久代”,字符串常量池、静态变量移除,保存到堆中 |
JDK1.8 | 无永久代。类型信息、字段、方法,常量保存在本地内存的元空间,但字符串常量池、静态变量仍然在堆中 |
永久代。逻辑上的概念,下面的是具体实现
- Perm_Gen(Permanent_Generation永久区)
< JDK7
- 字符串常量位于PermSpace
- FGC才触发
- 大小启动时指定,不能变
- MetaSpace(元数据区)
>= JDK1.8
- 字符串常量位于Heap
- 会触发FGC清理
- 不设定的话,最大就是物理内存
2. Constant_Pool
- 常量池:字节码文件中的一部分
- 运行时常量池:在方法区中
1. 常量池
一个Java源文件中的类、接口,编译后产生一个字节码文件。而字节码需要数据支持,通常这种数据很大以至于不能直接存到字节码里。换另一种方式,存到常量池,字节码包含了指向常量池的引用。常量池、可以看做是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等类型
- 存放内容:
- 字面量
- 字符串值
- 类引用
- 字段引用
- 方法引用
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.
- 当类加载到内存中后,JVM就会将《字节码常量池》中的内容存放到运行时常量池中,运行时常量池也是每个类都有一个
- 相对于Class文件常量池的另一重要特征是:具备动态性
5. Direct_Memory
- JVM可以直接访问的内核空间的内存 (OS管理的内存)
- 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.