栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

class文件加载从java到C++的初步研究

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

class文件加载从java到C++的初步研究

目录

测试类自定义加载类解析defineClass类加载器的C++实现

在学习反射时,实现了自定义ClassLoader类加载器,我们都知道点击运行后.java代码文件在编译成.class字节码文件后,jvm自动加载.class文件,而在自定义类加载器后,不再是jvm自动加载,而是手动选择加载编译后的.class文件,此文是研究class文件从加载到运行中的具体过程.

本人在学习中参考的是B站up主 青空の霞光 的视频,讲的挺好,反射不明白的可以去看看,吐槽一句,人与人之间是不能比较的,在看到自定义classLoader类加载器时,up主一句"这是我高中研究出的一个玩意"把我惊到了.


测试类
ReflectTest.java是一个可以随意编写的测试类
这里是我自己写的
package test;

public class ReflectTest {
    public String name;
    public int age;
    private String id;
    public String password;
    public ReflectTest(){}
    public ReflectTest(String name){
        this.name = name;
    }
    private ReflectTest(int age){
        this.age = age;
    }
    public ReflectTest(String name,int age){
        this.name = name;
        this.age = age;
    }


    public void say() {
        System.out.println(name+"12345");
    }
    public void sayAge(){
        System.out.println("age:"+age);
    }
    public void saySome(String word){
        System.out.println("this is myword:"+word);
    }
    private void sayHello(String name){
        System.out.println("Hello "+name);
    }

    private void sayHellos(String... names){
        for (String item:names
             ) {
            System.out.println("Hello"+item);
        }
    }

    public void sayId(){
        System.out.println("id:"+id);
    }
}

第一步:执行命令javac .srcTestStudent.java 得到Student.class字节码文件



然后会多出一个.class文件,如下图




我们把ReflectTest的源文件删除




自定义加载类

下面是我们加载ReflectTest的测试类,其中自定义类加载器放在Test12类中

package reflect;

import java.io.FileInputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

//自定义加载器加载class文件,可运行时加载
public class Test12 {

