前面已经写过一篇关于java classloader的拙文java classloader原理初探

时隔几年,再看一遍,觉得有些地方显得太过苍白,于是再来一篇:

完成一个Java类之后,经过javac编译,会生成一个class文件,这个class文件中包含跟这个类相关的所有基本信息:属性字段,方法等。这些都属于一个类的元数据,是不变的部分。在执行过程,则需要根据类的元数据信息生成一个实例对象,这个实例对象可以根据不同场景拥有不同状态。也就是说同一个class对应了运行过程中的不同状态。(请注意这里是class,不是object)每一个java文件在被编译之后,编译器都会给加入一个public的静态字段叫:class。在程序中,我们就可以通过SomeObject.class的方式来获取一个Class对象。
Java中通过包路径和类名来确定一个类,通过java api我们知道Class中有个方法是getClassLoader(),但如果一个类被两个不同classloader加载, 这个方法就会返回不同结果,在这种情况下,虽然是同一个class,但被不同class loader加载,那对应的Class对象就不是同一个。

为什么要设置环境变量:JAVA_HOME, CLASSPATH?JAVA_HOME对应的是java的安装根路径,为了能在系统中随处都能用到java给我们提供的命令行工具,所以要把JAVA_HOME/bin的路径添加到PATH中。但是CLASSPATH呢,为什么需要在CLASSPATH中指定那三个jar(tools.jar, dt.jar, rt.jar)?
先说明一下tools.jar主要是一些java 工具类,可以从openjdk上下载jdk的源码,找langtools这个路径下的文件来看看, 如果想写一些工具类,比如通过java来分析或编译java文件,可以查看一下tools里面的javac相关的api;dt.jar主要是swing相关的一些类。而rt.jar则是运行时相关的,是java的核心库中的java类。
再来理解一下java在启动时类的代理模式加载顺序。JAVA中除了Bootstrap class loader 都有一个parent class loader。参阅java 官方对于“Understanding Extension Class Loading”的说明。里面提到了三个方面的加载内容:一是rt.jar和i18n.jar等基础类包;二是扩展包;三就是我们classpath指定的依赖包。对于第一部分,由Bootstrap class loader负责加载,所以java包中的类的class loader就是bootstrap class loader,而扩展路径(一般是在jre/lib/ext)下由 extension class loader(ExtClassLoader)负责. 第三部分的加载就需要应用class loader(AppClassLoader)来负责了。一般情况下,直接通过java启动时,会注明一下-classpath 或 -cp来标识出依赖包列表(注意,不是一个路径,而是文件列表)。代码中负责初始化相关class loader的过程是在sun.misc.Launcher类中, 下面是Launcher的constructor方法中的实现。
public Launcher() {
        // Create the extension class loader
        ClassLoader extcl;
        try {
            extcl = ExtClassLoader.getExtClassLoader();
        } catch (IOException e) {
            throw new InternalError(
                "Could not create extension class loader", e);
        }

// Now create the class loader to use to launch the application
        try {
            loader = AppClassLoader.getAppClassLoader(extcl);
        } catch (IOException e) {
            throw new InternalError(
                "Could not create application class loader", e);
        }

// Also set the context class loader for the primordial thread.
        Thread.currentThread().setContextClassLoader(loader);
…..
}
对于前面提到的相关加载路径也都在这个类中有相关代码,不一一列举。

Java Api中的ClassLoader类中的loadClass方法:
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;
        }
    }

加载某个类时,当前classloader都会先委托parent class loader尝试加载。这种处理方式,有几个方面的考虑:一是安全问题,如果攻击者模拟实现了对应的类如java.lang.String, 当通过loadClass来加载类时,会首先到parent classloader中查找,很明显可以在bootstrap class loader中找到,这样可以尽可能保护最关键的代码。一是冗余问题,通过这种方式可以最大限度的降低重复加载。每个层次的classloader负责对应路径下的类库加载,而对于应用实现来说就可以集中在应用系统中。这里可以考虑一下web container的实现,如tomcat。每个jsp页面都会最终被编译成一个class文件,并放到work路径中,所以每个web容器都会自建一个classloader来加载指定路径中的class文件。

通过上面的说明,可以了解到jvm在启动过程中,如果发现有相同包路径的情况(不同jar,但相同包)下的同名类,则仅会加载一次,主要看哪个包在最前面。这里有个问题:为什么是在前面的包可以起作用,后面不行?如果是由同一个classloader加载还不会被覆盖么?其实如果是自己实现的classloader,那就可以调整这种策略,但java中约定的就是先加载的class会根据起binary name,将class metadata存于永久区,于是后面再有同名的类,都可以被找到,而不需要重新加载。

