精华内容
下载资源
问答
  • 最近在测试项目代码中遇到同一个类不同classloader加载后出现的问题: A中有一个字段a,它的类型为X B中有一个字段b,它的类型也为X A由classLoaderA所加载,B由classLoaderB所加载 执行赋值语句A...

    最近在测试项目代码中遇到同一个类由不同的classloader加载后出现的问题:

    1. 类A中有一个字段a,它的类型为X
    2. 类B中有一个字段b,它的类型也为X
    3. 类A由classLoaderA所加载,类B由classLoaderB所加载
    4. 执行赋值语句A.a = B.b,由于这两个类型均为X,可以执行,但是有一个要求,这个要求就是在A中所装载类X的装载器必须和在B中装载类X的装载器相同,否则赋值语句失败

        为什么会产生上面的输出,我们可以来看一个以下的代码

     首先是一个简单的类调用:

    类Foo3

    1 public class Foo3 implements IFoo{
    2     public void hello() throws Exception{
    3         Class<?> clazz = Foo.class;
    4         Foo foo2 = Foo4.foo;
    5     }
    6 }

    在上面的代码中,变量foo2引用了类Foo4的一个静态引用:

    1 public class Foo4 {
    2     public static Foo foo = new Foo();
    3 }

    类Foo是一个非常简单的java类,即普通的java类:

    1 public class Foo implements IFoo{}

      重点在于如何运行这段代码,我们运行一段代码,分别使用两个类加载器来加载同一个类类Foo,运行代码如下

    1 MyClassLoader3 myClassLoader3 = new MyClassLoader3(T.class.getClassLoader());
    2 IFoo foo3 = (IFoo) (myClassLoader3.loadClass("com.m_ylf.study.java.classLoad.Foo3").newInstance());
    3 foo3.hello();

    在上面的代码中,采用自定义的classLoader来定义类Foo3,我们来看具体的定义:

    1 public Class<?> loadClass(String name) throws ClassNotFoundException {
    2     if("Foo".equals(name) ) {
    3         //自定义
    4     }
    5     if("Foo3".equals(name) ) {
    6         //自定义
    7     }
    8     return super.loadClass(name);
    9 }

     其实就是将类类Foo和类Foo3交由classLoader3即我们自定义加载器来加载,其它的类仍交由super即appClassLoader来加载。现在运行这段代码,即会有一个出错信息,出错信息如下:

    Exception in thread "main" java.lang.LinkageError: loader constraint violation: when resolving field "foo" the class
    loader (instance of MyClassLoader3) of the referring class, Foo4, and the class loader (instance of sun/misc
    /Launcher$AppClassLoader) for the field's resolved type, /Foo, have different Class objects for that type
        at Foo3.hello(Foo3.java:7)

      错误在第7行,即Foo foo2 = Foo4.foo;这一行出错了。

        为什么会出错,我来看来第一行代码:Class<?> clazz = Foo.class;这段代码,会对Foo类进行加载,采用的加载器为myClassLoader3,即加载Foo3类时所使用的加载器。这句话运行之后,即表示类Foo已经被加载了,且加载器为myClassLoader3。
        第二行代码:Foo foo2 = Foo4.foo。这段代码会初始化Foo4,由于myClassLoader3并没有特殊处理Foo4,所以将由父类加载器,即AppClassLoader来加载,在加载过程中,因为调用到了Foo4.foo,所以会加载Foo类。这个加载是在Foo4类初始化时进行加载的。因为在碰到类Foo时,appClassLoader显示其从未加载过foo(先前的foo是由myClassLoader加载的,而不是由appClassLoader加载的),所以又会加载Foo。
        这时候,类Foo就会有两个类加载器,一个是由myClassLoader3加载的,另一个是由appClassLoader加载的。如果两个类分开运行,代码是没有问题的。
        问题就出在这个赋值语句,或者说是对象引用上。在Foo3内部使用Foo4.foo时,JVM会记录Foo4.foo在foo3内部的类引用和加载器,在这个运行代码中,此加载器为myClassLoader,因为在调用Foo4.foo之前已经加载了Foo。然而,在引用时,它将得到声明Foo4.foo时的Foo类型的加载器,在Foo4.foo中, Foo类型的加载器为appClassLoader。JVM在运行时会对这两个加载器进行验证,JVM规范中要求这两个加载器必须要一致,否则将报类验证错误,即VerifyError的错误,这是为了防止不正常的类冒充正确的类进行类型欺骗。如在类Foo3中的Foo是来自于黑客故意构建的一个类时。

        我们再来看关于jbmp的问题,这是由于引用juel.jar时,里面有一个类如ExpressionFactory类,此类在类JspApplicationContext中被声明。在juel.jar中,类ExpressionFactory已经被jspClassLoader加载了,现在要进行赋值语句,即=由jspContext中取得的expressionFactory对象。而JspApplicationContext是由Tomcat的StandardClassLoader类加载的,在类JspApplicationContext中声明的expressionFactory字段自然也是由StandardClassLoader类加载的。现在两个由不同类加载器加载的同一个对象要进行引用操作,自然不能通过JVM的验证了。
        总而言之,就是说JVM在引用其它类的字段,或者调用其它类的方法时,将进行类型验证。验证包括,字段的类型验证,方法的返回类型验证,方法参数类型验证等。验证的内容就验证在调用方和被调用方时,同一个类的加载器是否一致。即在调用方时,记录的字段(参数)类型的加载器与被调用方法记录的字段(参数)类型的加载器是否一致。如果不一致,自然就不会被JVM验证通过。

    ref:http://www.iflym.com/index.php/code/understand-jvm-load-constraint.html

     

    上面这篇blog和我在项目中遇到的问题是一致的,我们在项目中需要对旧版本的Class对象就行替换,之前的做法仅仅是把Impl中值给替换了,之后在debug过程中发现这样是不够的,因为在tuscany的加载过程中它会对具体的implementation实现进行Introspection来检查这个实现中有哪些Reference、Service等等,它会将Reference的字段保存下来,然后在运行过程中通过相应的Injector来进行注入,一开始的做法,我们是将Injector里面保存的method和field用新版本的给替换(因为我们发现tuscany里面的注入的具体实现是通过反射来实现的),这样改完之后运行时就出现了上面blog中出现的问题。

    仔细分析了下错误原因,我们发现tuscany通过反射注入的值是旧版本的,而我们的method、field对象都是新版本的,这样就会出现IllegalArgumenException错误,分析之后得出结论:tuscany用来生成注入值所使用的字段接口仍然是旧版本的,也就是说我们的替换不完全,通过对WireObjectFactory中保存的interfaze的替换,将旧版本从中移除,这样反射时就不会出错了

     

    在调试过程中还遇到另外一个问题,由于field是private类型的,当我们需要对它进行注入时取消java语言访问控制检查

    1 newField.setAccessible(true);

     

     

    转载于:https://www.cnblogs.com/feiling/archive/2013/01/12/2857682.html

    展开全文
  • 不同类加载加载同一个class文件

    千次阅读 2020-10-19 07:41:15
    不同类加载加载同一个class文件得到的类型也是不同的。 验证如下: 在D:\\00-test目录下,有名为Test.class的文件,其编译前的源码如下: public class Test { public static int count = 0; public Test()...

    不同类加载器加载同一个class文件得到的类型也是不同的。

    验证如下:

    D:\\00-test目录下,有名为Test.class的文件,其编译前的源码如下:

    public class Test {
        public static int count = 0;
    
        public Test() {
            ++count;
            System.out.println(this.getClass().getClassLoader());
            System.out.println("count: " + count);
        }
    }
    

    每当创建一个对象的时候,静态变量count就会自增,可以利用count的值来判定不同类加载器得到的Class对象是否是同一个。

    创建Main类如下:

    public class Main {
        public static void main(String[] args) throws Exception {
            URLClassLoader loader_1 =
                    new URLClassLoader(new URL[]{new File("D:\\00-test").toURI().toURL()});
    
            URLClassLoader loader_2 =
                    new URLClassLoader(new URL[]{new File("D:\\00-test").toURI().toURL()});
    
            Class<?> clazz_1 = loader_1.loadClass("Test");
            clazz_1.newInstance();
            Class<?> clazz_2 = loader_2.loadClass("Test");
            clazz_2.newInstance();
        }
    }
    

    main()方法中,首先自定义两个类加载器,分别使用这两个类加载器加载Test.class文件,然后使用得到的Class对象创建实例,最后得到结果如下:

    java.net.URLClassLoader@1b6d3586
    count: 1
    java.net.URLClassLoader@74a14482
    count: 1
    

    可以看到,两个对象的类加载器并不相同,count值却是相同的,这说明这两个实例虽然都是从Test.class中得来,类型却并不相同,从而count只自增一次。

    实际上,在jvms8的5.3.2节有这样一段阐述:

    At run time, a class or interface is determined not by its name alone, but by a pair:
    its binary name (§4.2.1) and its defining class loader. Each such class or interface
    belongs to a single run-time package. The run-time package of a class or interface is
    determined by the package name and defining class loader of the class or interface.
    

    翻译如下:
    在运行时,一个类或者接口并不是单单由它的名称确定的,而是由它的二进制名称以及它的定义类加载器共同确定的。
    每个这样的类或者接口都属于一个运行时包,运行时包则由包名和定义类加载器共同确定。

    上述中所谓的定义类加载器实际上就是getClassLoader()的返回值,而这个概念的产生则跟类加载过程中的“双亲委派机制”有关。

    当要加载一个类时,第一个发起类加载的加载器称之为“初始类加载器”(initiating loader),但是根据“双亲委派机制”,它会先将加载委派给父加载器,如果父加载器加载失败,才会最终由自己尝试加载。而无论哪个加载器加载成功,它就是该类的定义类加载器(defining class loader)。

    参考:https://www.cnblogs.com/SanjiApollo/p/12839682.html

    展开全文
  • 下面小编就为大家带来classloader类加载器_基于java加载方式详解。小编觉得挺不错的,现在就分享给大家,也给大家做参考。一起跟随小编过来看看吧
  • 、定义类加载器、初始类加载类加载器结构: bootstrap ExtClassloader AppClassloader 自定义clsloadr1 自定义clsloadr2 如果用“自定义clsloadr1加载java.lang.String,那么根...

    https://blog.csdn.net/qq_26222859/article/details/52600260

    一、定义类加载器、初始类加载器

    类加载器结构:

    • bootstrap
      • ExtClassloader
        • AppClassloader
          • 自定义clsloadr1
          • 自定义clsloadr2

    如果用“自定义clsloadr1”加载java.lang.String类,那么根据双亲委派最终bootstrap会加载此类,那么bootstrap类就叫做该类的“定义类加载器”,而包括bootstrap所有得到该类class实例类加载器都叫做“初始类加载器”。

    二、命名空间

    命名空间是指jvm为每个类加载器维护的一个“表”,这个表记录了所有以 此类加载器 为 “初始类加载器”(而不是定义类加载器,所以一个类可以存在于很多的命名空间中)加载的类的列表。

    例如:
    CLTest是AppClassloader加载的String通过加载CLTest的类加载器,也就是AppClassloader进行加载,但最终委派到bootstrap加载的(当然,String类其实早已经被加载过了,这里只是举个例子)。所以,对于String类来说,bootstrap是“定义类加载器”,AppClassloader是“初始类加载器”。根据刚才所说,String类AppClassloader命名空间中(同时也在bootstrapExtClassloader的命名空间中,因为bootstrap,ExtClassloader也是String的初始类加载器),所以CLTest可以随便访问String类。这样就可以解释“处在不同命名空间的类,不能直接互相访问”这句话了。

    三、

    一个类由不同的类加载器实例加载的话,会在方法区产生两个不同的类彼此不可见,并且在中生成不同Class实例

    四、

    那么由不同类加载器实例(比如-自定义clsloadr1,-自定义clsloadr2)所加载的classpath下和ext下的类,也就是由我们自定义的类加载器委派给AppClassloader和ExtClassloader加载的类,在内存中是同一个类吗?

    所有继承ClassLoader并且没有重写getSystemClassLoader方法的类加载器,通过getSystemClassLoader方法得到的AppClassloader都是同一个AppClassloader实例,类似单例模式。

    在ClassLoader类中getSystemClassLoader方法调用私有的initSystemClassLoader方法获得AppClassloader实例,在initSystemClassLoader中:

    sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
    ...
    scl = l.getClassLoader();
    

    AppClassloader是sun.misc.Launcher类的内部类,Launcher类在new自己的时候生成AppClassloader实例并且放在自己的私有变量loader里:

    loader = AppClassLoader.getAppClassLoader(extclassloader);
    

    值得一提的是sun.misc.Launcher类使用了一种类似单例模式的方法,即既提供了单例模式的接口getLauncher(),又把构造函数设成了public的。但是在ClassLoader中是通过单件模式取得的Launcher 实例的,所以我们写的每个类加载器得到的AppClassloader都是同一个AppClassloader类实例

    这样的话得到一个结论,就是所有通过正常双亲委派模式的类加载器加载的classpath下的和ext下的所有类在 方法区 都是 同一个类,堆中 的 Class实例 也是同一个

    展开全文
  • 有时候我们需要做热更新...不允许两个ClassLoader加载同一个动态库。那么只要把更新下来的so文件命名成一个新的名字,并且使用两个不同的没有继承关系的CLassLoader加载就行了。  你以为这样就可以了吗?不是的。...

    有时候我们需要做热更新,所以如果更新包里面有so库的话。可能需要重新加载。但是更新前的classLoader已经加载过该动态库了,Java VM 为了确保ClassLoader的命名空间独立性。不允许两个ClassLoader加载同一个动态库。那么只要把更新下来的so文件命名成一个新的名字,并且使用两个不同的没有继承关系的CLassLoader加载就行了。

        你以为这样就可以了吗?不是的。这种方法在多数机型里面测试可以使用,但是在华为手机里面就直接卡住了。具体原因不清楚。所以最好的办法是,把so包放在dex文件里面。没更新之前先不加载so包到内存。而是随着dex包一下更新下载下来。然后删除掉旧的so包,使用新的so包。然后再把so包加载到内存里面。这样,整个过程就加载一次so包。

     

     

    展开全文
  • 看了很多地方都写着:同一个类不能被同一个classloader加载多次, 我的代码如下: [code="java"] for(int i=0 ; i;i++){ try { Thread.currentThread().getContextClassLoader().loadClass("test2....
  • 不同ClassLoader加载的同名属于不同的类型,不能相互转化和兼容。 新建一个工程NotSameClass。将如下代码,份放入NoSameClass工程源代码目录下,份编译成.class放入D:\temp目录下 运行如下代码,使用不同...
  • 【图解版】深入分析ClassLoader加载工作机制,从原理到JVM的装载过程,详情分析了ClassLoader加载类以及自定义加载器的过程,不可用于商业用途,如有版权问题,请联系删除!
  • 本篇文章主要给大家讲述了Java中ClassLoader类加载的原理以及用法总结,一起学习下。
  • 文章目录1、Java虚拟机的类加载机制概述2、Java虚拟机中的类加载器2.1、查看类加载加载的路径2.1.1、查看启动类加载器2.1.2、查看扩展类加载器3、类加载器之间的关系3.1、每个类加载器都有一个加载器3.2、父加载...
  • 深入理解Java类加载器(ClassLoader)

    万次阅读 多人点赞 2017-06-26 09:34:08
    采用双亲委派模式的是好处是Java随着它的加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免的重复加载,当父亲已经加载了该时,就没有必要子ClassLoader加载一次。其次是考虑到安全因素...
  • 一般个ClassLoader只能加载一个同名的,但是老大想我做一向下兼容检测的程序. 遇到一问题,无法加载两同名的ClassLoader. 请问下如何创建两个不同ClassLoader
  • NULL 博文链接:https://ldbjakyo.iteye.com/blog/1046984
  • 主要介绍了Java类加载ClassLoader用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • ClassLoader种类: -BootClassLoader -PathClassLoader -DexClassLoader -BaseDexClassLoader BootClassLoader: 加载framework 层的字节码文件。 PathClassLoader: 加载安装应用里面的字节码文件。 DexClassLoader:...
  • 主要为大家解析了ClassLoader类加载源码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • classLoader类加载器如何加载class

    千次阅读 2018-05-05 11:54:35
    ClassLoader翻译过来就是类加载器,普通的java开发者其实用到的不多,但对于某些框架开发者来说却非常常见。理解ClassLoader加载机制,也有利于我们编写出更高效的代码。ClassLoader的具体作用就是将class文件加载...
  • 如何加载任意文件夹下的Java对象?如何使用不同工程下的Java对象? 文章目录预备知识实现步骤代码参考 ...通过一个类的全限定名(包名+类名)来获取定义此类的二进制字节流。 将这字节流所代表的静态存储结构...
  • 主要给大家介绍了关于Java运行时环境之ClassLoader类加载机制的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • Java的加载是双亲委派模型,个类(即全类名相同),默认情况下是只会被一个类加载器加载一次的。 Java的加载器分别有: bootstrap:主要加载rt.jar ext classloader:主要加载jre/ext/*.jar app classloader...
  • ClassLoader类加载过程

    千次阅读 2018-08-24 19:16:23
    类加载器 负责将生成的.class文件加载到内存中,并生成对应的Class对象 类加载器的最终产物就是堆内存中的class对象 三种类加载器: BootStrapClassLoader:引导类加载器,负责java核心加载 ...
  • ClassLoader动态加载类 简单示例 包装tank.test; 导入java.util.Scanner; 导入tank.classloader.ClassLoaderManager; 导入tank.classloader.MyClassLoaderManager; 导入tank.classloader.SystemClassLoaderManager...
  • 类加载不同相同

    万次阅读 2019-05-30 17:03:44
    今天看《深入理解Java虚拟机》的7.4.1节时,实现一个书本上的代码,与书本结果不一致,不知道是因为JDK版本问题(本人JDK1.8)还是其他原因。 先给出代码: package com.bjtu.ClassLoader; import java.io....
  • java 的 ClassLoader 类加载机制详解

    千次阅读 2017-03-21 11:28:30
    一个程序要运行,需要经过一个编译执行的过程: Java的编译程序就是将Java源程序 .java 文件 编译为JVM可执行代码的字节码文件 .calss 。Java编译器不将对变量和方法的引用编译为数值引用,也不确定程序执行过程中...
  • 同一个Class,如果是由不同类加载器实例加载的,那么它们的类型是不相同的
  • Android使用ClassLoader加载类

    千次阅读 2016-08-10 15:37:52
    Android使用ClassLoader加载类 .ClassLoader继承关系   BootClassLoader处于双亲委派机制加载链的顶端,负责虚拟机内部的的加载。 一般应用里(除了我们自定义的加载器并且不遵守双亲委派机制)的...
  • 本篇文章主要介绍了详解Android类加载ClassLoader,小编觉得挺不错的,现在分享给大家,也给大家做参考。一起跟随小编过来看看吧
  • ClassLoader——类加载

    2020-10-26 16:45:37
    1.根类加载器(BootStrap ClassLoader) JVM本地代码(c/c++)实现,加载(%JAVA_HOME%\jre\lib)。是Java类加载层次中最顶层的类加载器,负责加载JDK中的核心类库,如:rt.jar、resources.jar、charsets.jar等。 2....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 138,394
精华内容 55,357
关键字:

不同classloader加载同一个类