以上笔记基于JavaGuide整理
1、运行时数据区域
JDK 1.8:
线程私有的:程序计数器,虚拟机栈,本地方法栈
线程共享的:堆,方法区(元空间),直接内存 (非运行时数据区的一部分)
1.1、程序计数器(线程私有)
程序计数器有两个作用:
- 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。
- 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。
程序计数器是唯一一个不会出现
OutOfMemoryError
的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。
1.2、虚拟机栈(线程私有)
虚拟机栈的生命周期和线程相同,随着线程的创建而创建,随着线程的死亡而死亡。
除了一些 Native 方法调用是通过本地方法栈实现的,其他所有的 Java 方法调用都是通过栈来实现的。
每一次方法调用都会有一个对应的栈帧被压入栈中,每一个方法调用结束后,都会有一个栈帧被弹出。
每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法返回地址。
其中,局部变量表主要存放了编译期可知的各种基本数据类型和对象引用;操作数栈存放方法执行过程中产生的中间计算结果和临时变量,动态链接将常量池中指向方法的符号引用转化在内存地址中的直接引用。
栈帧随着方法调用而创建,随着方法结束而销毁。无论方法正常完成还是异常完成都算作方法结束。
1.3、本地方法栈(线程私有)
和虚拟机栈所发挥的作用非常相似,区别是:虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。 在 HotSpot 虚拟机中本地方法栈和 Java 虚拟机栈合二为一。
1.4、堆(线程共享)
Java 虚拟机所管理的内存中最大的一块,Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创建。堆内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。
Java 堆是垃圾收集器管理的主要区域,因此也被称作 GC 堆(Garbage Collected Heap)。从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,Java 堆还可以细分为:新生代和老年代;JDK 8 版本之后 PermGen(永久代) 已被 Metaspace(元空间) 取代,元空间使用的是本地内存。
字符串常量池在堆中,它是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。
1.5、方法区(元空间)(线程共享)
方法区存储已被虚拟机加载的类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
方法区中还包含运行时常量池,用于存放编译期生成的各种字面量(Literal)和符号引用(Symbolic Reference)的 常量池表(Constant Pool Table) 。
1.6、直接内存(线程共享)
直接内存是在本地内存上分配的,不是运行时的数据区域。
JDK1.4 中新加入的 NIO(Non-Blocking I/O,也被称为 New I/O),引入了一种基于通道(Channel)与缓存区(Buffer)的 I/O 方式,它可以直接使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样就能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆之间来回复制数据。
2、HotSpot 虚拟机对象分配、布局和访问
2.1、对象创建流程
- 类加载检查:检查有没有这个类,这个类是否加载
- 分配内存:为新生对象分配内存,分配方式有 “指针碰撞” 和 “空闲列表” 两种,选择哪种分配方式由 Java 堆是否规整决定
- 初始化0值:将分配到的内存空间都初始化为零值(默认值)
- 设置对象头:设置这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等信息
- 执行init方法:把对象按照程序员的意愿进行初始化
2.2、对象的内存布局
对象在内存中的布局可以分为 3 块区域:对象头、实例数据和对齐填充。
- 对象头包括两部分信息,第一部分用于存储对象自身的运行时数据(哈希码、GC 分代年龄、锁状态标志等等),另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
- 实例数据部分是对象真正存储的有效信息,也是在程序中所定义的各种类型的字段内容。
- 对齐填充部分不是必然存在的,仅仅起占位作用。 因为 Hotspot 虚拟机的自动内存管理系统要求对象起始地址必须是 8 字节的整数倍。而对象头部分正好是 8 字节的倍数(1 倍或 2 倍),因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。
2.3、对象的访问定位
Java 程序通过栈上的 reference 数据来操作堆上的具体对象。对象的访问方式由虚拟机实现而定,目前主流的访问方式有:使用句柄、直接指针。文章来源:https://www.toymoban.com/news/detail-475143.html
- 句柄:reference ->句柄->对象
- 直接指针:reference ->对象
两种对象访问方式各有优势。使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。使用直接指针访问方式最大的好处就是速度快,它节省了一次指针定位的时间开销。文章来源地址https://www.toymoban.com/news/detail-475143.html
到了这里,关于JVM | Java内存区域的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!