概述

首先还是把问题抛给大家,这个问题也是我厂同学在做一个性能分析产品的时候碰到的一个问题。

同一个类加载器对象是否可以加载同一个类文件多次并且得到多个Class对象而都可以被java层使用吗

请仔细注意上面的描述里几个关键的词

  • 同一个类加载器:意味着不是每次都new一个类加载器对象,我知道有些对类加载器有点理解的同学肯定会想到这点。我们这里强调的是同一个类加载器对象去加载。

  • 同一个类文件:意味着类文件里的信息都一致,不存在修改的情况,至少名字不能改。因为有些同学会钻空子,比如说拿到类文件然后修改名字啥的,哈哈。

  • 多个Class对象:意味着每次创建都是新的Class对象,并不是返回同一个Class对象。

  • 都可以被java层使用:意味着Java层能感知到,或许对我公众号关注挺久的同学看过我的一些文章,知道我这里说的是什么,不知道的可以翻翻我前面的文章,这里卖个关子,不直接告诉你哪篇文章,稍微提示一下和内存GC有关。

那接下来在看下面文章之前,我觉得你可以先思考一个问题,

同一类加载器对象是否可加载同一类文件多次且得到多个不同的Class对象(单选)
A.不知道 B.可以 C.不可以

虽然有些标题党的意思,不过我觉得标题里的99.99%说得应该不夸张,这个比例或许应该更大,不过还是请认真作答,不要随便选,我知道肯定有人会随便选的,哈哈。

正常的类加载

这里提正常的类加载,也是我们大家理解的类加载机制,不过我稍微说得深一点,从JVM实现角度来说一下。在JVM里有一个数据结构叫做SystemDictonary,这个结构主要就是用来检索我们常说的类信息,这些类信息对应的结构是klass,对SystemDictonary的理解,可以认为就是一个Hashtable,key是类加载器对象 类的名字,value是指向klass的地址。这样当我们任意一个类加载器去正常加载类的时候,就会到这个SystemDictonary中去查找,看是否有这么一个klass可以返回,如果有就返回它,否则就会去创建一个新的并放到结构里,其中委托类加载过程我就不说了。

那这么一说看起来不可能出现同一个类加载器加载同一个类多次的情况。

正常情况下也确实是这样的。

奇怪的现象

然而我们从java进程的内存结构里却看到过类似这样的一些现象,以下是我们性能分析产品里的部分截图

在这个现象里,名字为java.lang.invoke.LambdaForm$BMH的类有多个,并且其类加载器都是BootstrapClassLoader,也就是同一个类加载器居然加载了同一个类多次。这是我们的分析工具有问题吗?显然不是,因为我们从内存里读到的就是这样的信息。

现象模拟

上面的这个现象看起来和lambda有一定关系,不过实际上并不仅仅lambda才有这种情况,我们可以来模拟一下

   public static void main(String args[]) throws Throwable {Field f = Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible(true);Unsafe unsafe = (Unsafe) f.get(null);String filePath = "/Users/nijiaben/AA.class";byte[] buffer =getFileContent(filePath);Class<?> c1 = unsafe.defineAnonymousClass(UnsafeTest.class, buffer, null);Class<?> c2 = unsafe.defineAnonymousClass(UnsafeTest.class, buffer, null);System.out.println(c1 == c2);}

上述代码其实就是通过Unsafe这个对象的defineAnonymousClass方法来加载同一个类文件两遍得到两个Class对象,最终我们输出为false。这也就是说c1和c2其实是两个不同的对象。

因为我们的类文件都是一样的,也就是字节码里的类名也是完全一样的,因此在jvm里的类对象的名字其实也都是一样的。不过这里我要提一点的是,如果将c1和c2的名字打印出来,会发现有些区别,分别会在类名后面加上一个/hashCode值,这个hash值是对应的Class对象的hashCode值。这个其实是JVM里的一个特殊处理。

另外你无法通过java层面的其他api,比如Class.forName来获取到这种class,所以你要保存好这个得到的Class对象才能后面继续使用它。

defineAnonymousClass的解说

defineAnonymousClass这个方法比较特别,从名字上也看得出,是创建了一个匿名的类,不过这种匿名的概念和我们理解的匿名是不太一样的。这种类的创建通常会有一个宿主类,也就是第一个参数指定的类,这样一来,这个创建的类会使用这个宿主类的定义类加载器来加载这个类,最关键的一点是这个类被创建之后并不会丢到上述的SystemDictonary里,也就是说我们通过正常的类查找,比如Class.forName等api是无法去查到这个类是否被定义过的。因此过度使用这种api来创建这种类在一定程度上会带来一定的内存泄露。

那有人就要问了,看不到啥好处,为啥要提供这种api,这么做有什么意义,大家可以去了解下JSR292。jvm通过InvokeDynamic可以支持动态类型语言,这样一来其实我们可以提供一个类模板,在运行的时候加载一个类的时候先动态替换掉常量池中的某些内容,这样一来,同一个类文件,我们通过加载多次,并且传入不同的一些cpPatches,也就是defineAnonymousClass的第三个参数, 这样就能做到运行时产生不同的效果。

主要是因为原来的JVM类加载机制是不允许这种情况发生的,因为我们对同一个名字的类只能被同一个类加载器加载一次,因而为了能支持动态语言的特性,提供类似的api来达到这种效果。

总结

