1 对象的创建
- 1.虚拟机遇到new指令时首先去检查这个指令的参数能否在常量池中定位到一个类的符号引用,并且检查引用代表的类是否已被加载、解析和初始化过。如果没有,则执行类加载过程(第7章
虚拟机类加载机制)。 - 2.加载检查通过后,分配内存(内存在类加载完成后便可完全确定)。
- 3.内存空间初始化,如int,boolean类型变量初始赋值0/false
- 4.内存分配完成后,虚拟机对对象进行必要的设置,如对象是哪个类的实例、如何找到类的元数据信息.对象哈希码.对象的GC分代年龄等(都放在对象的对象头中)。
内存分配方式有:指针碰撞和空闲列表。使用哪种方式取决于堆是否规整(又由垃圾回收决定)。
内存分配的线程安全问题:1.CAS配上失败重试的方式保证原子性;2.每个线程在Java堆中预先分配一小块内存,本地线程分配缓冲(TLAB)。分配完成后内存空间初始化为0值(不包括对象头)。
从虚拟机角度看,一个新的对象产生了,但从java程序视角看,对象创建才刚刚开始,因为<init>方法还没有执行,,所有字段为零。
- 5.执行new指令之后会接着执行<init>方法(构造方法),进行初始化,这样一个真正可用的对象才算完成产生。
2 对象的内存布局
对象在内存中存储的布局可以分为3块区域:对象头、实例数据、对齐填充
①对象头含两部分(Header)
- 存储对象自身的运行时数据,如哈希码、GC分代年龄.锁信息(锁标志,线程持有的锁,偏向线程ID)等。长度在32位和64位的虚拟机中,分别为32bit、
64bit,官方称它为“Mark Word”。 - 类型指针,对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
备注:如果对象是java数组,对象头中还必须有一块记录数据长度的数据
②实例数据(InstanceData)
- 对象真正存储的有用信息,也是程序中定义的各种类型的字段内容,包括父类继承的都记录下来,存储顺序受到虚拟机分配策略参数和字段在Java源码中的顺序的影响。
③对齐填充(Padding)
- 由于HotSpot虚拟机要求对象的起始地址必须是8字节的整数倍,通俗的说,就是对象大小必须是8字节的整数倍。对象头正好是8字节的倍数。当实例数据部分没有对齐时,需要通过对齐填充来补全。
3 对象的访问定位
Java程序通过栈上的reference数据来操作堆上的具体对象。
不同虚拟机实现的对象访问方式会有所不同,目前主流的访问方式有两种:使用句柄和直接指针。
- 使用句柄:堆中划分出一块内存作为句柄池,reference存储对象的句柄地址 是间接访问,优点是reference中存储的是稳定的句柄地址,对象移动(垃圾会收时)时只会改变句柄中的实例数据指针。
- 使用直接指针 是直接访问,节省了一次指针定位的开销,优点就是速度快。(HotSpot)
注:对象类型数据指针(类对象引用)在Java堆里面。(个人观点:是类对象引用在编译器存在于常量池,而常量池是在堆里面)
4堆内存分配策略
1.对象优先在Eden区分配:大多数情况下,对象在新生代 Eden 区中分配。当 Eden 区没有足够空间分配时,虚拟机将发起一次 Minor GC。
2.大对象直接进入老年代,如参数:
-XX:PretenureSizeThreshold=4m
-XX:+UseSerialGC
PretenureSizeThreshold 参数只对 Serial 和 ParNew 两款收集器有效。4m表示大于4m的对象直接进入老年代。
最典型的大对象是那种很长的字符串以及数组。这样做的目的:1.避免大量内存复制,2.避免提前进行垃圾回收,明明内存有空间进行分配。
3.长期存活对象进入老年区,对象头里面有GC分代年龄。
如果对象在 Eden 出生并经过第一次 Minor GC 后仍然存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设1,对象在 Survivor区中每熬过一次 Minor GC,年龄就增加 1,当它的年龄增加到一定程度(默认为 15)时,就会被放到老年代中。
4.对象年龄动态判定:在 Survivor 空间中相同年龄所有对象大小的总和大于 Survivor 空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代.
5.空间分配担保
在发生 Minor GC前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么 Minor GC 可以确保是安全的。
如果不成立,则虚拟机会查看 HandlePromotionFailure 设置值是否允许担保失败。如果允许也就是空间担保,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC,尽管这次 Minor GC 是有风险的,如果担保失败则会进行一次 Full GC;如果小于,或者 HandlePromotionFailure 设置不允许冒险,那这时也要改为进行一次 Full GC。
备注:FullGC是老年代垃圾回收Minor GC则是新生代回收,老年代对象数量一般比较多,FullGC是比较消耗性能的
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。