运行时数据区域
程序计数器 Program Counter Register
可以看作是当前线程的字节码的行号指示器,为什么说是当前线程呢?因为java的多线程是通过线程轮流切换来实现的,为了切换后能恢复到正确的执行位置,每个线程都要有一个独立的程序计数器,之间互不影响。这块内存区域被称为“线程私有”的内存。
java虚拟机栈VM Stack
- 我们常说的栈内存就是指虚拟机栈
- 虚拟机栈也是线程私有的
- 生命周期与线程相同
- 描述了java方法执行的内存模型,每个方法执行时,都会创建一个栈帧(stack frame)用于存储局部变量表、操作数栈、动态链接,方法出口等信息,每个方法的调用到完成,对应着栈帧在虚拟机栈中的入栈到出栈的过程
- 局部变量表用于存放基本数据类型(boolean,byte,char,short,int,float,long,double),对象引用,和returnAddress
- 该区域会出现两种异常:1.如果线程请求的栈的深度大于虚拟机栈所允许的深度,会抛出StackOverflowError异常。2.若虚拟机栈可以动态扩展(大部分虚拟机都允许),如果扩展时无法申请到足够的内存,则会抛出OutOfMemoryError异常
本地方法栈 Native Method Stack
- 线程私有
- 与虚拟机栈类似,唯一区别就是虚拟机栈位虚拟机执行java方法服务,本地方法栈则虚拟机使用的native方法服务
- 也会抛出StackOverflowError异常与OutOfMemoryError异常
java堆 java heap
- 是一块被所有线程共享的内存区域,在虚拟机启动的时候创建。
- 唯一目的就是存放对象(及数组)实例,几乎所有对象实例都在这里分配内存
- 在堆中没有内存完成实例分配,并且堆无法再拓展的时候,就会抛出OutOfMemoryError异常
方法区
- 无法满足内存分配时就会抛出OutOfMemoryError异常
- 用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等
运行时常量池
- 是方法区的一部分
- 常量池:用于存放编译期生成的各种字面量(1,2,“abc”称为字面量)和符号引用,这部分内容在类加载后进入方法区的运行时常量池存放
- 无法满足内存分配时就会抛出OutOfMemoryError异常
直接内存
- 并不是虚拟机运行时数据区的一部分,是引入NIO类后出现的一块内存。
虚拟机对象
对象内存分配
- 指针碰撞:要求java堆是规整的!Serial,ParNew
- 空闲列表:虚拟机维护一个列表,记录哪些内存块是可用的,分配的时候找到一个空间给对象,并更新列表记录。CMS
对象的内存布局
对象在内存中存储的布局可以分为三块:对象头,实例数据,对齐填充
* 对象头:对象自身的运行时数据和类型指针(对象指向他的类元数据的指针)
* 实例数据:对象存储的有效信息
* 对齐填充:对象的大小必须是8字节的整数倍,因此不够得需要补齐
对象的访问定位
java程序需要通过栈中的reference来操作java堆上的具体对象
主流两种方式来访问:使用句柄和直接指针
直接指针节省了一次指针定位的时间