JVM的学习2____对象实例的内存分配原理
1.JVM的三种引用类型:
之前介绍过,为了提高GC的执行效率Java的实例对象也可以创建在堆区之外,但是绝大多数的实例对象还是创建在了堆区中。在JVM中包含三种引用类型即:类类型(class type),数组类型(array type),接口类型(interface type),这些引用类型的对象实例分别由类实例,数组实例,接口的实现类实例动态创建。
2.JVM为对象实例分配相应的内存空间大小:
那么JVM是如何创建一个对象实例?以一个Java类类型为例,在语法层面创建一个类的对象实例就是new一个对象,可是在JVM中就包含可多的细节了(涉及到类的加载过程,我们下一章会讨论)。当类已经装载完毕后,JVM就可以确定为该对象实例应分配的内存空间大小。
给对象实例分配内存空间,需要考虑到内存的分配,内存的回收问题,此外一次GC过后还会有可能会导致内存出现碎片化降低内存的使用率。关于内存的分配算法,此处提到两种空闲列表法,指针碰撞法。所谓指针碰撞法就是将内存以规整有序的排放,即就是未用的内存和已用的内存分开放,中间用指针维系着,该指针指向下一次分配的内存起始点,当再给一个对象实例进行内存分配时只需要将指针移动相应的偏移量即可。
3.对象实例分配的内存究竟在哪?
基于分代的思想,堆区可以分为新生代,老年代,其中新生代按比例1:1:8又可以分为fromSurvivor,toSurvivor,Eden。由于堆区和方法区都是线程共享的,但是内存的分配得必须保证原子性。快速分配策略:当该类已经完成装载步骤后,JVM会优先在TLAB(Thread Local Allocation本地线程分配缓冲区)为对象实例分配内存空间,TLAB(本地线程分配缓冲区)是位于堆上的线程私有的一部分,它包含在Eden中(所以说堆上也是有线程私有的内存区域)。
一般情况下,TLAB(本地线程分配缓冲区)是JVM给对象实例分配内存空间的首选地方,但是TLAB(本地线程分配缓冲区)非常的小,只占Eden区域的1%(我们也可以通过命令进行收的更改大小)当TLAB无法进行内存的分配时,JVM就会采用加锁的方法在Eden中进行内存的分配,当失败时就会调用MinorGC,直到可以在Eden中分配内存为止,若是需要分配大的内存区域,则直接会在老年代中进行分配。
(TLAB---》加锁机制Eden---》MinorGC----》老年代)
4.初始化对象实例,应用入栈,修改PC寄存器的字节码地址:
当给对象实例分配完内存后,JVM其次要做的就是给对象赋0值,这一步操作也就是为什么Java类的对象不赋初始值就可以使用的原因,赋完0值后,JVM就会初始化对象头和实例数据,再将对象的引用入栈,再修改PC寄存器的字节码指令地址。