Java Memory Origin Java内存区域
[TOC]
Java Memory Origin 全称Java Runtime Memory Origin。
详细划分共五大区域:Method Area,Heap,VM Stack,Native Method Stack,Program Counter Register.
有一种比较粗糙的划分就是堆栈划分。堆栈划分中的
一、堆栈划分
把Java内存分为堆内存和栈内存(Heap & Stack),的分法比较粗糙。但体现了大多数程序员最关注的、与对象内存分配最密切的内存区域是这两块。
- 其中"栈"指的是五大分区中VM Stack中局部变量表部分。
- "堆"指的是Heap
二、五大内存区域划分
Memory Origin | Thread Private、Share |
---|---|
Method Area | Share |
Heap | Share |
VM Stack | Private |
Native Method Stack | Private |
Program Counter Register | Private |

- JVM在执行Java程序的过程中,将它所管理的内存划分成五大区域,前两个是所有线程共享的数据区,后三个是隔离数据区。其中VM Stack\Program Counter Register是线程私有的。
- 有的区域随着虚拟机进程的启动而存在,有的则依赖用户线程的启动结束而建立销毁。
1.Method Area 方法区
共享数据区,别名Non-Heap,永久代(HotSpot for Jdk8 已经移除)
- jdk8以前,很多人称该区为"永久代"(Permanent Generation),仅仅是因为HotSpot把GC分代收集扩展至方法区,使用分代中的永久代来实现了方法区,hotSpot for Jdk8及以后及其他虚拟机不存在永久代的概念。
- jdk8及以后hotSpot采用 Metaspace(动态调整大小,最大可达物理内存) 替代 PermGen(只能启动时指定,最大64M)来存储Method Area
- 用来存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
- 异常:OutOfMemoryError
1.1Runtime Constant Pool 运行时常量池
方法区的一部分,存放编译器生成的各种字面量和符号引用。
2.Heap 堆
共享数据区,线程共享。
- 垃圾收集器管理的主要区域,也叫做GC堆(Garbage Collected Heap)。
- 最大的一块内存;作用是存放对象实例,(几乎所有的对象实例都是这里分配,"几乎"是指新出的一些优化技术)。
- 可根据内存回收角度或者内存分配角度划分,无论如何划分都与存放内容无关,存储的都仍然是对象实例。进一步划分的目的是未来更好的回收内存,或者更快的分配内存。
- 异常:OutOfMemoryError
2.1内存回收角度划分
细分为:Eden空间、From Survivor空间、To Survivor空间等。
粗分为新生代、老年代。
2.2内存分配角度划分
- 线程共享的Heap中可能分配多个线程私有的分配缓冲区Thread Local Allocation Buffer TLAB
- 多线程内存分配的时涉及到线程安全的问题,一般有两种解决办法:1:加锁。2.TLAB,单个线程的预分配内存满了,才需要同步锁定。
3.VM Stack 虚拟机栈
隔离数据区,线程私有。
VM Stack记录的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame),用来存储局部变量表(堆栈分法的"栈"区域指的就是这块)、操作数栈、动态链接、方法出口等信息。
每个方法调用直至执行完成的过程就对应这一个栈帧在VM Stack中入栈到出栈的过程。
- 异常:StackOverflowError & OutOfMemoryError
3.1局部变量表
局部变量表部分存放了
- 编译期可知的8基本数据类型,64位的long,double会占据两个Slot局部变量空间。
- 对象引用(reference类型,指向一个对象起始地址的引用指针、代表对象的句柄或其他与此对象相关的位置)。
- returnAddress类型(指向一条字节码指令的地址)
局部变量表所需要的内存在编译期间确定。当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的。方法运行期间不会改变局部变量表的大小
4. Native Method Stack 本地方法栈
隔离数据区,线程私有,HotSpot的实现将之合并到了VM Stack.
- 用户VM stack作用相似,不同的是VM stack为Java方法(也就是字节码)服务,Native Method Stack 为虚拟机使用到的Native方法服务。Native Method是非Java的方法实现,如对OS的一些调用,C++实现的一些外部方法。
- 异常:StackOverflowError & OutOfMemoryError
5. Program Counter Register 程序计数器
隔离数据区,线程私有。
Program Counter Register是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。
分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
每个线程都需要一个独立的Program Counter Register,这类每个线程独立存储的内存区域被称为"线程私有"内存。
三、堆栈划分Heap & Stack
Stack 栈(私有)
栈的优势是
- 存取速度比堆要快,仅次于直接位于CPU中的寄存器。
栈的缺点是
- 栈数据在多个线程或者多个栈之间是不可以共享的(但是在栈内部多个值相等的变量是可以指向一个地址的)。
- 存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
Heap 堆(共享)
堆的优势是
- 数据可共享
- 速度慢
- 可以动态地分配内存大小,生存期也不必事先告诉编译器
- Java的垃圾收集器会自动收走这些不再使用的数据。
堆的缺点是
- 由于要在运行时动态分配内存,存取速度较慢。
四、对象结构 Object Structure(JDK6实现)
Object存储在Heap中,hotSpot虚拟机中(jdk6或之前的图),其结构如下:

4.1 head 对象头
4.1.1 markWord
markWord,用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在32/64位的虚拟机(未开启压缩指针)中分别为32/64bit。
- 最后2bit是锁状态标志位
markdown存储内容
状态 | 标志位 | markWord存储内容 |
---|---|---|
未锁定 | 01 | 对象哈希码、对象分代年龄 |
轻量级锁定 | 00 | 指向锁记录的指针 |
膨胀(重量级锁定) | 10 | 执行重量级锁定的指针 |
GC标记 | 11 | 空(不需要记录信息) |
可偏向 | 01 | 偏向线程ID、偏向时间戳、对象分代年龄 |
4.1.2 klass 类型指针
klass,类型指针,即对象指向它的类元数据的指针,hotSpot通过这个指针来确定当前对象是哪个类的实例。
4.2 instance data 对象实例数据
instance data 存储的是对象的有效信息,各种非静态的变量(实例变量),包括父类继承的实例变量。
4.2 padding 对齐
由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,没有对齐使用padding补齐。
网友评论