JVM系列总结

对象的一生

Java是一门面向对象的语言,所以在Java程序运行中无时无刻的都有对象被创建。创建的方式有很多种譬如new,克隆,反序列化等方式。当然对象也有很多种譬如普通的Java对象,数组对象或者是代表类的Class对象。那么在jvm中使用new关键字创建一个普通的Java对象,虚拟机都做了那些事情呢。

当虚拟机遇到new 指令时,首先去检查这个指令的参数是否在常量池中定位到一个类的符号引用。并检查这个引用代表的类是否已经被加载,解析,初始化。(双亲委派模型,一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上加载)如果没有那就必须先执行相应的类加载过程。

类加载过程

首先虚拟机会使用应用程序类加载器(Application ClassLoader)去加载,然后把加载的数据转换成二进制字节流,再将二进制字节流转换成方法区运行时的数据结构,最后在Java堆中生成一个java.lang.Class对象。并将这个Class对象指向方法区的数据结构。此时类的加载阶段已经结束。

类加载之后会进行链接:1,链接会对二进制字节流进行验证,2,为变量分配空间。3将符号引用替换为直接引用。链接过程完毕后会执行类的初始化。某种程度上来说就是调用Clinit()方法,该方法首先会为调用父类的Clinit()方法,然后为静态语句或者是静态块赋值,该方法还保证了初始化一个类使用单线程。

完成上面操作后,虚拟机将会为新生对象分配内存,当然这个对象会在Java堆的Eden区分配。分配内存的方式主要有两种:

  • 指针碰撞:分配内存是只是将指针向空闲空间移动分配空间的距离
  • 空闲列表:内部维护一个列表,当分配空间时 在列表中找出足够大的空间记录在列表里。

内存分配

内存分配完成之后虚拟机会把分配的空间都初始化为零值(不包括对象头),然后就是对对象进行必要的设置,譬如如何找到类的元数据信息,对象的哈希吗,对象的GC分代年龄,这些信息都会被设置到对象头中。如果根据虚拟机是否支持偏向锁,则在对象头中启用偏向。此时在虚拟机的角度来说,新的对象已经产生了,但是对于Java语言来说,对象并没有产生。因为在Java语言中对象的开始时init()方法(构造函数),这个方法是编译器给生成的,init()主要干了下面几件事

  • 调用另一个()方法(本类的另外一个()方法或父类的()方法)
  • 初始化实例变量

此时,一个Java对象真真正正的被创建在了eden区,我们暂时加他小Z,说一下接下来小Z的生活。

GC过程与同步锁

在程序正常运转了一段时间多次GC后,小Z从Eden区搬到了From区。之后小Z躲过了多次GC,幸运的存活了下来。当小Z长到了15岁的时候,小Z从From区搬到了敬老院(Old 区)。

当小Z(此时启用偏向)第一次走到了同步代码块时,此时并没有对象与他竞争这块区域,虚拟机便把这块代码的锁给消除了,设置为把小Z的对象头设置了一个偏向锁标志位。后来,来了少量的对象与小Z竞争这个代码块,虚拟机又将锁升级为轻量级锁。再后来来了很多对象,小Z没有竞争过,于是虚拟机把锁设置为重量级锁,并把小Z所在的线程挂起。

随着系统的升级,小Z的作用越来越少,直到没有指针指向小Z,寿终正寝,最后被小Z被虚拟机回收,当系统快要关闭时,小Z的加载器也会被回收掉。小Z的加载器被回收掉,紧接着小Z也会被回收。

本片系列文章参考主要深入了解Java虚拟机一书,还有网络上的一些博客,这里就不一一列举了。本来我是写在word中,然后搬到了blog中。适合于想研究JVM或想深入学习一下JVM的话,可以参考本系列文章。如果有哪里不对,欢迎指出感激不尽。

文章阅读顺序

JVM系列(一)类的加载机制

JVM系列(二)JVM内存结构​

JVM系列(三)GC算法 GC收集器​

JVM系列(四)JVM配置参数​

JVM系列(五)GC分析

JVM系列(六)Java内存模型和线程安全

坚持原创技术分享,您的支持将鼓励我继续创作!
  • 本文作者: XiuYu.Ge
  • 本文链接: 366.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!