面向对象的编程语言
众所周知,Java是一门面向对象的编程语言,有人会问,什么是面向对象的编程语言呢,通常听到最多又最古老的话就是“万物皆对象”,那么你对你的“对象“到底知道多少呢?
你的对象是如何产生的呢?你的对象又存有哪些属性呢?你的对象存放在哪里呢?你的对象又是怎么死亡的呢?
下面我们带着上面的疑问来对你的对象进行一下深入了解吧。
一.对象实例化的过程
稍微有点Java基础的人都知道,我们创建一个对象通常有两种方式,一种就是常见的”new“一个对象,再一种就是通过反射来获取到你的对象。
在这里,我们看看”new“一个对象的过程吧。
①当JVM遇到一条字节码new指令的时候,首先会进行”检查加载“,这里的检查加载就是去检查你对象这个类是否已经加载并初始化过,如果没有,则会去进行一次类加载过程,然后再次去检查加载。
②当检查加载验证通过后,JVM会为其分配内存,绝大多数的对象都是在堆中分配内存,部分对象会涉及到逃逸分析,这里先不进行讨论。
③完成内存分配后,会进行内存空间的初始化,再接下来就是会对对象进行设置,然后一点点进行对象的初始化,比如一步一步初始化类的成员、代码块、构造方法等。
通过以上步骤,我们的对象就诞生了,具体流程可以参考一下下图。
二.对象中存放这什么东西呢
通常我们的对象由三部分组成,分别是对象的头、对象的身体、对象的腿 分别是对象头、实例数据、对齐填充。
我们先从后往前看,先说对齐填充,其实这部分存储的东西并没有什么实际的意义,有时候可以没有这部分,因为Java的对象占用的空间必须是8字节对齐的,即所有对象占用的位数必须是8的整数倍,这个对齐就是用来补位用的,所以他可能有可能没有,且没有实际意义。
再者就是实例数据,实例数据我们都知道,就是一个对象的所有的成员变量。
对象头相对来说存储的东西还是比较重要的。对象头里面通常又存放三种东西,一是存储对象运行时自身数据(Mark Word),其中包括哈希码、GC年龄、锁标识状态、线程持有的锁等。二是存放着对象的类型指针,通常我们都是通过指针来访问一个对象。三就是当对象是数组的时候,这个对象头中还要记载数组的长度。
具体我们可以看下图表示
三.我们的对象存在哪?
醒醒吧,你哪来的对象,该起来搬砖了
上文我们说到,绝大多数的对象都是在堆中的,但是JVM会有逃逸分析算法,JDK7以后版本都是默认开启逃逸分析的,我们也可以通过命令来开启和关闭逃逸分析。有关逃逸分析可以看下这个文章,写的很全面。
-XX:+DoEscapeAnalysis :开启逃逸分析(从JDK1.7开始默认开启)
-XX:-DoEscapeAnalysis :关闭逃逸分析
但是在即使对象分配在堆中,也是有不同的区域的,有的对象会存活在新生代,收到一次次GC带来的煎熬,有的对象则会直接进入老年代,来安享晚年。。。
四.我们的对象又是什么时候死亡的呢
提及到对象的死亡就离不开垃圾回收机制。通常有两种垃圾回收算法,一种是引用计数法、一种是可达性分析。现在JVM通常用的都是可达性分析法。
可达性分析法就是分析这些对象有没有大哥带着,就是他的关系链中有没有大哥,没有大哥保护的话一次GC来了就全都GG了。这里我们说的大哥就是所谓的GC roots,通常能作为大哥的有静态变量、线程栈变量、常量池、JNI(指针),所谓的有大哥带着就是他们之间存在这引用关系。
但是有大哥带的话就一定不会被回收了吗?
当然不行,有的对象又软又弱,甚至有的还很虚,有大哥带也有可能被回收,这就是我们常常谈及的对象的四种引用了,分别是强引用、软引用、弱引用及虚引用,引用想必大家都很熟悉,这里就不谈了,感兴趣的话可以看看其他引用相关的文章。
那么没大哥的对象就一定会完蛋吗?
答案也是否定的,我们的Object类中有一个finalize方法,这个方法就像一块免死金牌,但是又不是百分百会免死,这个方法在高版本的Java已经被废弃了,这里我们稍作了解就好了,知道有这么回事就好。
那么以上就是本篇文章的全部内容,大家如果对JVM感兴趣或者对对象感兴趣,欢迎来我的微信公众号来找我