- 参考文章
- java内存结构
- 堆(heap)
- 内存中共享的运行时数据区域,用于存储实例化的java对象,当jvm启动时,该区域会被实例化
- 在当前区域中只会存在java class对象以及数组数据,对于堆的空间大小可以通过启动参数 设置实现固定或动态
- Jvm提供初始化和修改堆空间大小的用户控制;当 new 关键字在使用时,实例化的对象会被存放在堆空间中,但对于当前的对象的引用数据,会被存储在栈中
- 每个jvm有且仅有一个heap区域
- 对于 heap区域gc是强制的
- Heap实际是由 eden + survivor(0/1) + oldGeneration
- 方法区(method area):实际也是属于heap的
- 属于heap中的逻辑区域,在jvm启动时创建
- 为class 结构/方法数据/构造器字段数据 分配的内存区域,空间大小也可以是固定或动态的;
- 可以固定大小或动态扩容, 无需是连续的内存空间
- 虽然 method area 属于 heap,但对于当前区域gc并非是强制的
- 对于 run-time constant pool 是属于 method-area:主要是存储 编译时常量数据(字符串和数字)以及运行时的方法以及字段引用
- 栈(stack)
- 在线程创建时,栈也会随之创建,栈是属于线程的私有区域;当方法存在返回值和动态链接库,被用来存储数据和部分结果
- 栈空间可以是固定的或动态的,也可以通过java代码在创建线程时来指定栈空间大小
- 栈的内存空间无需是连续内存
- 编程语言中的栈定义
- 对于调用栈也可以称为栈,调用者会将返回的地址压入栈中然后调用下一个执行,当他完成时,调用栈的结果有可能出栈也有可能入栈以及转移指定地址的控制权;如果调用其他的行为时,则会将返回的地址写入调用的栈中;对于程序的执行就是出栈和入栈的结果
- 对于高级语言中,特殊点在于对于调用栈的操作是隐藏的,只会提供相关的操作方法,而不允许直接操作内存
- java 中的栈
- 每个jvm线程都会拥有一个私有的栈空间,在创建线程时同步创建当前线程的栈空间;
- jvm栈中存储的是帧(数据句柄)
- jvm栈和c的栈一致,都是存储方法局部变量和运行时结果;
- 由于jvm中栈空间绝对不会被直接操作;堆分配栈帧
- jvm栈的空间不需要是连续内存
- 本地方法栈(native method stack)
- 也可以成为C语言栈,native method并不是由java代码生成,其内存只和线程关联,在线程创建时会分配内存, 也可以固定大小或动态
- program counter registers
- 每个执行特殊方法任务的jvm线程都会存在一个 程序计数器寄存器相关联;对于存在程序计数器的非native方法会存储有效的JVM指令地址,而 对于native方法中,该程序计数器的值时未定义的;
- 在一些特殊的平台中,程序计数器寄存器支持存储返回数据的内存地址或一个 native 指针
- 非堆(non-heap)
- Metaspace : 元数据 ,一般包含一些class 数据
- code cache:
- 对于code cache首先需要了解 java编译过程,以及jit
- 对于一个java代码的执行过程; 首先 前端编译(javac): .java -> .class ;对于.class的执行并不能直接被cpu识别;
- 此时就引出了后端编译器(执行 class 数据)
- 解释执行: 对字节码进行逐行解释运行,运行效率较低
- 编译执行:在第一次正式执行前,将class翻译为机器码,供cpu执行调用;优点是运行效率高,但缺点是第一次编译为机器码时效率较低
- 分层编译:
- 对于编译后的机器码数据,此时就会存储在codecache区域; 但在该区域不止包含jit编译后的机器码,也包含 jni (native)代码
- 原生类型数据存储
- 对于 编译时初始化的原生数据(int等),会在编译时存放到run-time constant pool中;
- 对于方法参数中的原生数据是该方法执行时压入栈中的;
- 对于类的属性,会在当前类实例化时直接存放在当前对象实例所在的堆区域
public class JVMPrimaryDataDemo {
// 对于当前常量数据,在类加载期间时将class文件转为Class对象后,初始化时会将当前数据存放到run-time constant pool 区域
public static final int CONSTANT_COMPILER_DATA = 1;
public int j = 4; // 对于该字段的数据是存放在构造方法中的;是存在于实例化对象中(堆)
public static void main(String[] args) {
// 而对于方法中的数据在运行时会被直接加载到栈中
int i = 3; // 该数字在编译后会被翻译为 iconst_数值
}
/**
* 对 编译后的class文件 使用javap -v 解析
* // 常量池 == run-time constants pool
* Constant pool:
* #1 = Methodref #2.#3 // java/lang/Object."<init>":()V
* #2 = Class #4 // java/lang/Object
* #3 = NameAndType #5:#6 // "<init>":()V
* #4 = Utf8 java/lang/Object
* #5 = Utf8 <init>
* #6 = Utf8 ()V
* #7 = Class #8 // com/xing/level/JVMPrimaryDataDemo
* #8 = Utf8 com/xing/level/JVMPrimaryDataDemo
* #9 = Utf8 CONSTANT_COMPILER_DATA // 字段名
* #10 = Utf8 I
* #11 = Utf8 ConstantValue
* #12 = Integer 1 // 声明的常量数据
* #13 = Utf8 Code
* #14 = Utf8 LineNumberTable
* #15 = Utf8 LocalVariableTable
* #16 = Utf8 this // 当前对象引用
* #17 = Utf8 Lcom/xing/level/JVMPrimaryDataDemo;
* #18 = Utf8 main // 方法名
* #19 = Utf8 ([Ljava/lang/String;)V
* #20 = Utf8 args // 参数名
* #21 = Utf8 [Ljava/lang/String;
* #22 = Utf8 i // 局部变量名
* #23 = Utf8 SourceFile
* #24 = Utf8 JVMPrimaryDataDemo.java
*/
}