总的来说,正常情况下,同一个类文件被同一个类加载器对象只能加载一次,不过我们可以通过Unsafe的defineAnonymousClass来实现同一个类文件被同一个类加载器对象加载多遍的效果,因为并没有将其放到SystemDictonary里,因此我们可以无穷次加载同一个类。这个对于绝大部分人来说是不太了解的,因此大家在面试的时候,你能讲清楚我这文章里的情况,相信是一个加分项,不过也可能被误伤,因为你的面试官也可能不清楚这种情况,不过你可以告诉他我这篇文章,哈哈,有收获请帮忙点个好看,并分享出去,感谢。
点击标题查看完整问答

推荐阅读震惊!线上四台机器同一时间全部 OOM,到底发生了什么?
推荐阅读 不重启JVM,替换掉已经加载的类,偷天换日?

95%的人都会答错的类加载的问题相关推荐

  1. java一个类多次加载_据说99.99%的人都会答错的类加载问题

    概述 首先还是把问题抛给大家,这个问题也是我厂同学在做一个性能分析产品的时候碰到的一个问题. 同一个类加载器对象是否可以加载同一个类文件多次并且得到多个Class对象而都可以被java层使用吗? 请仔 ...

  2. 支付宝二面:使用 try-catch 捕获异常会影响性能吗?大部分人都会答错!

    不知道从何时起,传出了这么一句话:Java中使用try catch 会严重影响性能. 然而,事实真的如此么?我们对try catch 应该畏之如猛虎么? 一.JVM 异常处理逻辑 Java 程序中显式 ...

  3. 面试官:为什么要尽量避免使用 IN 和 NOT IN?大部分人都会答错!

    作者:Hydor 来源:https://www.cnblogs.com/hydor/p/5391556.html WHY? IN 和 NOT IN 是比较常用的关键字,为什么要尽量避免呢? 1.效率低 ...

  4. 如果要存 IP 地址,用什么数据类型比较好?大部人都会答错!

    欢迎关注方志朋的博客,回复"666"获面试宝典 来源:blog.csdn.net/mhmyqn/article/details/48653157 在看高性能MySQL第3版(4.1 ...

  5. 90% 的人都会答错的面试题 == 和 equals 的区别

    == 和 equals 的区别是什么? == 解读 对于基本类型和引用类型,== 的作用效果是不同的,如下所示: 基本类型:比较的是值是否相同: 引用类型:比较的是引用是否相同: 代码示例: Stri ...

  6. 德国80%的统计学教授都会答错的6个与P值有关的问题!

    欢迎关注"R语言和统计"~~ 在2022年的第2天,小编阅读了一篇发表于2002年关于P值的一项问卷调查研究 [1],作者在6所德国大学中邀请了3组不同的受试者,分别为: 心理学专 ...

  7. 99%的人都会用错或者不会看的N折验证

    做实际比赛和机器学习项目, 很多朋友都会经常遇到线下线上不一致的情况,排除一些特殊的情况,例如问题没法预测,不存在潜在pattern的情况等,我敢说99%的人都不会看N折验证的结果. 没错,是真的! ...

  8. 支付宝二面:使用 try-catch 捕获异常会影响性能吗?90%都会答错

    不知道从何时起,传出了这么一句话:Java中使用try catch 会严重影响性能. 然而,事实真的如此么?我们对try catch 应该畏之如猛虎么? 一.JVM 异常处理逻辑 Java 程序中显示 ...

  9. 据说有99%的人都会做错的面试题

    这道题主要考察了面试者对浮点数存储格式的理解.另外,请不要讨论该题本身是否有意义之类的话题.本题只为了测试面试者相关的知识是否掌握,题目本身并没有实际的意义. 下面有6个浮点类型变量,其中前三个是fl ...

  10. 面试必考题:基本95%的面试都会被问到的?

    这个问题,基本95%的面试都会遇到.究竟面试官想要知道什么呢? 让我们回到这个面试场景来看看. "说一下你印象最深的bug" 你的脑子里拼命的回想过去遇到的印象深刻或有价值的bug ...

最新文章

  1. python3教程-终于清楚python3详细教程
  2. python asyncore
  3. hibernate持久化对象
  4. 1.5 为什么正则化可以减少过拟合?
  5. 实现视图示例_AngularJS控制器,范围和视图教程示例
  6. SQL语句Not IN优化方案
  7. 容斥原理+简单博弈论(找个时间补充一下sg,希望我记得)
  8. QTP教程01 - Add-in Manager插件介绍
  9. Python实现:超分子化学的建模------如何操控客体分子穿过主体分子和计算该过程能量变化(高斯(Gauss)输入文件为例,一键批量处理)
  10. HTMLTestRunnerNew 测试报告详解
  11. 智慧校园信息化建设方案
  12. protocal buffer repeate 关键字
  13. 软件测试之TCP、UPD协议详解
  14. CentOS 8系统时间校准
  15. Linux服务器安装云锁
  16. angular- Directive
  17. Java-Tomcat的请求参数解析分析
  18. echarts中折线图、柱状图之间的转换
  19. 插上耳机没声音 程序包ID:Audioplaybackdiagnostic 错误代码:0x80070002 源:引擎 上下文:升级
  20. 各种抠图动态图片_抠图动画

热门文章

  1. 多线程总结--小码哥java
  2. 谷仓(某校老师原创题)
  3. LOJ#2155. 「POI2011 R1」同谋者 Conspiracy
  4. Springboot 整合 阿里云消息队列RabbitMQ版服务
  5. 医学影像中的基础知识
  6. iOS创建pch文件
  7. html td 长度适配,table的td宽度自适应
  8. https://www.jb51.net/article/146628.htm
  9. Rdd,DataFrame和DataSet的区别
  10. Mac常见问题:如何使用文件保险箱加密 Mac 上的启动磁盘!