类加载机制

一、类的加载、连接和初始化

1、JVM的启动和终止

当调用Java命令调用某个Java程序时,该命令将会启动一个JVM
同一个JVM的所有线程、所有变量都处于同一个进程中,他们都是使用该JVM进程的内存区。
当系统出现以下几种情况时,JVM进程将被终止:

  • 程序运行到最后正常结束
  • 程序运行到使用System.exit()或Runtime.getRuntime().exit()代码处结束程序
  • 程序执行过程中遇到未捕获的异常或错误而结束

当程序主动使用某个类时,如果该类还未加载到内存中,则系统会通过加载、连接和初始化3个步骤对该类进行初始化

2、类的加载

类加载指的是将类的class文件读入内存,并为此创建一个java.lang.Class对象;
类加载由类加载器完成,类加载器通常由JVM提供称为系统类加载器;
开发者可以通过继承ClassLoader基类来创建自己的类加载器;
通过使用不同的类加载器,可以从不同的来源加载类的二进制数据,通常有如下几种来源:

  • 从本地文件系统加载class文件,这是前面绝大部分示例程序的类加载方式
  • 从JAR包加载class文件,这种方式也是很常见的,JDBC编程时用到的数据库驱动类就放在JAR文件中,JVM可以从JAR文件中直接加载该class文件
  • 通过网络加载class文件
  • 把一个Java源文件动态编译,并执行加载

Java虚拟机允许系统预先加载某些类,也可以等到首次使用时才加入该类

3、类的连接

当类被加载之后,系统为之生成一个对应的class对象,接着会进入连接阶段;

连接阶段负责把类的二进制数据合并到JRE中;

类的连接又可分为如下三个阶段:

  • (1)验证:验证阶段用于检验被加载的类是否有正确的内部结构
  • (2)准备(静态域默认值):类准备阶段则负责为类的静态Field分配内存,并设置默认初始值
  • (3)解析(符号引用-直接引用):将类的二进制数据中的符号引用替换成直接引用

4、类的初始化

4.1目的

主要是对静态Field进行初始化

4.2方式
  • 声明静态Field时指定初始值
  • 使用静态初始化块指定初始值
    JVM会按这些语句在程序中的执行顺序依次执行他们
4.3初始化步骤
  • (1)假如这个类还没有加载和连接,则程序先加载并连接该类
  • (2)假如该类的直接父类还没有被初始化,则先初始化其直接父类
  • (3)假如类中有初始化语句,则系统依次执行这些初始化语句

4.6初始化时机

当Java程序首次通过下面6种方式来使用某个类或接口时,系统就会初始化该类或接口:

  • 创建类的实例。为某个类创建实例的方式包括:使用new操作符来创建实例,通过反射来创建实例,通过反序列化的方式来创建实例
  • 调用某个类的静态方法
  • 访问某个类或接口的静态Field或为该静态Field赋值
  • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。例如代码:Class.forName(“Person”),如果系统还未初始化Person类,则这行代码将会导致该Person类被初始化,并返回Person类对应的java.lang.Class对象。
  • 初始化某个类的子类。当初始化某个类的子类时,该子类的所有父类都会被初始化。
  • 直接使用java.exe命令来运行某个主类。当运行某个主类时,程序会先初始化该主类
    注意

以下情况不会触发初始化:

  • 当某个静态Field使用了final修饰,而且它的值在编译时就可以确定下来,那么程序其他地方使用该静态Field时,相当于使用常量,静态Field所在的类也不会初始化
  • 当使用ClassLoader类的loadClass()方法来加载某个类时,该方法只是加载该类,并不会执行该类的初始化

二、类加载器

1、类加载器简介

类加载器负责加载所有的类,系统为所有被载入内存中的类生成一个java.lang.Class实例
一旦一个类被载入JVM中,同一个类就不会被再次载入了;
在JVM中,一个类用其全限定名和其类加载器作为其唯一标识(类名,包名,类加载器实例),如(Person,pg,k1);

2、加载器层次结构

2.1、Bootstrap:根(引导或原始)类加载器

负责加载Java的核心类

  • file:/D:/Java/jdk1.7.0/jre/lib/resources.jar
  • file:/D:/Java/jdk1.7.0/jre/lib/rt.jar
  • file:/D:/Java/jdk1.7.0/jre/lib/sunrsasign.jar
  • file:/D:/Java/jdk1.7.0/jre/lib/jsse.jar
  • file:/D:/Java/jdk1.7.0/jre/lib/jce.jar
  • file:/D:/Java/jdk1.7.0/jre/lib/charsets.jar
  • file:/D:/Java/jdk1.7.0/jre/lib/classes.jar

指定加载附加类

  • java.exe -Xbootclasspath
  • java.exe -D(sun.boot.class.path)

根类加载器不是ClassLoader的子类
获取根类加载器所加载的全部URL数组

URL[] urls=aun.misc.Launcher.getBootstrapClassPath().getURLs();
for(int i=0;i<urls.length;i++){
	System.out.println(urls[i].toExternalForm());
}
2.2Extension Classloader:扩展类加载器

负责加载JRE的扩展目录

  • ./jre/ib/ext
  • java.ext.dirs
2.3 System ClassLoader:系统(应用)类加载器

负责加载以下命令或属性指定的

  • java -classpath
  • java.class.path
  • CLASSPATH

获取系统类加载器

  • ClassLoader.getSystemLoader

3、类加载机制

3.1、三种类加载机制
  • 全盘负责
    所谓全盘负责,就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入。
  • 父类委托
    所谓父类委托,则是先让parent(父)类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。
  • 缓存机制
    缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区中搜寻该class,只有当缓存区中不存在该class对象时,系统才会读取该类对应的二进制数据,并将其转换成class对象,存入缓存区中。这就是为什么修改了class后,必须重新启动JVM,程序所做的修改才会生效的原因。
3.2父子关系
Created with Raphaël 2.2.0 用户类加载器 系统类加载器 扩展类加载器 根类加载器
3.3实现类

系统类加载器是AppClassLoader的实例,扩展类加载器是ExtClassLoader的实例。实际上,这两个类都是URLClassLoader类的实例。
JVM的根类加载器并不是Java实现的,而且由于程序通常无需访问访问根类加载器,因此访问扩展类加载器的父类加载器时返回null。

3.4 ClassLoader的一些方法
  • loadClass(String name,boolean resolve):该方法为ClassLoader的入口点,根据指定的二进制名称来加载类,系统就是调用ClassLoader的该方法来获取指定类对应的Class对象。
  • findClass(String name):根据二进制名称来查找类。
  • findSystemClass(String name):从本地文件系统装入文件,它在本地文件系统中寻找类文件,如果存在,就使用defineClass()方法将原始字节转换成Class对象,以将该文件转换成类。
  • static getSystemClassLoader():这是一个静态方法,用于返回系统类加载器。
  • getParent():获取该类加载器的父类加载器。
  • resolveClass(Class<?> e):链接指定的类,类加载器可以使用此方法来链接类。
  • findLoadedClasss(String name):如果此Java虚拟机已加载了名为name的类,则直接返回该类对应的Class实例,否则返回null,该方法是Java类加载缓存机制的体现。
  • defineClass(String name,byte[] b,int off,int len):该方法负责将指定类的字节码文件读入字节数组byte[] b内,并把它转换成Class对象,该字节码文件可以来源于文件,网络等。该方法管理JVM的许多复杂的实现,它负责将字节码分析成运行时数据结构,并校验有效性等,该方法是final型。
3.5 创建并使用自定义的类加载器
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值