目录
- 图片需要可以去我的github自取。JVM学习要点
- 本篇内容是阅读《深入理解Java虚拟机》提取码dg9o的内容,如果想了解更多可以自行阅读。
- 为什么你需要整理呢?为了方便进行自我知识架构。
- 如果觉得内容整理的不错,感谢移步到MyGithub帮忙star,follow,issues三连哦
- 标红内容是面试常问,需要重点学习掌握。
Java JVM虚拟机学习
1 JVM
JVM是Java Virtual Machine的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
2 内存管理机制
2.1 Java内存区域
-
2.1.1 方法区
方法区是线程共享内存的一块区域。
方法区用于存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后代码的数据。
在HotSpot JDK8之前为永久代
JDK8之后为元空间,存储空间移到了本机内存当中。 -
2.1.2 堆
Java虚拟机管理的内存最大的一块,被所有线程共享的一块内存区域。几乎所有的对象实例以及数组都应当在堆上分配。(栈上分配,标量替换)。
Java堆在HotSpot虚拟机中被划分为老年代:新生代(2:1)。新生代又被分为Eden空间,From Survivor,To Survivor(8:1:1)-
老年代
-
新生代
- Eden
- From Survivor
- To Survivor
-
运行时常量池
JDK8之前这部分内容被放在方法区中。
存储编译期生成的各种字面量和符号引用
具备动态性,Java语言不要求常量一定只有编译期才能产生,运行期间也可以放入(String类的intern()方法,可以运行期间将字符串放入常量池中)
-
-
2.1.3 虚拟机栈
虚拟机栈描述的是Java方法的内存模型。每个方法执行后会创建一个栈帧,栈帧的结构如下:
-
局部变量表
局部变量表占多少插槽。
在编译的时候确定占据多大。
局部变量表的插槽是可复用的。 -
操作数栈
操作栈,在方法执行过程中会有各种字节码指令往操作数栈中写入和提取内容。
操作数栈中的元素的数据类型必须与字节码指令的序列严格匹配。
Java虚拟机也被成为基于栈的执行引擎 -
动态连接
指向运行时常量池中该帧所属方法的引用
-
返回地址 恢复现场
-
其他信息
调试信息等等
-
-
2.1.3 本地方法栈
与虚拟机栈相似。虚拟机栈指定的是本地方法。
-
2.1.4 程序计数器
程序计数器指向当前线程所执行字节码行号。可以通过更改程序计数器的值来选取下一条需要执行的字节码指令,分支,跳转,循环,异常处理,线程恢复等功能都需要依赖这个计数器来完成。
-
2.1.5 直接内存
并不属于JVM虚拟机运行时数据区的部分。在JDK1.4中加入NIO类,引入基于通道与缓冲区IO的方式,可以使用Native函数库直接分配堆外内存。
2.2 对象
-
2.2.1 对象创建
-
加载判断
首先检测常量池中是否有类的符号引用,检查这个类是否被加载,解析,初始化过。如果没有就执行相应的类加载过程。
-
内存分配
加载完成后,虚拟机为对象分配内存,大小由加载完成后确定。分配方法有指针碰撞,空闲列表俩种.
分配策略由Java堆是否规整,堆是否规整取决于垃圾回收期是否带空间压缩整理觉得的。-
指针碰撞
假设Java虚拟机中堆内存是规整的,用过的空间放在一起,空闲的放在另一边。中间存放一个指针作为分界点指示器。
分配内存就是把分界点指示器向空闲区域移动创建大小的相等的距离。 -
空闲列表
假设Java虚拟机堆内存不完整,使用和空闲的区域交错。
虚拟机维护一个列表,记录那些空间是可用的,内存分配时,就把分配一块与对象大小相等的一块空闲区域,并更新表上的记录。 -
内存分配并发控制
-
CAS+不断重试
-
本地线程分配缓存
每个线程固定分配一个大小的区域(TLAB),创建对象的时候,会首先对这块区域进行操作,如果区域满之后再进行同步锁定。
可以通过-XX:+/-UseTLAB 参数设定大小
-
-
-
对象头填充
把信息放入到对象的对象头当中。
对象是那个类的实例
如何找到类的元数据信息
对象的哈希码
对象的GC分代年龄
是否启用偏向锁 等等 -
方法执行
按照程序员编写的内容进行初始化,构成一个真正的对象。
-
-
2.2.2 对象内存布局
-
对象头
HotSpot当中分成俩部分内容。
1.存储对象自身运行时数据。如:哈希码、GC分代年龄、锁状态标志、线程持有锁、锁偏向ID、偏向时间戳
2.类型指针,即对象指向类元数据的指针 -
实例数据
对象的有效信息,我们在程序代码中定义的各种类型的字段内容。
这些内容的分配策略会收虚拟机分配策略和定义顺序影响。Hotspot存储分配策略是按顺序longs/doubles、ints、shorts/chars、bytes/booleans、oops(Ordinary Object Pointers)
父类的变量分配在子类之前。
如果CompactFileds 设置为true,子类较窄的变量会穿插在父类的变量空隙中 -
对齐填充
占位符,方便内存管理。对象大小是8字节的整数倍。
-
-
2.2.3 对象的访问定位
-
使用句柄访问
划分出一块内存作为句柄池,reference当中存储的就是对象的句柄地址。相当于一个二次指针。
好处是可以存储对象的位置,也可以存储对象实例数据。
也可以对对象的移动修改简单,只需要改变句柄值 -
直接指针访问
直接存放对象的地址。
需要考虑如何找到对象的实例数据。这样速度更快,对象的访问在Java中非常频繁
-
2.3 OutOfMemoryException
详细实