1.运行时数据区域
(1). 程序计数器 (线程私有)
一块较小的内存空间, 它可以看作是当前线程所执行的字节码的行号指示器。如果线程正在执行的是一个Java方法, 这个计数器记录的是正在执行的虚拟机字节码指令的地址; 如果正在执行的是本地(Native) 方法, 这个计数器值则应为空(Undefined) 。
(2). Java虚拟机栈 (线程私有)
虚拟机栈描述的是Java方法执行的线程内存模型: 每个方法被执行的时候, Java虚拟机都会同步创建一个栈帧(Stack Frame) 用于存储局部变量表、 操作数栈、 动态连接、 方法出口等信息。 每一个方法被调用直至执行完毕的过程, 就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
(3). 本地方法栈 (线程私有)
本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码) 服务, 而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。
(4). Java堆 (线程共享)
Java世界里“几乎”所有的对象实例都在这里分配内存。它是垃圾回收机制的重点关照对象,因此也被称为“GC堆”。
(5). 方法区 (线程共享)
用于存储已被虚拟机加载的类型信息、 常量、 静态变量、 即时编译器编译后的代码缓存等数据。 在JDK 6的时候HotSpot开发团队就有放弃永久代, 逐步改为采用本地内存(Native Memory) 来实现方法区的计划了,到了JDK 7的HotSpot,已经把原本放在永久代的字符串常量池、静态变量等移出, 而到了JDK 8, 终于完全废弃了永久代的概念, 改用与JRockit、 J9一样在本地内存中实现的元空间(Metaspace) 来代替, 把JDK 7中永久代还剩余的内容(主要是类型信息) 全部移到元空间中。
(6). 运行时常量池
运行时常量池(Runtime Constant Pool) 是堆的一部分。 Class文件中除了有类的版本、 字段、 方法、 接口等描述信息外, 还有一项信息是常量池表(Constant Pool Table) , 用于存放编译期生成的各种字面量与符号引用, 这部分内容将在类加载后存放到方法区的运行时常量池中。 Java语言并不要求常量一定只有编译时产生,也就是说运行期间也可以将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法。
JDK版本 | 方法区的实现 | 运行时常量池所在的位置 |
---|---|---|
JDK6 | PermGen space(永久代) | PermGen space(永久代) |
JDK7 | PermGen space(永久代) | Heap(堆) |
JDK8 | Metaspace(元空间) | Heap(堆) |
(7). 直接内存
在JDK 1.4中新加入了NIO(New Input/Output) 类, 引入了一种基于通道(Channel) 与缓冲区 (Buffer) 的I/O方式, 它可以使用Native函数库直接分配堆外内存, 然后通过一个存储在Java堆里面的 DirectByteBuffer对象作为这块内存的引用进行操作。 这样能在一些场景中显著提高性能, 因为避免了 在Java堆和Native堆中来回复制数据。
2.HotSpot虚拟机对象探秘
(1).简述对象创建过程:
当Java虚拟机遇到一条字节码new指令时, 首先将去检查这个指令的参数是否能在常量池中定位到
一个类的符号引用, 并且检查这个符号引用代表的类是否已被加载、 解析和初始化过。 如果没有, 那
必须先执行相应的类加载过程, 类加载检查通过后虚拟机将为新生对象分配内存。
分配内存 - 指针碰撞
指针碰撞要求垃圾收集器具有空间压缩整理(Compact)的能力,例如 Mark - Compact垃圾收集器
假设Java堆中内存是绝对规整的, 所有被使用过的内存都被放在一边, 空闲的内存被放在另一边, 中间放着一个指针作为分界点的指示器, 那所分配内存就仅仅是把那个指针向空闲空间方向挪动一
段与对象大小相等的距离, 这种分配方式称为“指针碰撞”(Bump ThePointer) 。
分配内存 - 空闲列表
内存堆不规整的时候需要虚拟机维护一个列表,记录那一块是可用的,那一块是不可用的,分配内存时
对列表进行状态更改即可,实现较为复杂
虚拟机对对象进行必要的设置,如这个对象是哪个类的实例、 如何才能找到类的元数据信息、 对象的哈希码(实际上对象的哈希码会延后到真正调用Object::hashCode()方法时才计算) 、 对象的GC分代年龄等信息,是否启用偏向锁等。
上面的工作都完成后才开始执行构造函数,用户定义的程序构造才开始执行,执行完成后一个真正可用的对象才算完全被构造出来。
(2).对象的内存布局
在HotSpot虚拟机里, 对象在堆内存中的存储布局可以划分为三个部分: 对象头(Header) 、 实例数据(Instance Data) 和对齐填充(Padding)。
想深入了解的可查看以下两位博主的文章:
解析1:https://www.cnblogs.com/zhengbin/p/6490953.html
解析2:https://blog.youkuaiyun.com/qq_33591903/article/details/106077584