JVM 内存布局详解
程序计数器(PC)
这个是当前线程正在执行的字节码行号指示器, 类似于实际的PC, 根据这里面的内存数据来确定程序接下来执行的指令. 在JAVA中, 每个线程都有一个, 相互隔离, 线程之间的切换就是基于程序计数器. 如果执行的是方法, 这里记录的是虚拟机字节码指令的地址. 注意:当执行的是Native方法的时候为空(Undefined).
因为只存储一个指令, 所以它不会出现任何 OutOfMemoryError.
Java虚拟机栈
每个线程私有, 里面装的多个栈帧, 每个栈帧对于的一个方法. 里面存储的是Java方法的内存模型. 相当于描述的是一个方法需要的内容. 逻辑上类似于操作系统中的进程栈, 每个线程都有一个虚拟机栈, 每个栈中都有多个栈帧. 每个方法的执行过程都是栈帧的进栈于出栈, 类似于进程中的函数调用.
每个栈帧存就是对方法的描述, 栈帧中存储局部变量, 局部变量是一个方法内使用的变量, 包括各种数据类型的临时变量. (boolean、byte、char、short、int、float、long、double 类型), 以及对象的引用. 对象本身并不会存储在线程栈中, 即使这个对象是在方法中新建的, 线程栈中也仅会保存对象的引用, 作为局部变量, 而对象本身则存储在JVM的堆空间中.
异常情况:线程请求的栈深度大于虚拟机允许的深度, 将抛出StackOverflowError异常. 如果虚拟机栈可以动态扩展, 当扩展的时候没有申请到内存的时候抛出OutOfMemoryError.
本地方法栈
每个线程都有自己的本地方法栈, 这时线程私有的, 用于线程支持对本地方法的调用, 本地方法栈(Native Method Stack)是为调用本地方法(Native Method)服务的, 一个典型的例子是使用 JNI(Java Native Interface)调用用 C/C++ 编写的本地代码. 本地方法通常用于调用操作系统的 API, 使用已有的 C++ 库, 与硬件交互等, 本地方法栈也就是本地方法代码执行的地方.
Java堆
Java虚拟机管理最大的一块,线程共享,存放对象实例和数组.分新生代(1/3)和老年代(2/3),新生代还可以分Eden(8/10)、From Survivor(1/10) 、To Survivor(1/10),是主要根据垃圾清理来分的.
异常情况: 无法再对对象实例分配, 并且堆也无法扩展时, 将抛出OutOfMemoryError.
方法区
线程共享, 主要存储被虚拟机加载的类信息, 常量, 静态变量, 即时编译器编译后的代码. 运行时常量池也是方法区的一部分, 比如String有一个常量池, 他就是放到这个里面的. 类似于进程的代码区以及全局变量区.
异常情况: 当方法区无法满足内存分配时, 将抛出OutOfMemoryError异常.
直接内存
NIO通过使用Native函数库直接分配对外内存, 因为JVM本质上是一个进程, 这部分内存实际上是 JVM 进程的堆内存, java 程序可以在内部申请这块内存使用, 这种使用方式相当于绕过了 JVM 的内存管理与自动回收机制, 内存管理需要开发者手动管理. Java 支持两种方法使用这块内存, 分别是 JVM 自身提供的接口, 这块内存直接被 Java 代码使用. 另一种是 Native 代码申请内存, 这部分内存用于 JNA 调用本地代码的时候使用. 这两种使用方式的相同点是, 两者均使用的是 JVM 这个进程的用户的虚拟空间的堆的内存空间, 不同点是:
特性 | 堆外内存(Off-Heap Memory) | Native 代码申请的内存 |
---|---|---|
分配方式 | 使用 JVM 提供的接口(如 Unsafe.allocateMemory 或 DirectByteBuffer ). |
通过本地代码(C/C++)直接调用操作系统 API(如 malloc ). |
管理接口 | Java 层提供操作接口,Unsafe 或 DirectByteBuffer 包装了分配的内存. |
完全依赖 C/C++ 的内存管理工具(如指针操作). |
语言依赖 | 完全在 Java 中操作,使用 Java 提供的工具类或方法来访问和管理内存. | 必须通过 JNI 或其他桥接机制从 Java 调用 C/C++ 函数. |
释放机制 | DirectByteBuffer 可依赖 JVM 的 Cleaner 机制自动释放(但不及时). |
由 C/C++ 开发者显式调用 free 或 delete 来释放内存. |
线程安全性 | Java 层可能提供一些线程安全特性(如 ByteBuffer 的读写同步). |
完全依赖 C/C++ 代码的实现. |
调试和诊断工具支持 | JVM 提供了一些工具(如 jmap )可以查看堆外内存的使用情况. |
必须依赖 C/C++ 的调试工具(如 valgrind ). |
异常情况: 不受Java堆大小限制, 但是受机器的物理内存限制, 当各个内存区域大于机器物理内存的时候, 会出现OutOfMemoryError.
标签:Java,本地,JNA,详解,线程,内存,JVM,方法 From: https://www.cnblogs.com/wevolf/p/18635212