Class 类加载机制,面试考察方面挺频繁的,今天在项目中也遇到了要了解类加载机制的地方,要了解Javaagent,Javaagent 大家知道它是在类加载时期对程序起到监控作用的。特此记录一下,发现真的以学习研究的项目为驱动,真的的会效率很高,而且执导老师说的多写demo ,现在养成一学知识就先想整个demo 出来,手头上的demo 还真的渐增了起来,今天看源码比网上的教程会明白,配合别人将源码的博客真的瞬间懂了点什么感觉。生看的化是很难记住的。
/A class loader is an object that is responsible for loading classes. The
* class <tt>ClassLoader</tt> is an abstract class. Given the <a
* href="#name">binary name</a> of a class, a class loader should attempt to
* locate or generate data that constitutes a definition for the class. A
* typical strategy is to transform the name into a file name and then read a
* "class file" of that name from a file system.
*
* <p> Every {@link Class <tt>Class</tt>} object contains a {@link
* Class#getClassLoader() reference} to the <tt>ClassLoader</tt> that defined
*/
摘自 jdk 中 对class load 的介绍 第一次觉得 看注释这么有用,
一个class loader 是对 已加载到类响应的 object 他是一个抽象类,通过传递一个 二进制的类name ,类加载器试图定位或生成数据来构造一个对类的定义 典型的方式 是转化为一个 文件名称 然后读取类的名称在 文件系统中。 就是从文件系统中读取该文件名称的文件。
因此你可以通过 得到本类的class 对象 获得 加载本身的ClassLoader对象
ClassLoader classLoader = ClassLoaderDemo.class.getClassLoader();
当然获得类加载的对象,并不是我们想了解的我们需要了解类加载的过程,才能在明白Javaagent 及一些在编译期发挥作用的字节码技术。关键有两个方法 loadClass 和 findClass 。
从这两个方法中可以引申出 类加载机制的很关键的一个 类加载模型 就是双亲委派机制模型。
类在加载时,首先会查看是否已经加载过了这个类,如果已经将类加载到 jvm 中 ,那就返回加载的类。
如果没有
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);
//如果没有找到加载的类 返回为 null
if (c == null) {
//设置System.nanoTime()的返回值只和进程已运行的时间有关, 不受调系统时间影响. 后面计算时间有用的
long t0 = System.nanoTime();
try {
//如果该类加载器有 父类 让父类尝试去加载 传入的name 类 也就是双亲委派的实现 让父类去先加载
if (parent != null) {
c = parent.loadClass(name, false);
} else {
//如果没有 则让 接下来这个 实际是这个 本地方法 c++ 去调用加载 private native Class<?> findBootstrapClass(String name);
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 如果文件不存在 自然父类 加载不到 抛出异常
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
//如果父类返回的同样为null 说明父加载没有成功
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
// 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;
}
}
/
* Finds the class with the specified <a href="#name">binary name</a>.
* This method should be overridden by class loader implementations that 应该实现 其委派模型去加载类
* follow the delegation model for loading classes, and will be invoked 该方法应该在执行了父类检查类加载后被执行
* the {@link #loadClass <tt>loadClass</tt>} method after checking the 它默认只实现了抛出异常
* parent class loader for the requested class. The default implementation
* throws a <tt>ClassNotFoundException</tt>.
*
* @param name
* The <a href="#name">binary name</a> of the class
*
* @return The resulting <tt>Class</tt> object
*
* @throws ClassNotFoundException
* If the class could not be found
*
* @since 1.2
*/
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
因为我们知道 所有的类加载器都要去继承 ClassLoader 去重写其方法,实现了的 类加载器,去重写findClass方法时要实现本 类加载器去加载传入的 name。
验证一下 查看一下 URLClassLoader 中 的 findClass() 方法
/
* Finds and loads the class with the specified name from the URL search 找到 并加载类
* path. Any URLs referring to JAR files are loaded and opened as needed
* until the class is found.
*
* @param name the name of the class
* @return the resulting class
* @exception ClassNotFoundException if the class could not be found,
* or if the loader is closed.
* @exception NullPointerException if {@code name} is {@code null}.
*/
protected Class<?> findClass(final String name)
throws ClassNotFoundException
{
final Class<?> result;
try {
result = AccessController.doPrivileged(
new PrivilegedExceptionAction<Class<?>>() {
public Class<?> run() throws ClassNotFoundException {
String path = name.replace('.', '/').concat(".class");
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
return defineClass(name, res);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
} else {
return null;
}
}
}, acc);
} catch (java.security.PrivilegedActionException pae) {
throw (ClassNotFoundException) pae.getException();
}
if (result == null) {
throw new ClassNotFoundException(name);
}
return result;
}
最高层为 c++ 实现的类加载器。 下面两个为扩展的类加载器及 程序加载器, 是Launcher类中的 两个静态内部类。继承是上面所说的URLClassLoader的子类 网上一直拿上面的图举例说明肯定 这两个类肯定有作用。
static class AppClassLoader extends URLClassLoader {
static class ExtClassLoader extends URLClassLoader {
下次再去分析它
还有一个问题就是 为什么类要求 进行双亲委派制模型去进行类加载 ?
直接使用一个类加载器,或者根据文件的类型去划分类加载器去分别加载它不行吗?
只知道 bootstrap classloader 是使用 c++ 编写的 ,然后 下面两个是java 编写的,是不是很这样导致的分层关系,
网上的觉得有道理 摘抄过来
一个是避免重复加载,因为他会一只向上递归去检查自己的上一层父类是否已经被加载了。
下面说的意思归结起来就是 防止自定义的核心类库被破坏掉,我的想法:如果你自定义的类 和api里面定义的类 同时能够被类加载了,那jvm不知道执行哪个了,还有像下面说的 如果可以加载了自定义的超类 object ,那所有的类型都会按照你定义的object 去走,那肯定给你的开发 也带来不便和混乱。
不同层次的类加载器具有不同优先级,比如所有Java对象的超级父类java.lang.Object,位于rt.jar中,无论哪个类加载器加载该类,最终都是由启动类加载器进行加载,保证安全。即使用户自己编写一个java.lang.Object类并放入程序中,虽能正常编译,但不会被加载运行,因为Java中的java.lang.Object类是由启动类加载器进行加载,自己编写的java.lang.Object类不会被启动类加载器进行加载,保证不会出现混乱。相反,如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,那系统中将会出现多个不同的Object类,程序将混乱。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。