博客中相关代码的git 地址:
JVM学习(一)自动内存管理机制 | 地址:https://blog.csdn.net/qq_40119805/article/details/107399842 |
---|---|
JVM学习(二)垃圾回收器和内存分配策略 | 地址:https://blog.csdn.net/qq_40119805/article/details/107432575 |
JVM学习(三)虚拟机执行子系统 | 地址:https://blog.csdn.net/qq_40119805/article/details/107456166 |
JVM学习(四)虚拟机类加载机制 | 地址:https://blog.csdn.net/qq_40119805/article/details/107456166 |
JVM学习(五)JVM并发内存模型 | 地址:https://blog.csdn.net/qq_40119805/article/details/107456166 |
本章主要介绍:
1.jvm内存分布和创建对象如何分配内存;
线程基础
1.自动内存管理机制
1.1 运行时数据区
运行时数据区有5大块,分别为:堆,栈,方法区,程序计数器;其中栈分为本地栈和java栈;
按照线程私有 有程序计数器,栈*2;
按照线程共有 有 堆 ,方法区;
(1)程序计数器
简单的说程序计数器就是记录字节码指令执行到了哪里;
这是唯一一个在java虚拟规范中没有规定任何OOM的区域;
(2)java虚拟机栈
java栈是线程私有的,且他与线程生命周期相同;每个方法在执行时都会生成一个栈帧;栈帧中存储这程序方法的基本信息,比如 局部变量表,动态链接,方法出口等;
局部变量存放了编译器可预知的基本数据类型和引用变量;
(3)本地方法栈
本地方法栈和java虚拟机栈类似,只不过本地方法是为native服务的,java栈是为java服务的;
(4)java堆
java堆主要用来存放实例对象,基本所有的实例对象和数组都存在于堆中,另外java虚拟机规范中规定,java堆内存可以是不连续的,在逻辑上连续即可;
(5)方法区
用于存放已经加载的类信息,常量,静态变量,静态变量,即时编译后的代码等数据;
(6)运行时常量池
常量池署于方法区的一部分;用于存放各种字面量和类的符号引用;
2.HotSpot 虚拟机对象相关
2.1 对象的创建
(1)内存分配
当虚拟机遇到一条new指令时,他首先回到常量池中检测,是否能找到一个该类的一个符号引用,并检测是否已被加载和初始化,如果没用那会先执行类的加载过程。
类加载完成后会为新生对象分配内存,分配内存有两种方式:
1.指针碰撞:在gc堆使用标记整理算法时内存时规整的,使用的内存放在一边,未使用的放在另一边,这样每次分配对象只要将指针向另一侧移动相应的大小即可。
2.空闲列表:当gc堆使用标记清理算法时,堆内存时不规整的,所以需要额外维护一个列表来记录哪块内存使用了,那块内存未使用;
(2)分配对象的并发问题
对象的创建在虚拟机中是一个非常频繁的行为,哪怕只是修改一个指针所指向的位置,在并发情况下也是不安全的,可能出现正在给对象 A
分配内存,指针还没来得及修改,对象 B 又同时使用了原来的指针来分配内存的情况。解决这个问题有两种方案:
对分配内存空间的动作进行同步处理(采用 CAS + 失败重试来保障更新操作的原子性);
把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在 Java 堆中预先分配一小块内存,称为本地线程分配缓冲(Thread
Local Allocation Buffer, TLAB)。哪个线程要分配内存,就在哪个线程的 TLAB 上分配。只有 TLAB
用完并分配新的 TLAB 时,才需要同步锁。通过-XX:+/-UserTLAB参数来设定虚拟机是否使用TLAB。