Java虚拟机在执行程序过程中会把管理的内存划分为不同的数据区域,包括方法区、堆、虚拟机栈、本地方法栈、程序计数器这5个部分。

1、程序计数器

它是一块较小的内存空间,指示当前线程执行的字节码行号,字节码解释器工作时就是通过改变这个计数器值来选取下一条需要执行的字节码指令,从而完成分支、循环、跳转、异常处理、线程恢复等逻辑功能。

每个线程都需要一个独立的程序计数器,因为Java虚拟机的多线程是通过线程轮流切换分配处理器执行时间来实现的,也就是在任何一个确定的时间,处理器都只能执行一个线程的中的指令,所以就需要线程能独立的存储各自当前的执行位置,也就是独立的程序计数器。

如果执行的是java方法,计数器记录的是当前执行的字节码指令地址,如果是native方法,这个值为空。

程序计数器内存区域是java虚拟机规范中唯一没有规定OutOfMemoryError情况的区域。

2、Java虚拟机栈

虚拟机栈也就是通常所说的栈内存,一般理解栈内存是存放方法局部变量表的地方,但其实不止这些。

每个方法执行时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。一个方法从调用到执行完成,就对应了一个栈帧在虚拟机栈中入栈到出栈的过程。

局部变量表所需的内存空间在编译期间完成分配,也就是当进入一个方法时它需要的在栈帧中分配多大的局部变量空间是完全确定的,运行期间不会改变变量表的大小。

3、本地方法栈

本地方法栈与虚拟机栈对应,为native方法服务。

以上三个区域就是线程私有的,下面两个被所有线程共享内存区域。

4、Java堆

堆内存唯一目的就是存放创建的对象实例,所有对象实例和数组都要在这里分配内存,因此也是垃圾回收的主要区域。根据虚拟机规范,java堆可以在物理空间上不连续,只要逻辑上连续即可,当堆内存无法继续分配时会抛出OutOfMemery异常。

5、方法区

用于存储被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码数据等。Java虚拟机规范把方法区描述为堆的一个逻辑部分。

运行时常量池是方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项就是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。


对象的访问定位

一般当一个对象创建完成后,我们会有一个栈上的引用类型(reference)和堆上的具体对象,由于reference类型在Java虚拟机规范中只规定了一个指向对象的引用,具体这个引用通过何种方式定位、访问堆中的对象并没有定义,因此具体策略取决于虚拟机的实现。主要有两种访问方式:句柄和直接指针。

句柄方式

这种方式会在Java堆中划分一块内存来作为句柄池,引用中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的地址信息。

直接指针方式

直接指针就是引用类型直接存储对象地址,对象实例中再存放对象类型数据的地址。

两种方式各有优势、句柄相当于增加了一个隔离层,当对象被移动时(垃圾回收)只需要改变句柄中的数据指针,引用本身不需要修改,直接访问就不行,但是由于直接访问是引用直接指向对象实例,访问时比句柄少了一次寻址,因此访问速度也就更快。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