- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
1.1 类加载的过程
2.1 类加载机制-类加载器
2.1.1 一个类被加载都内存所经历的过程
2.1.2 代码验证、类加载器之间的关系
2.1.3 自定义类加载器
2.2 源码解析:类加载的过程
2.2.1 类之间的继承关系
2.3 双亲委派模型
2.3.1 宗旨:向上委托、向下委派。有事往上捅。
2.3.2 优点
2.4 打破双亲委派模型:重写loadClass()
2.4.1 JDBC、JNDI等SPI机制
2.4.2 Tomcat
2.4.3 热部署
3.1 验证
3.2 准备
3.2 解析
Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称为虚拟机的类加载机制。
Java语言里类型的加载、连接、初始化都是在程序运行期间完成的。例如,编写一个面向接口的应用程序,可以等到运行时再指定其实际的实现类,用户可以通过Java预置的或者自定义类加载器,将某个应用程序在运行时从网络或者本机磁盘加载一个二进制流文件作为程序的一部分(比如SPI机制)。这都依赖Java语言运行期类才加载诞生的。
一个类被加载的过程包括加载、链接(验证、准备、解析)、初始化。
注意:这里类之间的关系不是继承关系而是它们之间存在这样的加载顺序。
1.类加载器先查询缓存,判断是否加载过 findLoaderClass,若加载过直接返回
2.若没有加载过,向上委托:自底向上检查该类是否已经加载
3.直到BootStrap ClassLoader,findClass是不是归自己管理。是:加载
**4.否:向下委派:**自顶向下进行实际查找和加载
5.类似递归的过程
**
**
/**
* 类加载器
*
* @author Promsing(张有博)
* @version 1.0.0
* @since 2022/8/6 - 11:23
*/
public class ClassLoadLevel {
public static void main(String[] args) {
System.out.println(String.class.getClassLoader());
//期望是bootstrap,但是显示null,null就是bootstrap
//bootstrap是C++实现的,Java中没有与它做对应,所以空值
System.out.println(sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader());
//期望是extension,但是显示extension
System.out.println(JSONObject.class.getClassLoader()); //application
//期望是application,但是显示AppClassLoader
System.out.println(ClassLoadLevel.class.getClassLoader());
//期望是application,但是显示AppClassLoader
System.out.println("----加载器的加载器,加载器的加载器不是父加载器---------------");
System.out.println(sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader().getClass().getClassLoader());
//期望是bootstrap,但是显示null,null就是bootstrap
System.out.println(JSONObject.class.getClassLoader().getClass().getClassLoader()); //application
//期望是bootstrap,但是显示null,null就是bootstrap
System.out.println(ClassLoadLevel.class.getClassLoader().getClass().getClassLoader());
//期望是bootstrap,但是显示null,null就是bootstrap
System.out.println("----加载器的父加载器---------------------");
System.out.println(sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader().getParent());
//期望是bootstrap,但是显示null,null就是bootstrap
System.out.println(JSONObject.class.getClassLoader().getParent()); //application
//期望是extension,但是显示extension
System.out.println(ClassLoadLevel.class.getClassLoader().getParent());
//期望是extension,但是显示extension
}
}
/**
* 自定义类加载器
*
* @author Promsing(张有博)
* @version 1.0.0
* @since 2022/8/6 - 15:23
*/
public class CustomClassLoad extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
String concat = name.replaceAll(".", "/");
String replace = name.replace(".", "/");
//file转字节数组
File f=new File("F:\\09 个人学习\\study-designn-pattern\\src\\main\\java\\",name.replace(".","/").concat(".class"));
FileInputStream fis=new FileInputStream(f);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int b=0;//b是实际的内容
while ((b=fis.read())!=0){
baos.write(b);
}
byte[] bytes = baos.toByteArray();
baos.close();
fis.close();
//把字节码转化成java.lang.Class
return defineClass(name,bytes,0,bytes.length);
} catch (IOException e) {
e.printStackTrace();
}
return super.findClass(name);
}
public static void main(String[] args) throws Exception {
CustomClassLoad load=new CustomClassLoad();
Class<?> aClass = load.loadClass("com.promsing.creational.singleton.Person");
System.out.println(aClass.getName());
Object o = aClass.newInstance();
System.out.println(o.toString());
}
}
AppClassLoader--》URLClassLoader--》SecureClassLoader--》ClassLoader
ExtClassLoader--》URLClassLoader--》SecureClassLoader--》ClassLoader
加载的方法:ClassLoader.loadClass(name,resolve)方法。跟上面刚说的一致:** 一个类被加载都内存所经历的过程**
1.先findLoaderClass 看是否被加载过
2.变量parent被final修饰了,无法更改加载顺序。
3.//注意注意:这里就是向上委托的过程
c = parent.loadClass(name, false);
4. //注意注意:这里就是向下委派的过程,
代码上不直观,好好想一想递归
5.这个类似一个递归的过程,当父加载器找不到时,就回到了子加载器。这就是委派的过程
6.具体过程看代码注释
loadClass() 主要进行类加载的方法,默认的双亲委派机制就实现在这个方法中
findClass() 根据名称或位置加载.class字节码
definClass() 把字节码转化成java.lang.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 {
//AppClassLoader的父类是ExtClassLoader,依次往上
if (parent != null) {
//父类的loadClass方法也是调用的ClassLoader.loadClass
//注意注意:这里就是向上委托的过程
c = parent.loadClass(name, false);
} else {
//直到BootStrapClassLoader没有父类了,调用c++代码加载class文件
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
//注意注意:这里就是向下委派的过程,代码上不直观,好好想一想递归
//首先是ExtClassLoader中的c == null然后执行,返回
//接着是AppClassLoader中的c == null
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
//最终AppClassLoader中执行findClass找到了我们自定义的类
//看下文
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;
}
双亲委派的指的是,类加载由最底下的自定义加载器开始,将加载请求委派给上级,如果上级已经加载过该类,则直接加载完成,如果没有则继续递归至顶级Bootstrap加载器,然后父类判断如果该类不属于自己的加载范畴则委派子类加载。继续往下递归。
如果一个类加载器收到了类加载的请求,他首先不会自己去尝试加载这个类,而是把这个请求委派父类加载器去完成。每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个请求(他的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
1.保证了JVM提供的核心类不被篡改,保证class执行安全
2.保证一个类只能被加载一次、防止重复加载同一个class,每个类加载器只会加载自己负责的部分,这样每个类只会被加载一次。
原因:
但是,类加载器受到加载范围的限制,在某些情况下父类加载器无法加载到需要的文件,需要委托子类加载器去加载class文件。
解决办法:
DriverManager封装了静态代码块来实现这一加载过程。
静态代码块中调用 loadInitialDrivers() 方法,并调用ServiceLoader.load(Driver.class) 加载所有在META-INF/services/java.sql.Driver 文件里边的类到JVM内存,完成驱动的自动加载。
原因
**1.一个容器部署两个程序 **
一个web容器可能需要部署两个应用程序,不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求同一个类库在同一个服务器只有一份,因此要保证每一个应用程序的类库都是独立的,保证相互隔离
2.部署在同一个容器的程序要共享类库
部署在同一个web容器中相同的类库相同的版本可以共享。否则,如果服务器有10个应用程序,那么要有10份相同的类库加载进虚拟机
3.web容器的类库与程序类库隔离
web容器也有自己依赖的类库,不能与应用程序的类库混淆。基于安全考虑,应该让容器的类库和程序的类库隔离开来。
4.web容器需要修改jsp后不用重启
web容器要支持jsp修改,我们知道,jso文件最终也要编译成class文件才能在虚拟机中运行,但程序运行后修改jsp已经是司空见惯的事情,web容器需要支持jsp修改后不用重启
解决
1.CommonClassLoader
commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;CommonClassLoader能加载的类都可以被Catalina ClassLoader和SharedClassLoader使用,从而实现了公有类库的共用
2.CatalinaClassLoader
catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
3.SharedClassLoader
sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见
4.WebappClassLoader
WebappLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见
5.JsperClassLoader
以上为5之前的tomcat,5之后的tomcat只有common 和 webapp 以及 jsp ,其余两个需要自己定制
原因:
1.需要被热部署
2.利用同一个class文件不同的类加载器在内存创建出两个不同的class对象
解决:
1.自定义类加载器:重写方法
2.通过一个线程去监听文件的修改时间
3.重写findClass方法
4.把文件以流的形式读进来,然后调defineClass方法
验证文件是否符合JVM规范
静态成员变量赋默认值
将类、方法、属性等符号引用解析为直接引用
对于一个类何时被加载,有且仅有六种情况
遇到new、getstatic、putstatic或者invokestatic
使用反射的时候
初始化时,先初始化父类
虚拟机启动、初始化main方法所在的类
动态语言 java.lang.invoke.MethodHandle
接口中的默认方法、子类初始化的时候,接口也会初始化
是否可以复制一个完整的 JVM,并且在故障转移的情况下只需将负载转移到复制的 JVM 上? 如果是,那我们该怎么做呢? 最佳答案 如果您的应用程序是 Web 应用程序,请阅读“集群”和“负载平衡”。大
我读了下面的话,但我想知道它们之间的区别...... JVM 规范、JVM 实现、JVM 运行时 最佳答案 JVM 规范:描述 JVM 应如何运行的文档。 JVM 实现:基于 JVM 规范的 JVM
我目前有四个不同的 java 应用程序,它们由 .bat 文件启动的 jar 运行,这些文件位于 Windows XP Embedded 开始菜单的 starup 文件夹中。我还启动了 Firefox
有人能给我一些关于强制 64 位 jvm 作为 32 位 jvm 运行的想法吗? 我需要为蓝牙连接编写一个 jse 桌面应用程序。为此,我需要实现 Bluecove jar 。它只有 32 位文件。所
我看到过关于这个问题的多条评论——有人说是,有人说不是,许多答案模棱两可。任何人都可以用更简单的术语描述它所在的位置吗?在一篇文章中,我什至看到有人说它与类加载器加载类的类内存共享相同的内存位置 -
我正在寻找所有可能的 jvm 退出代码的完整列表(不是 java System.exit(x))。我使用搜索引擎唯一能找到的是 SIGTERM 退出代码列表:http://journal.thobe.
为了监视任何正常的 Java 进程 JVM,我们可以使用 Attach API。是否有可用于监控 WebSphere JVM 的 API? 最佳答案 您可以使用 PMI(性能监控基础设施)来监控 JV
这个问题在这里已经有了答案: 8年前关闭。 Possible Duplicate: Java - C-Like Fork? 我想知道如何从 JDK fork 子 JVM,甚至有可能这样做吗? 一些框架
JVM 上的哪些图灵完备语言实现不使用 JVM 堆栈作为调用堆栈? (我问是因为我想在同一个线程中实现 Scala 和另一种语言之间的协程。) 最佳答案 闪蝶 SISC(方案代码的第二解释者) 曾经不
我看到here除了 Java 之外,还有很多语言可以在 JVM 上运行。我对在 JVM 中运行的其他语言的整个概念有些困惑。所以: 为 JVM 使用其他语言有什么优势? 为 JVM 编写语言/编译器需
我已经运行了 straced JVM (OpendJDK 11): strace -e trace=mmap java -Xms8192m Main 输出是: mmap(NULL, 8192, PRO
我已经运行了 straced JVM (OpendJDK 11): strace -e trace=mmap java -Xms8192m Main 输出是: mmap(NULL, 8192, PRO
我编写了一个简单的数独求解器。为了粗略测试性能,我使用简单的 System.currentTimeMillis 调用。 我在文本文件中准备了一组初始数独配置。该程序读取该文件并解决每个数独配置。运行测
JVM 被广泛使用:Scala、Groovy、Jython 等。我听说它被描述为“卓越”、“出色”和“严重低估”。为什么? 更具体地说,是什么让 JVM 独一无二?随着所有资金投入 .NET,或者 C
这个问题在这里已经有了答案: 10年前关闭。 Possible Duplicate: Are there any Java VMs which can save their state to a fi
想象一下 6-7 台服务器的设置都完全相同Java 版本“1.6.0_18”OpenJDK 运行时环境 (IcedTea6 1.8) (fedora-36.b18.fc11-i386)OpenJDK
(如有错误请指正) 我了解到,当您通过发出 java 命令来运行 java 程序时, java MyProg 程序将在新的 JVM 上运行。 什么将程序加载到新的 JVM 中?是生成新线程的 JRE
我们有一个使用 JNI 的桌面应用程序偶尔会导致 JVM 崩溃。幸运的是,JVM 会生成一个 hs_err_pidXXXX.log 文件,这对于调试此类错误非常有用。然而,它似乎总是转到当前工作目录,
我在命令提示符下运行一个程序集 jar 文件并得到下面的异常。并导致终止。 Uncaught error from thread [ccp-akka.persistence.dispatchers.d
一、什么是Java虚拟机 虚拟机:指以软件的方式模拟具有完整硬件系统功能、运行在一个完全隔离环境中的完整计算机系统 ,是物理机的软件实现。常用的虚拟机有VMWare,Visual Box,Java
我是一名优秀的程序员,十分优秀!