ava虚拟机(JVM)创建对象的过程包括以下步骤:
需要注意的是,对象的内存分配和初始化是原子操作的,这意味着在对象初始化完成之前,其他线程无法看到或访问到这个对象。这有助于确保多线程环境
此外,Java虚拟机可能会对对象的内存分配和回收采取一些优化措施,如对象池、分代垃圾收集等,以提高内存利用率和程序性能。
用于存储对象自身的运行时数据,如哈希码(HashCode)、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等,这部分数据的长度在 32 位和 64 位的虚拟机(未开启压缩指针)中分别为 32 个比特和 64 个比特。
包含两部分类信息:
这部分是一个类型指针,即对象指向它的类型元数据的指针,Java 虚拟机通过这个指针来确定该对象是哪个类的实例。
并不是所有的虚拟机实现对象头都具有类型指针,这和对象的访问定位方式有关,主流的访问方式主要有使用句柄和直接指针两种:
使用句柄的方式:Java 堆中将可能会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自具体的地址信息
直接指针的方式:Java 堆中对象的内存布局就必须考虑如何放置访问类型数据的相关信息,reference 中存储的直接就是对象地址,如果只是访问对象本身的话,就不需要多一次间接访问 的开销(HotSpot 虚拟机采用该方式,所以对象头中有类型指针用于存放对象结构的引用)
如果对象是一个 Java 数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通 Java 对象的元数据信息确定 Java 对象的大小,但是如果数组的长度是不确定的,将无法通过元数据中的信息推断出数组的大小。
实例数据部分是对象真正存储的有效信息,即我们在程序代码里面所定义的各种类型的字段内容,无论是从父类继承下来的,还是在子类中定义的字段都必须记录起来。这部分的存储顺序会受到虚拟机分配策略参数(-XX:FieldsAllocationStyle参数)和字段在 Java 源码中定义顺序的影响。 HotSpot 虚拟机默认的分配顺序为 longs/doubles、ints、shorts/chars、bytes/booleans、oops(Ordinary Object Pointers,OOPs),从以上默认的分配策略中可以看到,相同宽度的字段总是被分配到一起存放,在满足这个前提条件的情况下,在父类中定义的变量会出现在子类之前。如果 HotSpot 虚拟机的 +XX:CompactFields 参数值为 true(默认就为 true),那子类之中较窄的变量也允许插入父类变量的空隙之中,以节省出一点点空间。
这里指的是子类在继承父类的字段后,把原本填充的空间放上小的字段类型数据
对象的第三部分是对齐填充,这并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于 HotSpot 虚拟机的自动内存管理系统要求对象起始地址必须是 8 字节的整数倍,换句话说就是任何对象的大小都必须是 8 字节的整数倍。对象头部分已经被精心设计成正好是 8 字节的倍数(1 倍或者 2 倍),因此,如果对象实例数据部分没有对齐的话,就需要通过对齐填充来补全。
什么是GC?
GC 是 garbage collection 的缩写,意思是垃圾回收——把内存(特别是堆内存)中不再使用的空间释放掉;清理不再使用的对象。
为什么要GC?
堆内存是各个线程共享的空间,不能无节制的使用。服务器运行的时间通常都很长。累积的对象也会非常多。这些对象如果不做任何清理,任由它们数量不断累加,内存很快就会耗尽。所以GC就是要把不使用的对象都清理掉,把内存空间空出来,让项目可以持续运行下去。
什么样的对象是垃圾对象?
不再使用或获取不到的对象是垃圾对象。
如何把垃圾对象找出来?
办法1:引用计数法(不采用,不能解决循环引用问题)
办法2:可达性分析(从GC Roots对象出发,不可达的对象就是要清理的对象)
找到垃圾对象如何执行清理?
具体的GC算法
在回收对象的时候 要确认对象不可能再被任何途径使用的对象(以下方法)
核心原理:判断一个对象,是否存在从『堆外』到『堆内』的引用。
为了解决引用计数法的循环引用问题,Java 采用了可达性分析的方法。其实现原理是,将一系列"GCroot"对象作为搜索起点。如果在"GCroot"和一个对象之间没有可达的路径,则该对象被认为是不可访问的。
要注意的是,不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要经过两次标记过程。两次标记后仍然是可回收对象,则将面临回收。
jdk1.2之前:如何reference类型的数据中存储的数值代表的是另一块内存的其实地址,就称该reference数据是 代表某块内存,某个对象的引用
jdk1.2后:
在可达性分析算法中判定为不可达的对象,也不是‘非死不可‘在清理之前会进行两次标记,如过没有发现可达链,则会被标记一次
随后进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法
主要是对常量池的回收和对类的卸载。
在大量使用反射、动态代理、CGLib 、动态生成 JSP 以及 OSGi 场景都需要虚拟机具备类卸载功能,以保证不会出现内存溢出。
类的卸载条件:
不过满足也不一定被卸载,可以通过 -Xnoclassgc 参数来控制是否对类进行卸载。
将存活的对象标记,然后清除未标记的对象。
缺点:标记、清除效率不高,且会产生不连续的内存碎片,导致无法给大对象分配内存
所有存活对象都向一端移动,然后直接清理掉端边界外的对象。实现了内存连续
将内存划分为两块,每次只使用其中一块,内存用完将存活的对象复制到另外上,然后进行清理。
HotSpot虚拟机中Eden和S区比例是:8:1:1,内存的利用率达到90%,如果每次回收有多于10%的存活对象,此时要借用老年代来存储放不下的对象
根据对象存活周期将内存划分为几块,不同区域采用不同算法。
一般堆分为新生代和老年代:新生代使用复制算法;老年代使用标记-清除或标记-整理算法。