JVM内存结构-总纲
[原创]个人理解,请批判接受,有误请指正。转载请注明出处: https://heyfl.gitee.io/JVM/JVM-Memory-Structure-Menu.html
学习JVM的可以去我的GitHub 上查看我的Xmind详细笔记 对整本《深入理解JVM》都有详尽的笔记,帮助理解
前言
网上有不少描述JVM内存结构的文章,但是要么比较老久了,要么描述有误,今天根据自己的理解整理下,有误请指正。
整体图解
程序计数器
记录Java程序运行到哪里
- 线程私有,可以看做当前线程执行到哪行【字节码】
- 字节码解析器工作就是通过改变这个【计数器】来选择下一行要执行什么,分支、循环、线程恢复都依赖于它
- 若为Java方法,则记录当前执行的字节码指令地址;
- 若执行的是native方法,则为空
Java 虚拟机栈
- 描述java【方法】执行的【内存模型】
- 每个方法对应一个栈帧,在线程运行到该方法时才创建
- 一条线程拥有的栈帧之和最大为-Xss(我们这里把它叫做线程栈)
- 当前所有线程栈之和=当前Java虚拟机栈已用大小
- Java虚拟机栈总空间最大值:JVM 可分配内存-堆最大值-其他空间最大值–>剩下的作为栈的总空间最大值
详细可参考:
- java虚拟机栈的内存结构
- 栈帧结构(待填坑)
- Java虚拟机栈异常及其处理方案(待填坑)
本地方法栈
同虚拟机栈,区别在于:虚拟机栈服务于Java方法(字节码),本地方法栈服务于Native方法
堆
- 随JVM启动而创建,是虚拟机最大的一块内存,被所有线程共享
- 存放着对象与数组等一切new出来的对象
- 垃圾收集器主要管理的区域
- 还存放着常量池
(1.7及以后的版本,都移到堆里存储了,需要注意的地方比较多 后面会说 不在这里描述)
详细可参考我另外的这篇博客(待填坑)
字符串常量池
存放字符串常量池,不同JDK版本存放的内容不一样
**1. 怎样的String会被存到常量池
简单来说以下这些情况都会存入常量池:
- 直接使用双引号声明出来的String对象
- 调用intern()方法 这个可以参考这篇《深入解析String#intern》
- 何时存放String字符串也可以参考
String放入运行时常量池的时机与String.intern()方法解惑
2. 存储结构在不同JDK下的区别
- JDK≤6
- 常量池存于方法区中
- 常量池里的内容全部为字符串具体的值
- JDK≥7
- 常量池存于堆中来自官网原文:the string pool was relocated to the heap
- 存储的东西为字符串值或引用(引用堆里的值),具体可以参考美团的《深入解析String#intern》
3. String 加载进字符串常量池的方式/时机
首先要知道字符串常量池是位于运行时常量池中的
- 编译完刚启动: 加载String进字符串常量池的过程大致为:
graph LR A[编译后的class文件中的class常量池] B[运行时常量池中的字符串常量池] A-->B;
- 运行期间
graph LR A[代码] B[运行时常量池中的字符串常量池] A-->B;
方法区(永久代)
存放类的结构:版本、常量、全局变量、静态变量、方法、接口、即JIT编译后的代码等信息(JDK8后完全移出方法区 可以参考:Java 8: From PermGen to Metaspace)
- 具体里面存了啥 可以参考 JVM虚拟机结构
(不在JVM里的)元数据区
JDK8开始,替代方法区的存在;很大程度的避免了因为类加载过多导致的OOM问题
- 实际上元数据区不属于JVM内存的一部分;其为本地内存的一部分;大小取决于开发人员配置或可用的本地内存大小
- 有一点必须注意的是:
- -XX:MetaspaceSize=128m 不是初始元空间大小,而是达到了128m后才会对该区域进行GC 初始化大小20.8m 默认MetaspaceSize也是20.8m