谈一谈Java类加载相关的方方面面

大家好,这里是IT技术百货,专注于有价值的IT技术知识分享;

今天跟大家分享Java类加载的相关内容

什么是类加载器

类加载器就是将类的描述加载到虚拟机内存的这样一个模块;典型的类的描述就是java源码编译后的class文件,也可以是其他任何数据形式,比如网络字节流等;

类加载器有哪些

java默认定义了三种类加载器:

  • 启动类加载器(Bootstrap Class-Loade)主要加载jre/lib下面的jar包
  • 扩展类加载器(Extension or Ext Class-Loader)主要加载jre/lib/ext/目录下的jar包;(jre/lib/ext/目录可以通过指定的java.ext.dirs覆盖)
  • 应用类加载器(Application or App Class-Loader),加载 classpath 的内容(classPath是一组目录组合)。

应用类加载器是可以被“覆盖”的,通过-Djava.system.class.loader=com.yourcorp.YourClassLoader 整个参数来覆盖原有的应用类加载器;这里的覆盖加了双引号,实际上自定义的类加载器会称为默认应用类加载器的父亲;

一般在需要改变双亲委派模型的时候会这么做;关于双亲委派模型,看下文;

类加载器的双亲委派模型

什么是双亲委派模型

当类加载器(Class-Loader)试图加载某个类型的时候,除非父加载器找不到相应类型,否则尽量将这个任务代理给当前加载器的父加载器去做。使用委派模型的目的是避免重复加载 Java 类型

为什么采用双亲委派模型

采用双亲委派模型主要出于两点考虑:

  1. 保证类的唯一性,一个类只被加载一次,避免重复加载;(因为在JVM规范里面,同一个class文件被两个相同的类加载,会被视为两个类)
  2. 安全性,保证java核心API不被替换;也就是启动类加载器加载过的类不会被其他类加载器覆盖;

有哪些打破了双亲委派模型,为什么打破

  • JNDI服务
  • Tomcat服务器
  • OSGI实现热更新

以上场景打破双亲委派模型的原因是父类没有能力加载或者完成所需的一些逻辑,为了整体架构设计更加优雅、灵活,便交由子类加载器来进行加载;

类加载的过程

类加载主要分为三个过程,加载、链接、初始化;

加载:Java 将字节码数据从不同的数据源读取到 JVM 中,并映射为 JVM 认可的数据结构(Class 对象)

链接:这是核心的步骤,简单说是把原始的类定义信息平滑地转化入 JVM 运行的过程中。具体又细分为验证、准备、解析三个过程;

  • 验证:验证Class对象是否符合虚拟机规范
  • 准备:创建静态变量并准备内存空间
  • 解析:将常量池中的符号引用修改为直接引用

初始化:执行类的初始化逻辑,包括静态字段赋值,静态块的初始化等;父类型的初始化优先于当前类型的。

自定义类加载器有哪些应用场景

  • 修改类的字节码逻辑,比如:为外部类统一织入通用逻辑;
  • 根据用户需求,动态的创建类;比如:JDBC的驱动类,不同数据库需要使用不同的驱动类
  • 包名+类名有冲突的时候,一个类加载器不能同时加载,可以用不同的类加载器加载到内存中;
  • 热更新特性,通过自定义类加载器,实现在运行过程中动态的加载、卸载类

自定义类加载器的实现

主要核心在于获取类的字节码阶段,对于将字节码映射为Class对象,这部分一般不会涉及;

<code>

public

class

CustomClassLoader

extends

ClassLoader

{

public

Class

findClass

(String name)

throws

ClassNotFoundException

{

byte

[] b = loadClassFromFile(name);

return

defineClass(name, b,

0

, b.length); }

private

byte

[] loadClassFromFile(String fileName) { InputStream inputStream = getClass().getClassLoader().getResourceAsStream( fileName.replace(

'.'

, File.separatorChar) +

".class"

);

byte

[] buffer; ByteArrayOutputStream byteStream =

new

ByteArrayOutputStream();

int

nextValue =

0

;

try

{

while

( (nextValue = inputStream.read()) != -

1

) { byteStream.write(nextValue); } }

catch

(IOException e) { e.printStackTrace(); } buffer = byteStream.toByteArray();

return

buffer; } }/<code>

小结

今天跟大家分享了java类加载器的以下知识:

  1. 什么是类加载器
  2. 类加载器具体有哪些
  3. 类加载过程中的双亲委派模型
  4. 类加载的具体过程
  5. 自定义类加载器的应用场景
  6. 类加载器的实现

感谢浏览阅读,如果觉得内容有价值欢迎点赞,转发;喜欢请关注“IT技术百货”


分享到:


相關文章: