java中的【类加载机制】

在开始之前首先要明白一个问题:
能否自己写一个String类?
答案:
如果包名相同,就无法加载到自定义的类上面,因为根据类的双亲委派机制,会去加载父类,父类发现冲突了String类,就不再加载,但是如果包名不相同就可以重写。

com.soft;
public class String {
    public static void main(String[] args) {
        System.out.println(String.class.getClassLoader());
        java.lang.String str="hi";
        System.out.println(str.getClass().getClassLoader());
    }
}

在加载类的时候,能否对类的字节码进行修改?
可以修改。
使用Java探针技术,利用javaAgent和ASM字节码技术开发java探针工具,实现原理如下:
jdk1.5以后引入了javaAgent技术,javaAgent是运行方法之前的拦截器。利用javaAgent和ASM字节码技术,
在JVM加载class二进制文件的时候,利用ASM动态的修改加载的class文件,在监控的方法前后添加计时器功
能,用于计算监控方法耗时,同时将方法耗时及内部调用情况放入处理器,处理器利用栈先进后出的特点对方法调用先后顺序做处理,当一个请求处理结束后,将耗时方法轨迹和入参map输出到文件中,然后根据map中相应参数或耗时方法轨迹中的关键代码区分出我们要抓取的耗时业务。最后将相应耗时轨迹文件取下来,转化为xml格式并进行解析,通过浏览器将代码分层结构展示出来,方便耗时分析。
Java探针工具功能点:
1、支持方法执行耗时范围抓取设置,根据耗时范围抓取系统运行时出现在设置耗时范围的代码运行轨迹。
2、支持抓取特定的代码配置,方便对配置的特定方法进行抓取,过滤出关系的代码执行耗时情况。
3、支持APP层入口方法过滤,配置入口运行前的方法进行监控,相当于监控特有的方法耗时,进行方法专题分析。
4、支持入口方法参数输出功能,方便跟踪耗时高的时候对应的入参数。
5、提供WEB页面展示接口耗时展示、代码调用关系图展示、方法耗时百分比展示、可疑方法凸显功能。

类加载机制原理

什么是类加载机制?

JVM 的类加载机制是指:JVM 把描述类的数据从 .class 文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型,这就是 JVM 的类加载机制。
类加载的生命周期 类的生命周期总共分为7个阶段:加载、验证、准备、解析、初始化、使用和卸载。
其中验证、准备、解析三个步骤又可统称为连接。 加载、验证、准备、初始化和卸载五个步骤的顺序都是确定的,解析阶段在某些情况下有可能发生在初始化之后,这是为了支持 Java 语言的运行期绑定的特性。 在
JVM 虚拟机规范中并没有规定加载的时机,但是却规定了初始化的时机,而加载、验证、准备三个步骤
是在初始化之前。
在这里插入图片描述
类加载器 一般分为启动类加载器(Bootstrap ClassLoader),扩展类加载器(Extension
ClassLoader),应用程序类加载器(Application ClassLoader)
JVM的类加载机制主要有如下三种机制:
全盘负责, 当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显式使用另外一个类加载器来载入。
父类委托, 先让parent(父)类加载器视图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。(类加载器之间的父子关系并不是类继承上的父子关系,是类加载器实例之间的关系)
缓存机制, 保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区中搜寻该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区总。这就是为什么修改Class后,必须重启JVM,程序所做的修改才会生效的原因。
在这里插入图片描述

类的加载时机

隐式加载 new 创建类的实例,
显式加载:loaderClass,forName等
访问类的静态变量,或者为静态变量赋值
调用类的静态方法
使用反射方式创建某个类或者接口对象的Class对象。
初始化某个类的子类
直接使用 java.exe 命令来运行某个主类

类加载的过程

我们编写的 java 文件都是保存着业务逻辑代码。 java 编译器将 .java 文件编译成扩展名为 .class 的件。.class 文件中保存着java转换后,虚拟机将要执行的指令。当需要某个类的时候,java虚拟机会加载 .class 文件,并创建对应的class对象,将class文件加载到虚拟机的内存,这个过程被称为类的加载。
加载:类加载过程的一个阶段,ClassLoader通过一个类的完全限定名查找此类字节码文件,并利用字节码文件创建一个class对象。
验证:目的在于确保class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身的安全,
主要包括四种验证:
文件格式的验证,元数据的验证,字节码验证,符号引用验证。
准备:为类变量(static修饰的字段变量)分配内存并且设置该类变量的初始值,(如static int i = 5 这
里只是将 i 赋值为0,在初始化的阶段再把 i 赋值为5),这里不包含final修饰的static ,因为final在编译的时候就已经分配了。这里不会为实例变量分配初始化,类变量会分配在方法区中,实例变量会随着对象分配到Java堆中。
解析:这里主要的任务是把常量池中的符号引用替换成直接引用
初始化:这里是类加载的最后阶段,如果该类具有父类就进行对父类进行初始化,执行其静态初始化器
(静态代码块)和静态初始化成员变量。(前面已经对static 初始化了默认值,这里我们对它进行赋值,成员变量也将被初始化)
forName和loaderClass区别
Class.forName()得到的class是已经初始化完成的。
Classloader.loaderClass得到的class是还没有链接(验证,准备,解析三个过程被称为链接)的。

类与类加载器

在JVM中标识两个Class对象,是否是同一个对象存在的两个必要条件:
类的完整类名必须一致,包括包名。
加载这个ClassLoader(指ClassLoader实例对象)必须相同。

双亲委派机制

又叫双亲委托机制。
在这里插入图片描述
上图所示的这种关系我们就称之类加载器的双亲委派模型。在双亲委派模型中,除了顶层的 Bootstrap
ClassLoader 之外,其他的类加载器都有自己的父加载器。
双亲委派模型的工作流程
如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,只有当父类加载器无法完成这个类加载请求时,才会让子类加载器去处理这个请求。
双亲委派模式要求除了顶层的启动类加载器之外,其余的类加载器都应该有自己的父类加载器,但是在
双亲委派模式中父子关系采取的并不是继承的关系,而是采用组合关系来复用父类加载器的相关代码

工作原理

如果一个类收到了类加载的请求,它并不会自己先去加载,而是把这个请求委托给父类加载器去执行,如果父类加载器还存在父类加载器,则进一步向上委托,依次递归,请求最后到达顶层的启动类加载器,如果弗雷
能够完成类的加载任务,就会成功返回,倘若父类加载器无法完成任务,子类加载器才会尝试自己去加载,这就是双亲委派模式。
可以记为:每个儿子都很懒,遇到类加载的活都给它爸爸干,直到爸爸说我也做不来的时候,儿子才会想办法自己去加载。

优势

采用双亲委派模式的好处就是Java类随着它的类加载器一起具备一种带有优先级的层次关系,通过这种
层级关系可以避免类的重复加载,当父亲已经加载了该类的时候,就没有必要子类加载器(ClassLoader)再加载一次。其次是考虑到安全因素,Java核心API中定义类型不会被随意替换,假设通过网路传递一个名为
java.lang.Integer的类,通过双亲委派的的模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字类,发现该类已经被加载,并不会重新加载网络传递过来的java.lang.Integer.而之后返回已经加载过的Integer.class,这样便可以防止核心API库被随意篡改。可能你会想,如果我们在classpath路径下自定义一个名为java.lang.SingInteger?
该类并不存在java.lang中,经过双亲委托模式,传递到启动类加载器中,由于父类加载器路径下并没有该类,所以不会加载,将反向委托给子类加载器,最终会通过系统类加载器加载该类,但是这样做是不允许的,因
为java.lang是核心的API包,需要访问权限,强制加载将会报出如下异常:

Java.lang.SecurityException:Prohibited package name: java.lang

总结

简述原理:例如自定义的一个类,使用user classLoader,会去使用user classLoader的父类加载
器(Application classLoader),如果父类中有这个类,则不能加载,说明和父类类加载器冲突;如果
父类加载器还有父类,则继续尝试类加载,Extension classLoader(扩展类加载器),再去顶级Bootstrap
classLoader(启动类加载器),依次进行尝试匹配,直到使用子类加载器进行处理。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值