前言
今天看了<<深入理解java虚拟机>>后,决定将了解到的东西进行总结。
什么是java类加载
虚拟机将描述类的数据从Class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的java类型。
java类的加载和对象创建的区别
1>java类的加载只是对象创建的第一步,只有在这个类被加载的前提下,才能执行为对象分配内存,初始化内存空间为零值等后续操作。
2>java类的加载初始化类变量并生成java.lang.Class对象。而对象的创建会初始化类的成员变量并生成该类的实例对象。
类的生命周期
从类被加载到虚拟机中,到卸载出内存为止。
必须进行类初始化的五种情况
(1) 遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。
生成这4条指令的最常见的Java代码场景是:使用new关键字实例化对象的时候,读取或设置一个类的静态字段
(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。
(2) 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
(3) 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
接口初始化时,并不要求其父接口全部都完成了初始化,只有在真正使用到父接口的时候(如引用接口中定义的常量)才会初始化。
(4)当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
详细过程
加载:
1)通过类的全限定名获取此类的二进制字节流。
2)将字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3)在内存中生成代表此类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
验证:
为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全。
验证包括:
1)文件格式验证:验证字节流是否符合Class文件格式的规范。
2)元数据验证:对字节码描述的信息进行语义分析,保证其描述的信息符合java规范。
3)字节码验证:分析程序语义是否合法,符合逻辑。(不会危害虚拟机)
4)符号引用验证:对类自身以外的信息进行匹配性校验。
准备:
为类变量分配内存并设置类变量的初始值的阶段。
1)分配内存的仅为类变量,在方法区中分配。
2)初始值为0,并非默认赋的初始值。
解析:
虚拟机将常量池中的符号引用替换为直接引用的过程。
初始化:
执行类构造器<client>()方法的过程。
<client>()方法:
1)编译器自动收集所有类变量的赋值动作和静态语句块中的语句进行合并产生,收集顺序由语句在源文件中的位置决定。
2)虚拟机保证在子类<client>()执行前,父类<client>()方法已经执行完毕。
3)<client>()方法对于类和接口并不是必需的。
4)执行接口的<client>()方法不需要先执行父接口的<client>()方法,只有当父接口中定义的变量使用时,父接口才会初始化。
另外,接口的实现类初始化时也一样不会执行接口的<client>()方法。
5)虚拟机保证一个类的<client>()方法在多线程的情况下正确的加锁,同步。如果多个线程初始化一个类,只会有一个线程去执行
类的<client>()方法,其他线程都会阻塞等待。