文章目录
1. 程序计数器
线程私有,每个线程一个
用于存储当前线程执行到哪儿了。
如果线程正在执行一个 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是 Native 方法,这个计数器的值则为 (Undefined)。此内存区域是唯一一个在 Java 虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域。
2. 虚拟机栈
线程私有,每个线程一个,生命周期和线程一致
描述的是 Java 方法执行的内存模型:每个方法在执行时都会床创建一个栈帧(StackFrame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行结束,就对应着一个栈帧从虚拟机栈中入栈到出栈的过程。
2.1. 栈帧(StackFrame)
2.1.1. 局部变量表
用于存储编译期可知的各种基本类型(boolean、byte、char、short、int、float、long、double),对象引用(reference 类型)和 returnAddress 类型(指向了一条字节码指令的地址)
在局部变量表中存储数据的单位是 局部变量空间 Slot , long和double会占用2个Slot,其他类型仅占用1个
2.1.2. 操作数栈
操作数栈也常称为操作栈,它是一个先进后出的栈。他的元素是任意的java数据类型,然后对其进行数据操作。
操作数栈被用来进行计算和方法调用时传递参数。
如 加法,运行加法指令的时候会将栈里的前两个数据出栈,然后计算,然后把计算结果入栈。
2.1.3. 动态连接方法
其实就是一个指向该栈帧
所属方法在 运行时常量池
中的引用。不然你怎么能知道当前栈帧
是哪个方法的具体执行呢。
2.1.4. 返回地址
方法返回的情况有两种:正常return (正常完成出口)和遇到异常 异常退出(异常完成出口)
返回值为void方法 最后一句默认为
return
但是可以省略
不管怎么退出当前方法,都会继续执行调用该方法的语句
后面的代码,所以方法返回可能需要在 栈帧
中保存一些信息,用来恢复上层方法的执行状态。
3. 本地栈
线程私有,每个线程一个
本地方法栈则为虚拟机使用到的 Native 方法服务
4. 方法区(永久代) PermGen
线程共享,属于共享内存区域
存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
4.1. Class信息
- 魔数
- 版本号
- 类
- 父类
- 接口数组
- 字段
- 方法
4.2. 运行时常量池
运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量和符号引用,但是字符串常量已经在1.7中被移动到字符串常量池中
5. 元数据空间 Metaspace(元空间)
在jdk1.7的HotSpot中,已经把原本放在方法区中的静态变量、字符串常量池等移到堆内存中
在JDK1.8中,HotSpot已经没有“PermGen space”这个区间了,取而代之是Metaspace(元空间)
元空间并没有处于堆内存上,而是直接占用的本地内存(NativeMemory)。
6. 堆
线程共享
主要是存放对象实例和数组
内部会划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB)。可以位于物理上不连续的空间,但是逻辑上要连续。
6.1. 字符串常量池
在开发过程中,字符串类型的数据使用特别频繁。最早在方法区,在1.7版本移入到了堆中,在1.8中永久代变更为元数据空间(MetaSpace),但字符串常量池没有发生变化。
6.2. 新生代
新生代由Eden Space和两块相同大小的Survivor Space(通常又称为S0和S1或From和To)构成
- Eden Space
- From Survivor Space
- To Survivor Space
Eden是对象的出生地,所有新创建的对象都会存到Eden Space中。
当Eden Space
内存用完之后,创建对象会发现内存不足触发Minor GC
进行回收,回收完成之后,还存活在Eden Space
中的对象会被移动 From Survivor Space
中
在 Minor GC
触发时还会扫描 From Survivor Space
区中的对象,如果对象不再使用了跳过,如果对象在使用,使他们的年龄+1,如果年龄达到条件,这个对象会被复制到老年代中,如果年龄达不到条件会被复制到 To Survivor Space
区。Minor GC
完成后,清空 From Survivor Space
,To Survivor Space
变成新的 From Survivor Space
,,From Survivor Space
变成新的 To Survivor Space
。 “To”区被填满之后,会将所有对象移动到年老代。
这么做的好处,如果在一个内存块中执行回收对象的操作,执行完成之后,可使用内存会分布在整个内存块中的各个位置,很难管理,且会产生内存碎片。而且对内存的使用必须使用映射表,无法顺序使用。
总结Minor GC
的工作有以下几点:
- 将不可回收对象从
Eden Space
移动到From Survivor Space
- 清空
Eden Space
- 将老年对象从
From Survivor Space
移动到老年代
- 将年轻对象从
From Survivor Space
移动到To Survivor Space
- 清空
From Survivor Space
6.3. 老年代
7. 直接内存(Direct Memory)
这块内存主要是提供给DirectByteBuffer使用,通过调用Navive函数直接在虚拟机使用的内存外,划分一部分虚拟机外内存供DirectByteBuffer使用。
虽然这块内存不受到Java虚拟机内存的限制,但是还是会受到主机本身的内存限制,也会出现OutOfMemoryError