java中class文件如何加载的_jvm如何加载class文件

编译期:

javac是JDK自带的编译器, 可以将java文件编译为class字节码文件,

javap是JDK自带的反编译器,将.class字节码反编译为.java文件,javap -help是javap常用指令,javap -c XXX.class可以反编译字节码文件,

还会输出一系列虚拟机指令,这些指令就是java虚拟机指令。

5e335546afa86718051de55642c58b2e.png

33cd1d3f92a94b96314bcb6d2a83a4fc.png

为什么不直接生成机器码去执行?

1.每次生成机器码都需要做大量准备,而不同系统需要生成不同的机器码。生成.class这样的中间码,可以交给虚拟机到处执行,不关心是什么机器语言。

2.也可以使用其他语言,来生成.class文件,由java虚拟机去执行,兼容性更好。

java虚拟机组成

1912b7288a01d7186a0a3b2af326eb89.png

ClassLoader:根据特定格式,加载class文件到内存。

Execution Engine:命令解释器,对加载的命令进行解析。

Native Interface:融合不同开发语言的原生库为java所用。在jdk中,可以存放一些其他语言编写的类库,java的一些原生方法中也会调用native修饰的方法,就是通过native interface,去调用这些其他语言类库的接口。

Runtime Data Area :JVM内存空间结构模型。

ClassLoader

classloader的作用:

主要工作在class装载的加载阶段,其主要作用是从系统外部获得class二进制数据流。它是java的核心组件,所有的class都由classloader进行加载,classloader负责通过将class文件里的二进制数据流装载进系统,然后交给java虚拟机进行连接、初始化等操作。

classloader的种类:

BootStrapClassLoader:C++编写,加载核心库java.*,必须看JVM代码才能看的到

ExtClassLoader:java编写,加载扩展库javax.*

AppClassLoader:java编写,加载程序所在目录,加载class.path路径下的所有class,而这些class又是由avac编译好的

自定义ClassLoader:java编写,定制化加载。

自定义ClassLoader的实现:

关键函数:

// 寻找根据名称和path,寻找class文件,并读取文件为二进制数据流

protected Class> findClass(String name) throws ClassNotFoundException{

throw new ClassNotFoundException(name);

}

// 处理二进制数据流,并返回class对象

protected final Class> defineClass(byte[] b, int off, int len)throw ClassFormatError{

return defineClass(null, b , off, len, null);

}

public Class MyClassLoader extends ClassLoader{

private String path;

private String classLoaderName;

public MyClassLoader (String path, String classLoaderName){

this.path = path;

this.classLoaderName = classLoaderName;

}

@Override

public Class findClass(String name){

byte[] b = loaderClassData(name);

return defineClass(name, b, 0, b.length);

}

private byte[] loaderClassData(String name){

name = path + name + ".class";

InputStream in = null;

ByteArrayOutputStream out = null;

try{

in = new FileInputStream(new File(name));

out = new ByteArrayOutputStream();

int i = 0;

while((i = in.read()) != -1){

out.write(i);

}

}catch(Exception e){

e.printStackTrace();

}finally{

out.close();

in.cloase();

}

}

}

类加载器双亲委派机制

0f85b516eff780dcbc9bb2a4d88b2677.png

1.BootStrapClassLoader,是基于C++写的,它的主要函数findBootStrapClassOrNull(name)方法是一个native方法,调用C++库去执行的。

2.ClassLoader类中的LoadClass方法的代码,会被Synchronized关键字修饰,防止多个加载器同时加载同一个类,这也是为什么类加载是单例的,因为加了同步锁。

3.ClassLoader类中有一个成员变量 private ClassLoader parent;该变量的意思就是,当此ClassLoader为CustomerClassLoader时,它的parent就是App ClassLoader;以此类推,AppClassLoader的parent就是ExtClassLoader等等。当当前加载器,加载不到类时,会调用parent的类加载器去加载。

e042ec4b86593c20c2c96eadc529fef5.png

为什么要使用双亲委派机制去加载类

因为它能防止多份同样的字节码文件加载,如果不用委派,而是每个加载器自己加载自己的,同样的字节码文件可能会加载多次;而是用双亲委派,就会从自定义加载器或者app加载器向上查找,看哪个加载器是否已经加载过这个字节码文件就好。

类的加载方式

隐式加载:new,调用构造器方法,支持传入参数,给对象赋值。

显示加载:loadClass,forName等,class.newInstance(),不支持传入参数,需要通过反射,给对象赋值。

loadClass和forName的区别

都能在运行时,知道一个类的属性和方法,对于任意一个类的对象,都能调用类的方法和属性。

类的装载过程

1.加载 通过classLoader加载class文件字节码,生成class对象

2.链接:校验:检查加载的class正确性和安全性

准备:为类变量(static)分配存储空间并设置类变量初始值(类型的默认值)

解析:JVM将常量池内的符号引用转换为直接引用

3.初始化:执行类变量赋值和静态代码块

ForName的特点

Class.forName("");会执行类的静态代码块代码,会对类进行初始化。

我们在获取JDBC连接的时候:Class.forName("com.mysql.jdbc.Driver")时,Driver的静态代码块里,就初始化了数据库连接

54a51ef088cf4b753e129d7e208fc567.png

loadClass的特点

spring ioc因为大量使用了懒加载,所以spring ioc托管的类,会使用loadClass方法来加载,会生成class文件,但是不会初始化值。把类的初始化工作,留到实际使用到这个类的时候才去做。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值