java类加载

java类加载的时机

关于类什么时候被加载,Java虚拟机规范并没有做出硬性的规定,不同JVM可能有不同的实现。一般分为下面两种情况:

  1. 饿汉式(eagerly load):只要有其他类引用了它就加载(大概是指Test a;这种情况下就加载)
  2. 懒汉式(lazy load):类初始化的时候才加载

java初始化时机

  1. 创建类的实例
  2. 访问某个类的静态方法或静态变量,给静态变量赋值
  3. 反射==(ClassLoader.loadClass()仅仅是将类加载到内存中,不会进行初始化)==
  4. 初始化一个类的子类
  5. java虚拟机启动时被标记为启动类的类

关于类加载器

  1. 系统默认自带3个类加载器BootStrap、Extension、System(Application)。三个类加载器之间为父子关系(并非java中的父类子类关系)。
  2. Bootstrap为根加载器(由C/C++实现),加载jvm核心库(java.lang.*),是Extension加载器的父加载器;
  3. Extension为扩展加载器(由纯java实现),加载路径为jre/lib/ 和jre/lib/ext/下的类,是System的父加载器;
  4. System为系统加载器(由纯java实现),加载路径为环境变量中classpath中所包含的路径,是自定义加载器的默认父加载器;

类加载过程

加载

  1. 通过类的全限定名来获取类的二进制字节流(用户可操作,自定义类加载器(实现通过一个类的全限定名获取类的二进制字节流的动作放在jvm外部实现的模块))
  2. 将这个字节流所代表的静态存储结构转化为在方法区的运行时数据结构。
  3. 在内存中生成代表这个类的Java.lang.class类对象,作为这个数据访问的入口。

连接

连接分为三个部分:验证、准备、解析

验证

主要有四个方面的验证:

  1. 文件格式验证,是否以魔数开始,版本信息是否为jvm接受,常量池中是否有不支持的类型。
  2. 元数据验证:进行语法分析,是否每个类都有父类,是否有语法错误,是否继承自final。
  3. 字节码验证:语义分析,通过数据流和控制流分析,确保语义是合法的。
  4. 符号引用验证:是否能找到对应的类。发生在将符号引用转化为直接引用时,在解析中产生。

准备

正式为静态变量分配内存并赋默认值(基本数据类型为0,引用数据类型默认值为null),在方法区中分配
如果Final Static变量为常量访问该变量不会引起类的初始化,否则类会初始化

解析

将类中的符号引用转化为直接引用

初始化

为类的静态变量赋值,包括定义时赋值和静态代码块赋值;按顺序从上到下依次执行。

自定义类加载器

自定义加载器只需要继承ClassLoader这个抽象类重写FindClass方法。自定义加载器不传参数默认父加载器是System加载器,如果传入一个加载器则该加载器就是它的父加载器。所以父子加载器关系应该是组合关系,父子加载器有可能是父类和子类的关系有可能不是。

命名空间

每个类加载器都有自己的命名空间,命名空间由类加载器和它所有的父加载器所加载的类组成,在同一个命名空间中不会出现类的完整名字相同(包括包名)的两个类;不在同一个命名空间中有可能出现类的完整名字相同(包括包名)的类;不同命名空间的类相互不可见,所以同一个类可以被不同加载器加载多次,只要这些类加载器没有直接和间接的父子关系即可。子加载器加载的类的命名空间包含所有父加载器所加载的类的命名空间,因此由子加载器加载的类能够看见父加载器加载的类。由父加载器加载的类不能看见由子加载器加载的类,如果两个加载器之间没有直接或间接的父子关系,那么由这两个加载器加载的类相互不可见。

运行时包

由同一个加载器加载的相同包的类组成了运行时包,决定两个类是否是属于同一个运行时包,不仅要看包名是否相同,还要看加载该类的加载器是否是同一个类加载器。只有属于同一运行时包的类才能彼此访问包内默认权限的内容。例如java.lang.Object类是由Bootstrap类加载器加载的,但是你也可以自己定义一个java.lang包,里面写一个类GetObjectProtected,一般情况下java.lang.GetObjectProtected这个类可以由System类加载器成功加载。如果没有这个运行时包这个限制,jvm就会认为GetObjectProtected类和系统中的java.lang包中Object类是属于同一个包,那么GetObjectProtected就可以访问Object类中的所有包访问权限的属性和调用Object中protected方法。而这些包权限的属性和方法是只希望被系统java.lang包内的方法访问,这样的话就有可能会危害到jvm的安全,所以就用类运行时包来进行限制。

双亲委托机制

当一个类加载器需要加载一个类的时候,它首先回去找自己的父加载器,把任务委托给父加载器,父加载器同样也会去找自己的父加载器。就这样层层委托直到BootStrap加载器位置,然后BootStrap加载器发现自己没有父加载器(太可怜了)自能自己干活了,然后就到自己对应的路径中寻找对应的字节码文件,如果找到了就把它加载进内存,然后返回对应的Class类对象的引用。如果找不到那就只能告诉那个把任务委托给自己的不孝子,我干不了,你自己玩去吧。然后任务又按照原来的委托路径反向开始传达,如果一直传到这个任务的发起者,还没有加载进来的话,那么就只能是发起委托的加载在自己对应的路径进行寻找并加载,如果发起任务者也没能加载,就会抛出classNotFound异常。其中成功加载类的加载器叫该类做定义类加载器,定义类加载器和定义类加载器到任务发起加载器之间的所有加载器都叫该类的初始类加载器(定义类加载器会得到该类的Class类对象,初始类加载器会传递这个引用)。

  • 0
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值