目录
一.运行时数据区域
1.线程独享
2.线程共享
二.内存如何分配
1.指针碰撞法
2.空闲列表法
3.TLAB
三.对象在内存中的组成
编辑1.对象头
2.实例数据
3.对齐填充
一.运行时数据区域
1.线程独享
(1)栈
虚拟机栈:每个 Java 方法在执行的同时,会创建一个栈帧,用于存储局部变量表、操作数栈、常量池引用等信息;方法的调用过程,就是一个栈帧在 Java 虚拟机栈中入栈和出栈的过程;
本地方法栈:和虚拟机栈很类似,区别在于虚拟机栈为 Java 方法服务,本地方法栈为 Native 方法服务;其中 Native 方法可以看做用其它语言(C、C++ 或汇编语言等)编写的方法;
(2)程序计数器
一个 CPU 在某个时间点,只能做一件事情,在多线程的情况下,CPU 运行时间被划分成若干个时间片,分配给各个线程执行;
程序计数器的作用就是记录当前线程执行的位置,当线程被切换回来的时候,能够找到该线程上次运行到哪儿了;所以程序计数器一定是线程隔离的。
2.线程共享
(1)方法区
方法区用于存放已被加载的类信息、常量、静态变量、即编译器编译后的代码等。
还有要注意的一点:方法区是 JVM 的规范,在 JDK 1.8 之前,方法区的实现是永久代;从 JDK 1.8 开始 JVM 移除了永久代,使用本地内存来存储元数据并称之为:元空间(Metaspace)。
(2)堆
对于堆栈的区别总结一句话:堆中存对象,栈中存基本数据类型和堆中对象的引用;一个对象的大小是可以动态变化的,而引用是固定大小的。
这么看就容易理解堆为什么是线程公有的了,省地儿啊
二.内存如何分配
1.指针碰撞法
适用于堆内存完整的情况,已分配的内存和空闲内存分表在不同的一侧,通过一个指针指向分界点,当需要分配内存时,把指针往空闲的一端移动与对象大小相等的距离即可,用于Serial和ParNew等不会产生内存碎片的垃圾收集器。
2.空闲列表法
适用于堆内存不完整的情况,已分配的内存和空闲内存相互交错,JVM通过维护一张内存列表记录可用的内存块信息,当分配内存时,从列表中找到一个足够大的内存块分配给对象实例,并更新列表上的记录,最常见的使用此方案的垃圾收集器就是CMS。
3.TLAB
三.对象在内存中的组成
1.对象头
(1)markword
记录了该对象锁相关的信息、分代年龄、hashCode,在32位JVM中占32bit,在64位JVM中占64bit
(2)指向类型的指针
指向方法区对应class信息的指针,在32位JVM中占32bit,在64位JVM中占64bit(开启指针压缩的情况下占32bit)
(3)如果是数组-》数组长度
如果是数组的话,组成中会包含数组长度,32bit
2.实例数据
类的实例信息
3.对齐填充
JVM要求Java对象的大小应该是8bit的倍数,这部分就是将对象大小补充为8bit的倍数