JVM内存模型-入门到精通

1、JVM内存模型主要分为两类,线程共享和线程私有。
线程共享:方法区、堆
线程私有:程序计数器、虚拟机栈、本地方法栈
 
方法区:线程共享的内存区域,主要存放类信息,常量,静态变量。其中运行时常量池(常量池)也是在方法区内,常量池中主要存放字面量和符号引用
 
:JVM管理内存的最大一块内存空间,存放实例对象、数组。线程私有缓冲区(TALB)也是在堆上面的,但和堆内存互不影响。在随着JIT编译器的发展,并不是所有实例对象和数组一定是存放在堆内存上面。参考(逃逸分析、栈上分配、标量替换)优化技术。
 
程序计数器:线程的字节码执行的行号指示器,用来记录线程下一步的执行指令。字节码解释器会根据程序计数器中的值来获取线程下一条执行指令,也是整个内存空间中唯一一个不会抛出OOM异常的内存区域。
 
虚拟机栈:虚拟机栈也就是我们常说的栈内存,主要存放局部变量表、操作栈帧、动态链接、方法出口等信息。局部变量表中存放基础类型数据,对象引用,其中long和double类型占两个局部变量空间,其余类型占一个局部变量空间。
 
本地方法栈:本地方法栈中存什么我也不知道,(⊙o⊙)…如果面试被问到我觉得那个人脑子有问题......存储的内容没有强制规定,有虚拟机自由实现。主要是为虚拟机调用native方法服务的。
 
 
JVM内存模型中,除了程序计数器外其余内存空间都会抛出OOM异常。其中栈内存还会抛出StackOverflowError异常。
 
 
 
 
2、 JVM创建对象过程
 
使用new关键字来实例化一个对象的时候,JVM会去常量表中定位到这个类的符号引用,然后会去检查这个符号引用所代表的类有没有被加载、分析、初始化过,如果没有,则启动类加载器来加载,执行相应的类加载过程。然后在堆内存上开辟一块空间,优先在新生代的Eden区域开辟,由于在类加载编译过程中,类的内存空间大小已经是预知的,所以根据预知的空间大小来开辟。如果Eden区域没有相应的空间大小,则向后移动内存空间区域(指的时Survivor或者老年代内存区域)。如果还是没有内存区域,则执行垃圾回收器进行内存回收,如果垃圾回收器回收后依然没有足够的内存空间(这里的垃圾回收器指的时CMS或者G1),则执行Full GC,如果还是没有,则抛出OOM异常,实例化对象失败。划分内存的方式有两种,一种是指针碰撞,一种是空闲列表,至于采用何种方式,取决于GC的类型。为了避免多线程并发的安全问题,JVM提供两种解决方式,一种是CAS操作加失败重试机制,另外一种是把内存按线程来划分到不同的内存空间,即本地线程缓冲区(TALB)。创建好对象以后,进行初始化,默认值都为零,在JVM的角度看来,对象已经创建成功,但是在程序角度看来,对象还未创建成功,才刚刚开始,接下去会执行初始化方法,进行对象初始化。完成真正意义上的对象创建
 
3、 对象的内存布局
对象存储的布局可以分为3快空间, 对象头实例数据填充对齐
对象头:对象头包含两部分, 第一部分Mark Work,用于存储对象自身信息,例如hash code,GC分代年龄,锁状态标志、线程持有的锁、偏向线程ID、偏向锁时间戳等,如果对象是一个JAVA数组,那么对象头中还有一块用于存储数组长度的数据。
第二部分为类型指针,指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
第二部分:实例数据部分是对象真正存储的有效信息,也就是程序代码中定义的各种类型的字段信息。无论父类还是子类信息,都会记录下来。这部分存储顺序会受到虚拟机分配策略参数(FieldsAllocationStyle)和字段在JAVA源码中定义顺序的影响。
第三部分:填充对齐...不做过多解释,自己查资料。
 
4、对象的访问定位
栈内存上的reference数据是指向对象的引用,目前主流的访问方式有两种, 句柄直接指针
句柄:在JAVA堆内存上划分出一块内存做为句柄池,reference数据中存储的就是对象在句柄池中的地址,而在句柄中包含了对象的实例数据和类型数据地址,分别指向实例池和方法区的对象类型数据。
直接指针:直接存放实例对象的地址。而java堆对象一部分存储了对象实例数据,另外一部分存储了对象类型数据。
 
 
 
 

你可能感兴趣的:(java虚拟机,jvm,虚拟机,指针)