    public static void main(String[] args) throws Exception {
        MyClassLoader myClassLoader = new MyClassLoader();

        FileInputStream stream = new FileInputStream("src\Test\ReflectTest.class");//填ReflectTest.class的地址
        byte[] bytes = new byte[stream.available()];
        stream.read(bytes);

        Class clazz = myClassLoader.defineClass("test.ReflectTest",bytes);  //必须和我们定义的完整类名一致
        System.out.println(clazz.getName());  //加载成功
        stream.close();

        try {
            Object object = clazz.newInstance();   //用object接收ReflectTest的实例
            Method method = clazz.getMethod("saySome",String.class);
            method.invoke(object,"HuGuang");

            Field field = clazz.getField("age");  //获取属性
            field.set(object,23);       //设置age属性
            Method method1 = clazz.getMethod("sayAge");  //测试ReflectTest类的sayAge方法
            method1.invoke(object);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

	//自定义类加载器
    static class MyClassLoader extends ClassLoader{     //自定义class加载器,因为ClassLoader是抽象类不能被实例化
        public Class defineClass(String className,byte[] classByte){
            return defineClass(className , classByte , 0 , classByte.length);
        }
    }
}

可以看到在ReflcetTest.java文件被删的情况下,这个类仍然被加载运行,ReflectTest的方法测试成功,也就是说ReflectTest.class文件的确被我们的自定义类MyClassLoader加载.


自定义的加载类MyClassLoader中并没有太多内容,关键点是这句
return defineClass(className , classByte , 0 , classByte.length);

defineClass方法是继承父类ClassLoader的,ReflcetTest.class转化成字节流后最后也是传入defineClass方法中,被defineClass方法加载进JVM,所以我们要弄清楚ReflectTest.class如何被添加到JVM就是要弄清defineClass方法.




解析defineClass
下面是ClassLoader类中defineClass方法的源码
protected final Class defineClass(String className, byte[] classRep, int offset, int length) throws ClassFormatError {
	return defineClass(className, classRep, offset, length, null);
}
可以看到参数又被交给另一个重载的defineClass方法处理,跳到这个方法看看
protected final Class defineClass (
		final String className, 
		final byte[] classRep, 
		final int offset, 
		final int length, 
		ProtectionDomain protectionDomain) 
		throws java.lang.ClassFormatError 
{
	return defineClassInternal(className, classRep, offset, length, protectionDomain, false );
}
这个方法也是没啥用,参数又交给defineClassInternal方法处理,跳过去看看
final Class defineClassInternal(
		final String className, 
		final byte[] classRep, 
		final int offset, 
		final int length, 
		ProtectionDomain protectionDomain,
		boolean allowNullProtectionDomain)
		throws java.lang.ClassFormatError 
{
	Certificate[] certs = null; 
	if (protectionDomain != null) {
		final CodeSource cs = protectionDomain.getCodeSource();
		if (cs != null) certs = cs.getCertificates();
	}
	if (className != null) {
		String packageName = checkClassName(className);
		if ((protectionDomain == null) && allowNullProtectionDomain) {
			
		} else {
			checkPackageSigners(packageName, className, certs);
		}
	}

	if (offset < 0 || length < 0 || offset > classRep.length || length > classRep.length - offset) {
		throw new ArrayIndexOutOfBoundsException();
	}

	if ((protectionDomain == null) && !allowNullProtectionDomain) {
		protectionDomain = getDefaultProtectionDomain();
	}
	
	final ProtectionDomain pd = protectionDomain;
	Class answer = defineClassImpl(className, classRep, offset, length, pd);

	if (certs != null) {
		setSigners(answer, certs);
	}
	
	boolean isVerbose = isVerboseImpl();
	URL url = null;
	if (isVerbose) {
		if (pd != null) {
			CodeSource cs = pd.getCodeSource();
			if (cs != null) {
				url = cs.getLocation();
			}
		}
	}

	if (isVerbose) {
		String location = (url != null) ? url.toString() : ""; //$NON-NLS-1$
		com.ibm.oti.vm.VM.dumpString("class load: " + answer.getName() + " from: " + location + "n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
	}
	return answer;
}

看似defineClassInternal方法代码挺多,其实并没有对参数做过多处理,主要是对参数的合法性判断,转化成合适的格式,最后返回一个Class对象answer,长叹一口气,终于返回值中不带调用的方法了,难道到这里就结束了?那ReflectTest.class是什么时候进入JVM的?迷茫


再来看看answer是怎么得到的,defineClassInternal方法中的这句:
Class answer = defineClassImpl(className, classRep, offset, length, pd);

对比下原来自定义加载器MyClassLoader中地这句:
return defineClass(className , classByte , 0 , classByte.length);


发现了什么?!它竟然还在套娃,最初传入的传入的参数,几经轮转,几乎原封不动地又传入到方法defineClassImpl中,好吧,我们继续跳到defineClassImpl看看.

private final native Class defineClassImpl(String className, byte [] classRep, int offset, int length, Object protectionDomain);




defineClassImpl方法还是一层套娃,但到这里就不能再往下点了,因为这里显示是native修饰的方法,是调用其它语言(C/C++)实现,在JDK库中是看不到的.


类加载器的C++实现

那从哪查看源码呢?在网上四处寻找方法,于是下载了openJDK15,里面有虚拟机实现的源码,虚拟机部分在目录的 jdk15-0dabbdfd97e6srchotspot 下,研究了一会,又找到了一个文件classLoader.cpp ,这个文件大概率就是加载器的最终实现,然后我打开了它,嘿嘿,你猜怎么着,我看不懂,因为C++还没学好,而且代码也太多,溜了溜了,如果有能力日后再研究.
随便截取的一点点就已经头皮发麻了,这个研究下来估计得好一会了,自己有几斤几两还是清楚的,如果有什么问题欢迎评论吐槽和指点,如果有所研究希望也可以告诉我,感谢!

附上openJDK15下载地址
附上classLoader.cpp路径:jdk15-0dabbdfd97e6srchotspotshareclassfileclassLoader.cpp

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/749013.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号