上一节我们学习了虚拟机对内存管理的一些知识,这一节我们就来一起学习一下对对象的管理。主要从一下几个方面来学习:
一、 对象的创建过程
类加载器遇到new指令后,开始检查是否加载,如果加载成功了,就开始给对象分配内存,分配好内存后进行内存空间的初始化,并设置一些对象信息等,然后再完成对象的初始化工作,这时候一个java对象就创建成功了。

1)分配内存
1)划分内存
虚拟机在对象创建时需要申请内存空间,系统在划分内存空间时主要通过下面两种方式:
1.1) 指针碰撞
通过指针在一片连续的内存空间上的偏移量来划分内存,称为指针碰撞。这样划分的内存适合标记整理算法来回收内存;
1.2) 空闲列表
有一块单独的内存,存放的是哪些内存区域是空闲的信息表,称其为空闲列表,然后通过查询这个空闲表的内容来划分内存,这样划分的内存适合标记清楚算法来回收内存。
2)并发安全的问题:
1)CAS加载失败的问题(CAS 是通过比较交换的算法来完成,这样比较耗性能)
2)本地线程分配缓存(TLAB):预先就线程的对象分配到一块内存区域内,这样做的效率比较高,线程安全
2)内存空间初始化
内存空间初始化就是将申请好的内存空间先进行默认零值初始化。
3)设置对象信息
设置对象的信息,组信息,年龄,类型等
4)对象初始化
对象的初始化就是指调用对象的构造方法,进行对象的初始化工作。
二、对象的内存布局
在java中,万物皆对象,那么一个对象在JVM虚拟机中包含哪些内容呢?如下图所示:

三、对象的分配策略
1)首先检查对象是否可以在栈上分配。
(在栈上分配的对象,是不需要GC回收的,在栈帧结束后就会是释放)
采用的是逃逸分析技术来检查。如果分析是逃逸的对象,则不适合在栈上分配,而未逃逸的大部分对象则可以在栈上分配
逃逸分析的场景:
a、方法逃逸:方法的参数没有传出去 b、线程逃逸:一个对象是否被其他线程在调用
逃逸分析的原理(对象的作用域原理):
如果对象在方法中定义,在该方法的函数体外有其他地方在使用,那么该对象就逃逸了。而没有在外部使用的对象则不会逃逸。
逃逸的对象不会被立即回收。
2)不适合栈上分配内存的对象,优先在堆的eden区分配
3)如果是大对象,就直接进入堆的老年代(如非常大的字符串,数组等)
4)长期存活的对象进入老年代
5)动态对象的年龄判定
四、如何判断对象存活
JVM通过如下的几种方式来判断对象是否存活:
1)引用计数法
通过一个计数器来统计对象是否存活,当一个对象被引用一次,则该计数器会+1,对象被释放一次,该计数器就会-1 ,当计算器为0时,标识该对象没有被引用,可以被回收。
引用计数法存在一个问题:就是两个对象相互引用,就会导致这两个对象的引用计数永远不会为0
2)根可达分析
根可达分析是判断一个对象是否是GC Roots可以直接或者间接访问的,如果是可达的,则不会被回收,如果是不可达的,就发生GC时,会被回收。
GC Roots 包括:静态变量、线程栈变量、常量池、JNI指针、class,NullPointEcception ,类加载器,锁,JMXBean等等)
3)finzlized()
该方法是用于救活对象,只会执行一次,需要我们自己重写其实现,优先级非常低,
五、对象的访问
1)句柄:使用一个符号来执行真实的对象实例,该符号也就是对象的引用,称其为句柄。
2)直接指针:直接使用指针的方式访问对象
六、对象的各种引用
1)强引用 :就是直接=赋值的对象,这种强引用GC比较难回收
2)软引用:发生OOM时,会被GC回收
3)弱引用:只要发生GC,就会被回收
4)虚引用:随时都会被回收,主要用来检测垃圾回收器是否正常工作。
<u>由此可见对象的几种引用对GC回收器回收的难易程度是由强到弱的(强引用>软引用>弱引用>虚引用)</u>
GC回收器不是实时允许的,比较耗性能,在我们的业务代码中尽量减少使用,系统会自动触发,不需要我们手动调用。
GC回收器主要是回收对立面的东西,内存不足时才会触发GC回收
总结:

对象的创建:
类加载器遇到new指令后,判断是否已经加载类的相关信息,如果没有加载则重新加载,否则的话就开始向操作系统申请内存空间,系统根据分配策略来确定对象是在栈内存还是堆(eden ,from ,to ,老年代)内存中,然后采用指针碰撞(连续内存采用)或者空闲列表(分散内存)的方式,划分会对象的内存空间。然后再对申请好的对象内存空间进行全部0值的初始化。0值初始化结束后,进行设置对象的类型信息,组信息等,最后调用对象的构造函数进行对象的初始化。 这就是JVM创建对象的全部过程。
对象的使用:
对象创建好后,我们可以通过对象引用或对象指针的方式对内存对象进行操作。针对访问对象的方式不同,在发生GC时,对象的回收情况也不相同。
对象的销毁:
当我们的的内存空间不够时,会发生GC ,这时就会主要回收在堆内存的对象,在回收对内存对象时,采用引用计数法或根可达分析算法来判断对象是否存活,如果对象没有被引用,或者不可达,就会被回收。
当然如果对象时在栈上分配的内存,那么在栈帧结束后,对象内存就已经被回收了。
网友评论