java classloader原理深究的更多相关文章

  1. Android(java)学习笔记106-1:深入分析Java ClassLoader原理

    1. 前言: Android中的动态加载机制能更好的优化我们的应用,同时实现动态的更新,这就便于我们管理我们的应用,通过插件化来减轻我们的内存以及CPU消耗,在不发布新版本的情况下能更新某些模块. 当 ...

  2. Android(java)学习笔记45:深入分析Java ClassLoader原理

    1. 前言: Android中的动态加载机制能更好的优化我们的应用,同时实现动态的更新,这就便于我们管理我们的应用,通过插件化来减轻我们的内存以及CPU消耗,在不发布新版本的情况下能更新某些模块. 当 ...

  3. Java ClassLoader 原理详细分析(转)

    转载自:http://www.codeceo.com/article/java-classloader.html 一.什么是ClassLoader? 大家都知道,当我们写好一个Java程序之后,不是管 ...

  4. 深入分析Java ClassLoader原理

    一.什么是ClassLoader? 大家都知道,当我们写好一个Java程序之后,不是管是CS还是BS应用,都是由若干个.class文件组织而成的一个完整的Java应用程序,当程序在运行时,即会调用该程 ...

  5. Java ClassLoader 原理详细分析

    一.什么是ClassLoader? 大家都知道,当我们写好一个Java程序之后,不是管是CS还是BS应用,都是由若干个.class文件组织而成的一个完整的Java应用程序,当程序在运行时,即会调用该程 ...

  6. Java Classloader原理分析

       类的加载过程指通过一个类的全限定名来获取描述此类的二进制字节流,并将其转化为方法区的数据结构,进而生成一个java.lang.Class对象作为方法区这个类各种数据访问的入口.这个过程通过Jav ...

  7. 【java】深入分析Java ClassLoader原理

    一.什么是ClassLoader? 大家都知道,当我们写好一个Java程序之后,不是管是CS还是BS应用,都是由若干个.class文件组织而成的一个完整的Java应用程序,当程序在运行时,即会调用该程 ...

  8. Java ClassLoader 原理分析

    一.ClassLoader(类加载器)的作用 如果一个程序包含不止一个class文件,那么当程序启动时,带有main方法的类的class文件作为程序入口先被JVM加载,然后根据程序调用的需要,再逐步进 ...

  9. 【Java核心】ClassLoader原理及其使用

    又把博客的皮肤换了换,看着更加简洁舒心一些.前段的知识只是略懂,拿过来就能用,只是自己的审美和设计水平有限,实在难以弄出自己特别满意的东西,也算是小小的遗憾吧!言归正传,由于最近涉及到Java核心的东 ...

随机推荐

  1. style2paints、deepcolor、sketchkeras项目

    数据集不够怎么办? 1 一些传统的边缘提取算法可以提取图像边缘. 2 这里我们有一个使用神经网络提取线稿图的项目——sketchkeras 源码:https://github.com/lllyasvi ...

  2. [golang]内存不断增长bytes.makeSlice

    ------------------------------------------ 2015.7月更新 后面发现这里其实有一个sb的问题,在于内存回收和释放. 每个http请求,都会带一个http. ...

  3. geo实现方案

    1.数据库内在支持GIS(地理信息系统) MySQL: 目前只有MyISAM引擎是支持GIS的,Innodb在5.7版本中才支持空间索引.MyISAM这个引擎不支持事务.外键,而且是表锁.适合读为主, ...

  4. 4.GIT安装

    最早Git是在Linux上开发的,很长一段时间内,Git也只能在Linux和Unix系统上跑.不过,慢慢地有人把它移植到了Windows上.现在,Git可以在Linux.Unix.Mac和Window ...

  5. Mybatis框架学习总结-解决字段名与实体类属性名不相同的冲突

    在平时的开发中,我们表中的字段名和表对应实体类的属性名称不一定是完全相同的. 1.准备演示需要使用的表和数据 CREATE TABLE orders( order_id INT PRIMARY KEY ...

  6. tools-eclipse-002-常用插件

    1.spring 查看eclipse版本 下载对应版本插件包Spring Tool Sute 地址:http://spring.io/tools/sts/all 离线包只列举了最新的,如图, 如果ec ...

  7. 最简单的win7、win8免费升级正版win10图文教程

    https://www.microsoft.com/zh-cn/software-download/windows10 http://jingyan.baidu.com/article/19192ad ...

  8. odoo继承父类中的函数(方法)

    使用_inherit继承父类重新设计新类时,可以调用父类中的函数,具体为: 第一步:获得某个模型('model.name')的数据集并进行某种集合操作(model_function),从而获得想要的数 ...

  9. JVM堆内存相关的启动参数:年轻代、老年代和永久代的内存分配

    如果想观察JVM进程占用的堆内存,可以通过命令工具jmap或者可视化工具jvisualvm.exe.JVM这些启动参数都拥有默认值,如果想了解JVM的内存分配策略,最好手动设置这些启动参数.再通过JD ...

  10. 在Windows下使用Dev-C++开发基于pthread.h的多线程程序【转】

    在Windows下使用Dev-C++开发基于pthread.h的多线程程序[转]     在Windows下使用Dev-C++开发基于pthread.h的多线程程序   文章分类:C++编程     ...