https://blog.csdn.net/u011208987/article/details/79121782
对象创建的过程
对象的创建
对象创建有3中方式:new 、反射 、反序列化
对象的组成
对象一定会按照8个字节的整数倍来存储。
对象头Header
自身运行时数据(Mark Word)
哈希值
GC分带年龄
锁状态标志
..
实例数据
类型指针:指向字节码的指针
padding(对齐填充:为了实现8个字节的整数倍存储,剩余空间采取自动填充。)
Mark Word
对象的内存空间策略
对象创建时,会给对象分配内存,分配内存有两种类型:
1.指针碰撞:连续分布的地址空间(没有内存碎片),会采用指针碰撞的方式来分配内存。
根据指针的移动来分配内存。
2.空间列表:对于内存地址不连续的,会采用空间列表来记录哪段地址可以使用,哪段内存地址被使用过了。
对象创建的线程安全性问题
堆内存本地线程分配缓冲(TLAB策略):为每个线程分配一段空间来创建对象使用。
对象的访问定位
1.使用句柄,进行定位对象
对象引用会先到句柄池中查找对象的地址,然后在由句柄池访问对象真实数据。缺点:两次访问
2.使用直接指针进行访问
对象引用直接访问对象真实数据。(HotSpot中用的就是这种对象访问)
垃圾回收
如何判断对象是否为垃圾?
1.引用计数法
对象中维护一个计数器,被引用就自动+1.引用失效,自动-1。
引用计数法无法解决对象之间相互引用的问题。现在HotSpot已经不再使用了。
-verbose:gc -XX:+PrintGCDetails
2.可达性分析法(GcRoot方法)
任何一个对象不能通过一定的轨迹被GCRoot对象指向,则可判定为该对象为垃圾对象。
作为GcRoot的对象:虚拟机栈(局部变量表)、类变量
如何回收垃圾对象
1.标记清除算法
过程
- 标记:扫描整个堆内存,从GCRoot(根集合)开始扫描,扫描存活的对象,对存活的对象进行标记
- 清除:扫描整个堆空间,回收没有被标记的对象。
优点:比较简单
缺点:具有内存碎片。耗时(会对堆内存进行扫描两次)
2.复制算法
过程:
新生代:Eden 、From Suriver 、To Suriver
1、如果Eden区内存允许,对象出生,会优先分配在Eden区域。当Eden区域内存不足,那么会执行Minor GC,
2、此时Eden区域存活的对象会被复制到To Suriver区;
From区域中存活的对象,会根据年龄值来决定去向。年龄超过阈值,则会复制到老年代。年龄低于阈值,会被复制到To Suriver区,且年龄值+1;
然后清空Eden区域和From区域, From Suriver和 ToSuriver调换名称。From改为To ,To改为From。
3、GC一直重复这样的过程, 知道To区被填满,会把所有的对象移到老年代。
首先对象会被分配到Eden区
3.标记压缩算法
1.扫描整个堆内存,从GCRoot开始扫描,标记存活对象
2.将存活的对象移到连续的地址。
缺点:耗时,标记整理时会暂停用户线程。和Windows的磁盘整理挺像的。
内存分配策略 ?
-XX:PretenureSizeThreshold
-XX:MaxTenuringThreshold=15
-XX:+HandlePromotionFailure
逃逸分析与栈上分配
逃逸对象:在方法中创建的对象,被外部变量获得引用,这时候就称为逃逸对象。(对象没有超出方法的范围就是没有逃逸,对象超出方法的范围就是逃逸)
把这些没有逃逸的对象在栈上进行分配,称为栈上分配。