JVM最新发展
周志明老师在2020年1月更新并发布了经典书《深入理解Java虚拟机》第三版,此版距第二版出版已过去七八年。在新书中,老师加入了许多JVM自JDK8以后出现的新特性与新概念。JVM虚拟机内存管理方面,也有所更新。
在此我略微整理了下书中的概念,并做了思维导图,供以后学习
一、思维导图
1. JVM内存区域
2. 内存涉及概念
3. JVM处理Java对象
4. OutOfMemoryError
二、导图提纲
线程私有
以下三个区域为线程私有的内存区域
- 随线程的启动和结束而销毁
- 每个线程都会创建属于自己的线程私有区域 也就是说计数器、栈在JVM中可能不唯一
1. 程序计数器
- 一块较小的内存空间
- 类似于当前进程所执行的行号指示器
- 通过改变此计数器的值,程序实现了以下控制逻辑:
3.1. 分支
3.2. 循环
3.3. 跳转
3.4. 异常处理- 当单核CPU实现多线程任务、来回切换线程执行时,也需要利用此计数器来实现
- 唯一一个没有OutOfMemoryError的区域
2. Java虚拟机栈 (Java VM Stack)
- 本身就是一个数据结构的栈Stack,先入先出
- Java VM Stack描述的是Java方法在执行时的线程内存模型
- 当每个方法被执行时,会为之创建一个栈帧
- 栈帧
- 局部变量表
- 操作数栈
- 动态连接
- 方法出口
- 每个方法的slot (局部变量槽) 是完全确定的, 也就是说此部分需要申请多少内存完全确定
- 每个方法从被调用至执行完返回,对应着它的栈帧压栈入顶,最终出栈的过程
- 会出现StackOverflowError与OutOfMemoryError
(Hotspot不会有后者,当内存不够时,它会认为是深度不能满足了)- 虚拟机参数:-Xss
3. 本地方法栈
- 与Java虚拟机栈非常像,也是一个栈
- 描述的是Java调用本地Native服务(操作系统或其他程序提供的服务) 的线程内存模型
- 有些虚拟机(如Hotspot)会将Java虚拟机栈与本地方法栈合二为一,不严格区分
- 会出现OutOfMemoryError
线程共享
以下为线程共享的内存块:
- 在JVM启动,或者需要使用时分配
- 线程无关、所有线程共享内存
4. Java堆 (GC Heap)
1唯一目的:存放对象实例
2. 内存最大的区域,内存上可以不连续,但逻辑上应该被视为连续
3. 虚拟机垃圾回收最主要的战斗区域
4. 所有线程共享此区域,为提升对象分配效率以及垃圾 回收效率,可以划分出多个线程私有的分配缓冲区(TLAB)
5. 会出现OutOfMemoryError
6. 虚拟机参数:-Xmx -Xms
5. 方法区 (Non-Heap)
- 存储内容
- 类型信息
- 常量
- 静态变量
- JIT编译后的代码缓存
- 传说中的"永久代"
- JRocket、J9等虚拟机,都没有永久代
- Hotspot在JDK7之前有,8之后废弃
- JDK8之后的实现方式:采用本地内存(Native Memory), 元空间(MetaSpace) 不受JVM的内存限制;
- 可以不用垃圾回收,但主流虚拟机都会支持 主要是对常量池的回收以及对类型的卸载
- 会出现OutOfMemoryError
- 虚拟机参数:JDK8之后,-XX:MaxPermSize参数不再有效
-XX: MetaspaceSize、-XX: MaxMetaspaceSize成为有用控制
6. 运行时常量池
- 属于方法区的一部分
- Class文件中的**“常量池表”**,会被虚拟机加载映射到此区域储存
- 并非只有Class常量池表才可以进入运行时常量池;运行期间也可会将新的常量放到池中,例如String.intern()方法
- 会出现OutOfMemoryError
7. 直接内存
- 并不是在虚拟机JVM内存中
- 通过Native函数库,直接分配本地内存(本机直接内存)
3… Java内一般保有这个直接内存的引用,以便对其操作- 会受到本机物理内存等物理限制,当物理内存、swap分区等空间不够时, 也会抛出OutOfMemoryError
模块示例