Detailed explanation of Java class loading mechanism

Transferred from:

Class loading mechanism is a highlight of Java language, which enables Java classes to be dynamically loaded into Java virtual machine.

This time, we will put aside terms and concepts and start with examples to explain Java's class loading mechanism from simple to in-depth.

This article involves knowledge points: parent delegation mechanism, classloader, extclassloader, appclassloader, custom network class loader, etc

Article code: nofollow "style =" color: rgb (0219); text-decoration:none; "> https://github.com/wingjay/HelloJava/blob/master/common/src/classloader/HelloClassLoader.java

The general process of using java classes in Java virtual machine is as follows: first, compile the Java source code (. Java file) written by the developer into Java bytecode (. Class file), and then the class loader will read the. Class file and convert it into an instance of java.lang.class. With the class instance, the Java virtual machine can use methods such as newinstance to create its real object. < P style = "color: rgb (68,68); font family: 'Microsoft YaHei'; font size: 14px;" > Classloader is a class loader provided by Java. Most class loaders inherit from classloader and are used to load class files from different sources< h2 id="Classu6587_u4EF6_u6709_u54EA_u4E9B_u6765_u6E90_u5462_3F" style="color:rgb(34,238);border-bottom-width:1px;border-bottom-style:solid;font-size:18px;font-family:'microsoft yahei';"> What are the sources of class files?

How to load classes?

Load?

Class file, the contents are as follows:

 classloader;

<span class="hljs-keyword" style="font-weight:bold;">public <span class="hljs-class"><span class="hljs-keyword" style="font-weight:bold;">class <span class="hljs-title" style="color:rgb(68,85,136);font-weight:bold;">MusicPlayer {
<span class="hljs-function"><span class="hljs-keyword" style="font-weight:bold;">public <span class="hljs-keyword" style="font-weight:bold;">void <span class="hljs-title" style="color:rgb(153,0);font-weight:bold;">print<span class="hljs-params">() {
System.out.printf(<span class="hljs-string" style="color:rgb(221,17,68);">"Hi I'm MusicPlayer");
}
}

     ClassNotFoundException {
    Class clazz = Class.forName(); ClassLoader classLoader = clazz.getClassLoader(); System.out.printf(,classLoader.getClass().getSimpleName()); }

