一.简述
当我们看到MyClass mc = new MyClass()的时候,或者说当我们看到new这个关键字的时候,我们就知道,会有一个MyClass类的实例被创建出来,也知道MyClass实例是经过JVM根据MyClass类文件创建的。但是我们有没有想过一个问题:JVM在遇到new这样关键字的时候到底做了什么?
你可能会说:在堆(heap)中开辟一个内存空间用来保存MyClass对象,然后调用对应的构造方法初始化这个对象。
如果你掌握的更加深入一点,也许会说:
1.检索构造器所在的类的符号引用,并利用这个符号引用找到位于方法区常量池中对应的类信息;
2.如果没有找到这个类信息,则加载这个类;
3.在堆中开辟内存空间,然后将其分成三部分,对象头+实例数据+对其填充;
4.再然后将除了对象头之外的内存空间整体清零(想一想作用是什么?保证对象的每个字段具有一个初值);
5.之后写入对象头的信息;
6.最后调用相应的构造方法。
回过头来,我们看一看2号语句:“加载这个类”说得很模糊,只有在遇到new的时候加载么?加载到哪?如何加载?(经典的when,where,how)。
注:《深入理解JVM》中将类的加载全过程分为了7段:加载,验证,准备,解析,初始化,使用,卸载。本文只讨论了某些细节,一些过程并未涉及。
二:何时JVM会加载一个类?When?
JVM很规范,或者说很死板,当且仅当它遇到以下5中情况的时候,它才会执行类加载过程:
1.遇到new,getstatic,setstatic,invokestatic这四条指令码的时候且相应的类没有加载到常量池中,那么开始加载过程。通俗的说:遇到新建对象,获取/设置某各类的静态变量,调用某各类的静态方法的时候,会进行类加载工作。
2.通过反射调用一个类的时候,进行类加载。这一条很简单,不加载类,怎么反射?
3.初始化一个类的时候,如果类A的父类B还没有加载,那么会先加载类B,然后再加载类A。如果B是一个接口,那么会推迟B的加载,等到使用到B的接口方法的时候才会加载B。
4.对于主函数(main函数)所在的类先加载。
5.JDK1.7动态语言支持。动态语言相关请参考文章:https://www.cnblogs.com/web-java/p/5759376.html
三.将类加载到哪里?Where
虚拟机会将类信息存储到方法区中,包括类信息,静态变量,类的方法符号引用和一些字面量将会存储到方法区的运行时常量池中。
四.如何加载类?How
1.class文件是一个二进制文件。JVM通过类的全限定名获取这个类的二进制字节流;
2.将类的静态存储结构存入方法区中相应的位置。
类初始化过程:先将static变量声明顺序初始化,然后进行static块的初始化。一个类只会初始化一次。
3.为这个类生成一个class对象,作为方法区中这个类的各种数据的访问入口。
五. 一些补充:
1. 关于类加载器:
这时候我们就要介绍JVM中大名鼎鼎的双亲委派模型。
每个类的加载过程都是通过类加载器来进行加载的。jvm中类加载器分为三种,这三种类加载器分别负责加载不同的类。
1)启动类加载器:
2)扩展类加载器:
3)应用程序类加载器: