JVM内存区域

2021-06-13  本文已影响0人  Lnstark

Java的运行时数据区域包括线程私有的程序计数器,Java虚拟机栈和本地方法栈,线程共享的Java堆和方法区。


JVM内存区域.png

1.程序计数器

它可以看做是当前线程所执行的字节码的行号指示器。在多线程中,线程切换后需要恢复到正确的执行位置,因此每条线程都有一个独立的程序计数器。如果线程在执行一个Java方法,那么它记录的是正在执行的虚拟机字节码指令地址;如果执行的是本地方法,则计数器值是空。该区域也是唯一不会出现OutOfMemoryError的区域。

2.Java虚拟机栈

它的生命周期和线程相同,每个方法执行的时候,JVM会同步创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。方法的调用和执行完成对应着栈帧的入栈和出栈。局部变量表存放了基本数据类型(byte、char...)和对象引用。栈深过深如递归可能会造成StackOverflowError,如果创建了太多线程的话可能会造成OutOfMemorryError。

3.本地方法栈

本地方法栈的作用类似虚拟机栈,只是它是为本地方法服务的。同样会有StackOverflowError和OOM。

4.Java堆

堆是线程共享的内存区域,目的是用来存放 Java对象实例,也是垃圾回收的主要区域。主流的JVM的实现中它是可扩展的(通过-Xmx和-XMS设定)。当内存不足分配实例也无法扩展时,会抛出OOM。

5.方法区

6.直接内存

也叫堆外内存,在NIO中通过native方法直接分配,用DirectByteBuffer作为这块内存的引用,并可以直接进行操作,在有些场景下可以避免了Java堆和Native堆之间的数据拷贝,提高性能。该区域受机器的物理总内存限制,所以也会OOM。

7. 对象内存布局

Java对象由三部分组成:

7.1 对象头

对象头包括Mark Word和Klass Pointer

Mark Word用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等。

Mark Word在32位jvm里的存储内容(占4byte):


32位虚拟机.png

在64位jvm里的存储内容(占8byte):


64位虚拟机.png

Klass Pointer即类型指针,是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

7.2 实例数据

如果对象有属性字段,则这里会有数据信息。如果对象无属性字段,则这里就不会有数据。根据字段类型的不同占不同的字节,例如boolean类型占1个字节,int类型占4个字节等等;

7.3 对齐数据

所有的对象分配的字节总SIZE需要是8的倍数,如果前面的对象头和实例数据占用的总SIZE不满足要求,则通过对齐数据来填满。
为什么要对齐数据?字段内存对齐的其中一个原因,是让字段只出现在同一CPU的缓存行中。如果字段不是对齐的,那么就有可能出现跨缓存行的字段。也就是说,该字段的读取可能需要替换两个缓存行,而该字段的存储也会同时污染两个缓存行。这两种情况对程序的执行效率而言都是不利的。其实对其填充的最终目的是为了计算机高效寻址。

参考资料
《深入理解Java虚拟机(第3版)》
博客园-Java对象的内存布局

上一篇 下一篇

猜你喜欢

热点阅读