Is loaded by.

   () throws ClassNotFoundException { Class clazz = Class.forName(); ClassLoader classLoader = clazz.getClassLoader(); System..printf(,classLoader.getClass().getSimpleName()); 
    <span class="hljs-keyword" style="font-weight:bold;"&gt;while</span> (classLoader.getParent() != <span class="hljs-literal" style="color:rgb(0,128,128);"&gt;null</span>) {
        classLoader = classLoader.getParent();
        Sy<a href="https://www.jb51.cc/tag/stem/" target="_blank" class="keywords">stem</a>.<span class="hljs-keyword" style="font-weight:bold;"&gt;out</span>.printf(<span class="hljs-string" style="color:rgb(221,68);"&gt;"Parent is <a href="https://www.jb51.cc/tag/s/" target="_blank" class="keywords">%s</a>\n"</span>,classLoader.getClass().getSimpleName());
    }

}

It was my parents, but I didn't see it. In fact, as mentioned above, it is special. It is implemented internally by the JVM, so.

     ClassNotFoundException { ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();  Class clazz = appClassLoader.loadClass( 去加载,它仍然会被  加载到。

加载机制

的一些特性了,下面来看一下它的实现代码,加深理解。

 里的  方法,便是需要分析的源码了。这个方法里做了下面几件事:


<p style="color:rgb(68,68);font-family:'microsoft yahei';font-size:14px;">
看完实现源码相信能够有更完整的理解。


<h2 id="u7C7B_u52A0_u8F7D_u5668_u6700_u9177_u7684_u4E00_u9762_uFF1A_u81EA_u5B9A_u4E49_u7C7B_u52A0_u8F7D_u5668" style="color:rgb(34,238);border-bottom-width:1px;border-bottom-style:solid;font-size:18px;font-family:'microsoft yahei';">
类加载器最酷的一面:自定义类加载器
<p style="color:rgb(68,68);font-family:'microsoft yahei';font-size:14px;">
前面提到了 Java 自带的加载器 <code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">BootstrapClassLoader
 Class loadClass(String name, resolve)
     ClassNotFoundException {  (getClassLoadingLock(name)) {  Class c = findLoadedClass(name);  (c == ) {  t0 = System.nanoTime();  {  (parent != ) {  c = parent.loadClass(name,); }  {  c = findBootstrapClassOrNull(name); } }  (ClassNotFoundException e) {   } 
        <span class="hljs-keyword" style="font-weight:bold;"&gt;if</span> (c == <span class="hljs-keyword" style="font-weight:bold;"&gt;null</span>) {
            <span class="hljs-comment" style="color:rgb(153,136);"&gt;// 如果 parent 均没有加载到目标class,<a href="https://www.jb51.cc/tag/diaoyong/" target="_blank" class="keywords">调用</a>自身的 findClass() <a href="https://www.jb51.cc/tag/fangfa/" target="_blank" class="keywords">方法</a>去<a href="https://www.jb51.cc/tag/sousuo/" target="_blank" class="keywords">搜索</a></span>
            <span class="hljs-keyword" style="font-weight:bold;"&gt;long</span> t1 = Sy<a href="https://www.jb51.cc/tag/stem/" target="_blank" class="keywords">stem</a>.nanoTime();
            c = findClass(name);

            <span class="hljs-comment" style="color:rgb(153,136);"&gt;// this is the defining class loader; record the stats</span>
            sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
            sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
            sun.misc.PerfCounter.getFindClasses().increment();
        }
    }
    <span class="hljs-keyword" style="font-weight:bold;"&gt;if</span> (resolve) {
        resolveClass(c);
    }
    <span class="hljs-keyword" style="font-weight:bold;"&gt;return</span> c;
}

}

<span class="hljs-comment" style="color:rgb(153,136);">// BootstrapClassLoader 会调用 native 方法去 JVM 加载
<span class="hljs-keyword" style="font-weight:bold;">private <span class="hljs-keyword" style="font-weight:bold;">native Class<?> findBootstrapClass(String name);

、<code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">AppClassLoader和<code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">ExtClassLoader,这些都是
Java 已经提供好的。

<p style="color:rgb(68,68);font-family:'microsoft yahei';font-size:14px;">
而真正有意思的,是 <code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">自定义类加载器,它允许我们在<code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">运行时可以从<code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">本地磁盘或网络上动态加载自定义类。这使得开发者可以动态修复某些有问题的类,热更新代码。

<p style="color:rgb(68,68);font-family:'microsoft yahei';font-size:14px;">
下面来实现一个<code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">网络类加载器,这个加载器可以从网络上动态下载 .class 文件并加载到虚拟机中使用。

<p style="color:rgb(68,68);font-family:'microsoft yahei';font-size:14px;">
后面我还会写作与 <code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">热修复/动态更新 相关的文章,这里先学习 Java 层 <code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">NetworkClassLoader 相关的原理。

<ol style="list-style:none;color:rgb(68,68);font-family:'microsoft yahei';font-size:14px;">
<li style="list-style-type:decimal;">
作为一个 <code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">NetworkClassLoader,它首先要继承 <code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">ClassLoader;
<li style="list-style-type:decimal;">
然后它要实现<code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">ClassLoader内的 <code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">findClass() 方法。注意,不是<code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">loadClass()方法,因为<code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">ClassLoader提供了<code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">loadClass()(如上面的源码),它会基于<code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">双亲委托机制去搜索某个
class,直到搜索不到才会调用自身的<code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">findClass(),如果直接复写<code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">loadClass(),那还要实现<code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">双亲委托机制;
<li style="list-style-type:decimal;">
在 <code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">findClass() 方法里,要从网络上下载一个 .class 文件,然后转化成 Class 对象供虚拟机使用。


     {
<span class="hljs-<a href="https://www.jb51.cc/tag/Meta/" target="_blank" class="keywords">Meta</a>" style="color:rgb(153,153);font-weight:bold;"&gt;@Override</span>
<span class="hljs-keyword" style="font-weight:bold;"&gt;protected</span> Class<?> findClass(String name) <span class="hljs-keyword" style="font-weight:bold;"&gt;throws</span> ClassNotFoundException {
    <span class="hljs-keyword" style="font-weight:bold;"&gt;byte</span>[] classData = downloadClassData(name); <span class="hljs-comment" style="color:rgb(153,136);"&gt;// 从远程下载</span>
    <span class="hljs-keyword" style="font-weight:bold;"&gt;if</span> (classData == <span class="hljs-keyword" style="font-weight:bold;"&gt;null</span>) {
        <span class="hljs-keyword" style="font-weight:bold;"&gt;super</span>.findClass(name); <span class="hljs-comment" style="color:rgb(153,136);"&gt;// 未找到,抛异常</span>
    } <span class="hljs-keyword" style="font-weight:bold;"&gt;else</span> {
        <span class="hljs-keyword" style="font-weight:bold;"&gt;return</span> defineClass(name,classData,<span class="hljs-number" style="color:rgb(0,128);"&gt;0</span>,classData.length); <span class="hljs-comment" style="color:rgb(153,136);"&gt;// convert class byte data to Class<?> object</span>
    }
    <span class="hljs-keyword" style="font-weight:bold;"&gt;return</span> <span class="hljs-keyword" style="font-weight:bold;"&gt;null</span>;
}

<span class="hljs-keyword" style="font-weight:bold;"&gt;private</span> <span class="hljs-keyword" style="font-weight:bold;"&gt;byte</span>[] downloadClassData(String name) {
    <span class="hljs-comment" style="color:rgb(153,136);"&gt;// 从 localhost 下载 .class <a href="https://www.jb51.cc/tag/wenjian/" target="_blank" class="keywords">文件</a></span>
    String path = <span class="hljs-string" style="color:rgb(221,68);"&gt;"http://localhost"</span> + File.separatorChar + <span class="hljs-string" style="color:rgb(221,68);"&gt;"java"</span> + File.separatorChar + name.replace(<span class="hljs-string" style="color:rgb(221,68);"&gt;'.'</span>,File.separatorChar) + <span class="hljs-string" style="color:rgb(221,68);"&gt;".class"</span>; 

    <span class="hljs-keyword" style="font-weight:bold;"&gt;try</span> {
        URL url = <span class="hljs-keyword" style="font-weight:bold;"&gt;new</span> URL(path);
        InputStream ins = url.openStream();
        ByteArrayOutputStream baos = <span class="hljs-keyword" style="font-weight:bold;"&gt;new</span> ByteArrayOutputStream();
        <span class="hljs-keyword" style="font-weight:bold;"&gt;int</span> bufferSize = <span class="hljs-number" style="color:rgb(0,128);"&gt;4096</span>;
        <span class="hljs-keyword" style="font-weight:bold;"&gt;byte</span>[] buffer = <span class="hljs-keyword" style="font-weight:bold;"&gt;new</span> <span class="hljs-keyword" style="font-weight:bold;"&gt;byte</span>[bufferSize];
        <span class="hljs-keyword" style="font-weight:bold;"&gt;int</span> bytesNumRead = <span class="hljs-number" style="color:rgb(0,128);"&gt;0</span>;
        <span class="hljs-keyword" style="font-weight:bold;"&gt;while</span> ((bytesNumRead = ins.read(buffer)) != -<span class="hljs-number" style="color:rgb(0,128);"&gt;1</span>) {
            baos.write(buffer,bytesNumRead); <span class="hljs-comment" style="color:rgb(153,136);"&gt;// 把下载的二进制数据存入 ByteArrayOutputStream</span>
        }
        <span class="hljs-keyword" style="font-weight:bold;"&gt;return</span> baos.toByteArray();
    } <span class="hljs-keyword" style="font-weight:bold;"&gt;catch</span> (Exception e) {
        e.printStackTrace();
    }
    <span class="hljs-keyword" style="font-weight:bold;"&gt;return</span> <span class="hljs-keyword" style="font-weight:bold;"&gt;null</span>;
}

<span class="hljs-function"&gt;<span class="hljs-keyword" style="font-weight:bold;"&gt;public</span> String <span class="hljs-title" style="color:rgb(153,0);font-weight:bold;"&gt;getName</span><span class="hljs-params"&gt;()</span> </span>{
    Sy<a href="https://www.jb51.cc/tag/stem/" target="_blank" class="keywords">stem</a>.out.printf(<span class="hljs-string" style="color:rgb(221,68);"&gt;"Real NetworkClassLoader\n"</span>);
    <span class="hljs-keyword" style="font-weight:bold;"&gt;return</span> <span class="hljs-string" style="color:rgb(221,68);"&gt;"networkClassLoader"</span>;
}

}

<p style="color:rgb(68,68);font-family:'microsoft yahei';font-size:14px;">
这个类的作用是从网络上(这里是本人的 local apache 服务器 http://localhost/java 上)目录里去下载对应的 .class 文件,并转换成 Class<?> 返回回去使用。

<p style="color:rgb(68,68);font-family:'microsoft yahei';font-size:14px;">
下面我们来利用这个 <code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">NetworkClassLoader 去加载 localhost 上的 <code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">MusicPlayer 类:

<ol style="list-style:none;color:rgb(68,68);font-family:'microsoft yahei';font-size:14px;">
<li style="list-style-type:decimal;">
首先把 <code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">MusicPlayer.class 放置于 <code style="font-size:12px;font-family:'courier new';color:rgb(119,119);">/Library/WebServer/Documents/java (MacOS)目录下,由于
MacOS 自带 apache 服务器,这里是服务器的默认目录;
<li style="list-style-type:decimal;">
执行下面一段代码:

 className = ;
NetworkClassLoader networkClassLoader =  NetworkClassLoader();
Class clazz  = networkClassLoader.loadClass(className);
  • 成功。
  •  可以正常工作,如果读者要用的话,只要稍微修改 url 的拼接方式即可自行使用。

    等,并开发了自定义的



    总结

    以上是编程之家为你收集整理的Java 类加载机制详解全部内容,希望文章能够帮你解决Java 类加载机制详解所遇到的程序开发问题。

    如果觉得编程之家网站内容还不错,欢迎将编程之家网站推荐给程序员好友。

    本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。

    喜欢与人分享编程技术与工作经验,欢迎加入编程之家官方交流群!

    编程之家官方1群

    编程之家官方2群







    The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
    THE END
    分享
    二维码
    < <上一篇
    下一篇>>