我的JVM学习(3)

                                    我的JVM学习  (内存溢出异常)


对象的创建过程

1、虚拟机遇到new命令时,首先先去检查这个指令的参数能否在常量池中定位到一个类的符号引用,并且检查这个类是否被加载、解析和初始化,如果没有,先执行类加载过程。

2、在类加载检查通过后,虚拟机将为新生对象分配内存,为对象分配内存等同于把一块确定大小的内存从Java堆中划分出来。

划分内存的方式分两种:

-指针碰撞:当Java堆中内存是绝对规整的,用过的和空闲的内存划分两边,只需要在分界点放一个指针,分配内存时移动指针即可。   (在使用Serial、ParNew等带Compact过程的收集器时,采用指针碰撞)

-空闲列表:当Java堆中已用的和空闲的内存交错时,虚拟机维护一个记录有空闲内存的列表,分配内存时在表中划分一块足够大的内存,并更新列表。 (在使用CMS这种基于Mark-Sweep算法的收集器时,采用空闲列表)

3、Java堆内存的分配非常频繁,为了并发的情况下保证线程安全:第一种是对分配内存空间的动作进行同步处理。第二种是使用本地线程分配缓冲(TLAB),每个线程在堆中预先分配一小块内存,线程分配内存是在自己的TLAB中分配,只有TLAB用完并分配新的TLAB时才需要同步锁定。(通过-XX:+/-UseTLAB参数来设定虚拟机是否使用TLAB)

4、内存分配完成后,虚拟机将分配的内存空间进行初始化,然后对对象的对象头进行必要的设置(类信息、哈希值、GC分代年龄,是否启用偏向锁)

5、执行init方法,进行初始化


对象的内存布局

对象在内存中的布局分为:对象头、实例数据、对齐填充。

对象头:分为两部分,第一部分用于存储对象自身的运行时数据,如哈希值、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳,这部分的长度为当前虚拟机指针长度(32bit或64bit),官方称为Mark Word。第二部分为类型指针,指向它的类元数据,虚拟机通过指针确定该对象是那个类的实例。(数组的对象头中还包含一块用于记录数组长度的数据)

实例数据:对象真正存储的有效信息,即程序代码中定义的各种类型的字段内容。HotSpot中默认的存储顺序的分配策略是相同宽度的字段总是分配在一起,而且,父类中定义的变量会出现在子类之前

对齐填充:对齐填充仅仅起到占位符的作用,HotSpot中要求对象的大小必须是8个字节的整数倍,而对象头部正好是8个字节的倍数(1倍或2倍),所以当对象大小不够8个字节时,需要对齐填充。


对象的访问定位

句柄访问:Java堆中将会划分出一块内存作为句柄池,引用存储的是句柄位置,句柄指向对象

直接指针访问:Java堆中

猜你喜欢

转载自blog.csdn.net/u012693530/article/details/80068472