初步了解Java的类加载机制
Java运行时一个类是什么时候开始被加载的?
《java虚拟机规范》中并没有进行强制约束,交给了虚拟机自己去自由实现,HotSpot虚拟机是按需加载,在需要用到该类的时候加载这个类
1.Sun公司最早的Classic虚拟机
2.Sun/Oracle公司的HotSpot虚拟机
3.BEA公司的JRockit虚拟机
4.IBM公司的IBM J9虚拟机
https://docs.oracle.com/javase/8/docs/
JVM一个类的加载过程?
一个类从加载到jvm内存,到从jvm内存卸载,它的整个生命周期会经历7个阶段:
1、加载( Loading)
2、验证( Verification)
3、准备( Preparation)
4、解析( Resolution)
5、初始化( Initialization)
6、使用( Using)
7、卸载( Unloading)
其中验证、准备、解析三个阶段统称为链接( Linking)
类加载生命周期
加载: classpath、jar包、网络、某个磁盘位置下的类的dass二进制字节流读进来,在内存中生成一个代表这个类的 java.lang.Class对象放入堆上,class字节码二进制数据是放在元空间上,此阶段我们程序员可以干预,我们可以自定义类加载器来实现类的加载
-----------------------------------链接-------------------------------------
验证: 验证 Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求,保证虚拟机的安全;
准备: 类变量赋默认初始值,int为0,long为0L, boolean为 false,引用类型为null,常量赋正式值
解析: 把符号引用翻译为直接引用
----------------------------------------------------------------------------
初始化: 当我们new一个类的对象,访问一个类的静态属性,修改一个类的静态属性,调用一个类的静态方法,用反射API对一个类进行调用,初始化当前类,其父类也会被初始化.那么这些都会触发类的初始化;
使用: 使用这个类
卸载:(其实很少会卸载类)
1.该类所有的实例都已经被GC,也就是JVM中不存在该Class的任何实例
2.加载该类的Classloader已经被GC;
3.该类的 java.lang.Class对象没有在任何地方被引用,如不能在任何地方通过反射访问该类的方法;
public class Test {
// 常量,准备阶段 a 就赋值为123
public static final int a = 123;
// 静态类变量,准备阶段时b赋初始值为0 ,初始化阶段时赋值为234
public static int b = 234;
// 实例变量 ,准备阶段不处理,创建对象时赋值
public int c = 789;
//静态初始化块 初始化阶段执行
static {
System.out.println("=============静态初始化块===============");
System.out.println("现阶段输出各种变量为: " + a + "," + b);
System.out.println("======================================");
}
//初始化块 创建对象的时候执行
{
System.out.println("---------初始化块-----------------------");
System.out.println("现阶段输出各种变量为: " + a + "," + b + "," + c);
System.out.println("---------------------------------------");
}
//构造器 创建对象的时候执行
public Test(){
System.out.println("+++++++++++++构造器(构造函数)+++++++++++++");
System.out.println("现阶段输出各种变量为: " + a + "," + b + "," + c);
System.out.println("+++++++++++++++++++++++++++++++++++++++++");
}
public static void main(String[] args) {
// -XX:+TraceClassLoading (vm参数,用于监控类加载)
new Test();
}
}
一个类被初始化的过程
类的初始化阶段,Java虚拟机才真正开始执行类中编写的Java程序代码;进行准备阶段时,变量已经赋过一次系统要求的初始零值,而在初始化阶段,才真正初始化类变量和其他资源( 准备阶段:赋予变量初始化值 --> 初始化阶段:赋予变量真正的值)
继承时父子类的初始化顺序是怎样的?
案例代码:
public class ParentClass {
//静态变量
public static String p_StaticField = "父类--静态变量";
//变量
public String p_Field = "父类--变量";
//静态初始化快
static {
System.out.println(p_StaticField);
System.out.println("父类--静态初始化块");
}
//初始化块
{
System.out.println(p_Field);
System.out.println("父类-初始化块");
}
public ParentClass() {
System.out.println("父类--构造器");
}
public static void main(String[] args) {
//new ParentClass();
}
}
//-------------------------------------------------------------------------------------------------------
public class SonClass extends ParentClass {
//静态变量
public static String s_StaticField = "子类--静态变量";
//变量
public String s_Field = "子类--变量";
//静态初始化快
static {
System.out.println(s_StaticField);
System.out.println("子类--静态初始化块");
}
//初始化块
{
System.out.println(s_Field);
System.out.println("子类--初始化块");
}
public SonClass() {
System.out.println("子类--构造器");
}
public static void main(String[] args) {
//new ParentClass();
/*
父类--静态变量
父类--静态初始化块
子类--静态变量
子类--静态初始化块
父类--变量
父类-初始化块
父类--构造器
*/
new SonClass();
/*
父类--静态变量
父类--静态初始化块
子类--静态变量
子类--静态初始化块
父类--变量
父类-初始化块
父类--构造器
子类--变量
子类--初始化块
子类--构造器
*/
}
}
根据代码结果可知:
先加载父类静态变量以及静态初始块,然后是子类静态变量以及静态初始块,然后根据具体情况先加载父类变量以及初始化块,再加载子类变量以及初始化块
究竟什么是类加载器?
在类加载阶段,通过—个类的全限定名来获取描述该类的二进制字节流的这个动作的“代码”被称为“类加载器”( Class loader),这个动作是可以自定义实现的; 所以 类加载器其实也是程序代码
JVM有哪些类加载器?
站在Java虚拟机的角度来看,只存在两种不同的类加载器
1、启动类加载器( Bootstrap Classloader),使用C++语言实现,是虚拟机自身的部分
2、其他所有的类加载器,由Java语言实现,独立存在于虚拟机外部,并且大部分都继承自抽象类 java.lang.Classloader(父委派模式是通过组合来实现的)
站在Java开发者的角度来看,自JDK1.2开始,Java一直保持着三层类加载器架构;
JVM中不同的类加载器加载哪些文件?
1、启动类加载器( Bootstrap ClassLoader)---------根的类加载器
<JAVA_HOME>\jre\lib目录下的 rt.jar 、resources.jar、 charsets. jar 被-Xbootclasspath参数所指定的路径中存放的类库
public class ClassLoader01 {
public static void main(String[] args) {
// 都是jre\lib目录下类,但是并不是所有类都通过BootstrapClassLoader加载
System.out.println(HKSCS.class.getClassLoader());//BootstrapClassLoader->null
System.out.println(BufferedInputStream.class.getClassLoader());//BootstrapClassLoader->null
System.out.println(DefaultHandler.class.getClassLoader());//BootstrapClassLoader->null
System.out.println(AppInitEvent.class.getClassLoader());//AppClassLoader
/* (注意:底层是由c++写的,通过BootstrapClassLoader加载 结果为null)
null
null
null
sun.misc.Launcher$AppClassLoader@18b4aac2
*/
}
}
2、扩展类加载器( Extension ClassLoader)
sun. misc. Launcher$ExtClassLoader
<JAVA_HOME>\jre\lib\ext , 被java.ext.dirs系统变量所指定的路径中所有的类库
3、应用程序类加载器( Application ClassLoader)---------系统的类加载器
sun. misc. Launcher$AppClassloader ,加载用户类路径( ClassPath)上所有的类库;