JVM内存结构

- (线程)共享内存
- 堆
- 方法区(JDK1.7 永久代(位于虚拟机数据区) -> JDK1.8 元数据区(位于本地内存))
- (线程)私有内存
- JVM虚拟机栈
- 本地方法栈
- 程序计数器PC
程序计数器(PC)
-
定义: 当前线程正在执行的字节码指令的地址。若线程正在执行的是本地方法,则为
Undefined
-
作用:
- 字节码解释器通过改变程序计数器依次读取指令,实现代码的流程控制
- 记录当前线程执行位置,方便线程的上下文切换
-
特点:
- 内存空间占用小
- 不会出现内存溢出错误
- 线程私有,随线程的创建而创建、销毁而销毁
虚拟机栈(java栈)

-
定义:
- 记录java方法运行过程的内存模型
- 以栈帧为单位为每个方法创建一个内存区域:
- 局部变量表
- 操作数栈
- 动态链接
- 方法出口信息
- 由于Java 虚拟机栈是与线程对应的,数据不是线程共享的,因此不用关心数据一致性问题,也不会存在同步锁的问题。
-
压栈出栈过程
- 虚拟机栈栈顶的栈帧是正在执行的活动栈,表示当前正在执行的方法,PC寄存器会指向这个地址
- 当前方法内部创建局部变量时,会将局部变量的值存入栈帧的局部变量表中
- 当前方法调用新的方法时,会新建栈帧压入栈顶,同上述步骤
- 当前方法退出时,若存在返回值则会进入新栈帧的操作数栈中;若无返回值则无变化。
-
特点:
- 局部变量表的大小在编译时确定并分配内存,运行过程中不会变化
- 会出现两种异常:
- StackOverFlowError 请求栈的深度超过当前 Java 虚拟机栈的最大深度时
- OutOfMemoryError 线程请求栈时内存用完了
本地方法栈(C栈)
- 定义:
- 本地方法栈是描述本地方法运行过程的内存模型, 很多 Native 方法都是用 C 语言实现的。
堆
-
定义: 堆是用来存放对象的内存空间
-
特点:
- 线程共享,整个JVM只有一个堆
- 虚拟机创建时启动,是垃圾回收的主要场所
- 可分为:
- 新生代(Eden)——From Survior、To Survivor
- 老年代
- Java 堆所使用的内存不需要保证是连续的。而由于堆是被所有线程共享的,所以对它的访问需要注意同步问题,方法和对应的属性都需要保证一致性。
方法区
-
定义: Java 虚拟机规范中定义方法区是堆的一个逻辑部分。方法区存放以下信息
- 已加载类信息
- 常量
- 静态变量
- 即时编译器编译后的代码
-
特点:
- 线程共享(同堆一样)
- 存储一些长期存在不会发生频繁变动的信息,也被称为老年代/元数据区
- 该区域垃圾回收效率低,可以通过设置不允许垃圾回收。主要的回收目标是: 常量池、类的卸载
-
运行时常量池:
- 常量
- 静态变量
- 加载的类信息
- 即时编译代码
直接内存(堆外内存)
-
定义: 独立于java虚拟机数据区外的内存空间
-
操作直接内存:
在 NIO 中引入了一种基于通道和缓冲的 IO 方式。它可以通过调用本地方法直接分配 Java 虚拟机之外的内存,然后通过一个存储在堆中的DirectByteBuffer对象直接操作该内存,而无须先将外部内存中的数据复制到堆中再进行操作,从而提高了数据操作的效率 -
与堆内存比较:
- 直接内存申请空间耗费更高的性能
- 直接内存读取 IO 的性能要优于普通的堆内存。
- 直接内存作用链: 本地 IO -> 直接内存 -> 本地 IO
- 堆内存作用链:本地 IO -> 直接内存 -> 非直接内存 -> 直接内存 -> 本地 IO