jvm内存区域

1、内存总揽

image

2、内存区域介绍

2.1、程序计数器

概念: 程序计数器可看成当前程序执行的字节码的行号指示器,其作用不仅指示着程序下一条需要执行的字节码指令,而且 用于保证线程切换后能够正确恢复到下一条需要执行的指令位置;

特性: 哥线程的程序计数器相互独立,互不影响,属于线程私有; 并且其是java中唯一一个没有规范有OutOfMemoryError情况的区域;

2.2、java虚拟机栈

慨念: 虚拟机栈描述的是java方法执行的内存模型,每个方法在执行的同时会创建一个栈帧用于存储局部变量表,操作数栈,动态链接 方法出口等信息,每个方法的从调用直到执行完成的过程就对应着一个栈帧在虚拟机栈的入栈到出栈的过程。

特性: 虚拟机栈是线程私有的,他的生命周期与线程相同。

存储信息:

异常类型:

2.3、本地方法栈

本地方法栈和虚拟机栈很相似,虚拟机栈是为虚拟机执行java方法服务的,而本地方法栈是虚拟机执行本地方法服务的,其同样会抛出StackOverflowError, OutOfMemoryError异常;

2.4、java堆

概念: 虚拟机启动时创建,用于存放对象实例及数组,其主要是垃圾收集器的主要区域,因此也称为”GC堆”
存储: 对象实例以及数组
特性: 在堆中没有内存完成实例分配时,抛出OutOfMemoryError异常,物理内存不需要连续,逻辑上连续即可;

2.5、方法区

概念: 各个线程共享的内存区域;
存储: 已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据;
特性: 方法区里的垃圾回收的目标主要针对常量池的回收和对类型的卸载;其中类型的卸载条件非常的苛刻,当方法区无法满足内存分配需求时,抛出抛出OutOfMemoryError异常

2.6、运行时常量池

概念: 方法区的一部分
存储: 存放编译期生成的各种字面量(文本,final变量),符号引用(类,方法,变量的名称和描述)
特性: 具备动态性,常量并非编译期产生,运行期间也可产生,例如String.intern方法,当常量池无法申请到内存时会抛出OutOfMemoryError异常

2.7、直接内存

概念: 不是虚拟机运行时的数据区域,属于虚拟机外的内存;
特性: jdk1.4增加了NIO类,引入了一种基于通道和缓存区的I/O方式,它可以通过Native函数分配堆外内存,然后通过一种存储在堆中的DirectByteBuffer 对象来作为这块内存的引用来操作该内存,这样在某些场合提高了性能,避免了来回复制数据。内存无法扩展时,也会抛出OutOfMemoryError异常

3、对象的创建过程

image

3.1、类加载

  虚拟机当遇到new指令时,首先会到方法区的常量池中检查该类的符号引用是否存在,以及该符号引用所代表的类是否已被加载,解析和初始化过,若还没有, 则先加载该类。

3.2、内存分配

  内存分配即是在堆内存中划分出一块用于创建类的实例。当前内存分配有两种方式:

3.4、内存空间初始化

  使用TLAB时,该步骤可以放到TLAB之前将内存空间初始化为0,保证关键字段在不赋值的情况下也可被使用;

3.5、初始化

  分配空间后所有的字段值为0,需要调用init方法初始化字段值,达到程序员所要求的,这时对象的创建才算结束;

4、内存中的对象

4.1、对象的内存布局

  对象在内存中的布局分为3块:对象头,实例数据,对其填充;  * 对象头(header):包含两部分,一部分用于存储运行时的数据如哈希码,GC分代年龄,锁状态标志,偏向线程ID等;这部分数据在32或64位的虚拟机中分别用 32位和64位来存储,该部分被称为”Mark Word”;另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是那个类的实例,注意 并不是所有的对象都有类类型指针,例如数组就没有;

4.2、对象的访问定位

  主流的访问方式有两种:句柄方式和直接指针方式;

5、虚拟机相关内存参数设定