jvm类加载机制和类加载器

在许多开发人员中,类加载器是Java语言的底层,并且经常被忽略。 在ZeroTurnaround上 ,我们的开发人员必须生活,呼吸,饮食,喝酒,并且几乎与类加载器保持亲密关系,以生产JRebel技术,该技术在类加载器级别进行交互以提供实时运行时类重新加载,从而避免了冗长的重建/重新包装/重新部署周期。
这是我们从类加载器中学到的一些知识,其中包括一些调试技巧,这些技巧将有望为您节省时间和将来的总办事处。

一个类加载器只是一个普通的java对象

是的,这并不聪明,除了JVM中的系统类加载器之外,类加载器只是一个Java对象! 这是一个抽象类ClassLoader,可以由您创建的类实现。 这是API:

public abstract class ClassLoader {public Class loadClass(String name);protected Class defineClass(byte[] b);public URL getResource(String name);public Enumeration getResources(String name);public ClassLoader getParent()}

看起来很简单,对吧? 让我们逐个方法看一下。 中心方法是loadClass,它仅使用String类名,然后返回实际的Class对象。 如果您以前使用过类加载器,则这是最熟悉的方法,因为它是日常编码中使用最多的方法。 defineClass是JVM中的最终方法,该方法从文件或网络上的某个位置获取字节数组,并产生相同的结果(即Class对象)。

类加载器还可以从类路径中找到资源。 它的工作方式与loadClass方法类似。 有几种方法,getResource和getResources,它们返回一个URL或URL的枚举,这些URL或URL的枚举指向资源,该资源表示作为输入传递给该方法的名称。

每个类加载器都有一个父级。 getParent返回与Java继承无关的classloader父类,而是一个链表样式的连接。 稍后我们将对此进行更深入的研究。

类加载器是惰性的,因此仅在运行时请求类时才加载类。 类是由调用该类的资源加载的,因此,在运行时,一个类可以由多个类加载器加载,具体取决于从何处引用它们,以及哪个类加载器加载了引用的类。 让我们看一些代码。

public class A {public void doSmth() {B b = new B();b.doSmthElse();}}

在这里,我们有类A在其方法范围内调用类B的构造函数。 在幕后这是正在发生的事情

A.class.getClassLoader().loadClass(“B”);

最初加载类A的类加载器被调用以加载类B。

类加载器是分层的,但是像孩子一样,他们并不总是问父母

每个类加载器都有一个父类加载器。 当一个类加载器被要求提供一个类时,它通常会直接转到父类加载器,首先调用loadClass,而后者又可能会询问它的父类,依此类推。 如果要求两个具有相同父级的类加载器加载同一类,则父级将只执行一次。 当两个类加载器分别加载同一个类时,这将变得很麻烦,因为这可能会导致问题,我们将在后面讨论。

当设计JEE规范时,Web类加载器被设计为以相反的方式工作-很棒。 让我们看一下下图作为示例。


模块WAR1具有自己的类加载器,并且更愿意自行加载类,而不是委托给其父级(由App1.ear定义的类加载器)。 这意味着不同的WAR模块(例如WAR1和WAR2)无法看到彼此的类。 App1.ear模块具有自己的类加载器,并且是WAR1和WAR2类加载器的父级。 当WAR1和WAR2类加载器需要在层次结构中委派请求时,即WAR类加载器范围之外需要一个类时,它们将使用App1.ear类加载器。 实际上,WAR类会覆盖同时存在的EAR类。 最后,EAR类加载器的父级是容器类加载器。 EAR类加载器会将请求委派给容器类加载器,但是它的执行方式与WAR类加载器不同,因为EAR类加载器实际上更喜欢委托而不是本地类。 如您所见,这变得非常繁琐,并且与普通的JSE类加载行为不同。

平面类路径

我们讨论了系统类加载器如何通过类路径查找已请求的类。 该类路径可能包含目录或JAR文件,查找它们的顺序实际上取决于您使用的JVM。 您在类路径上可能需要该类的多个副本或版本,但是您将始终在类路径上找到该类的第一个实例。 本质上,这只是资源列表,这就是为什么将其称为扁平资源。 结果,在查找资源时,遍历类路径列表通常会比较慢。

当使用相同类路径的应用程序要使用类的不同版本时,可能会发生问题,让我们以Hibernate为例。 当类路径上存在两个版本的Hibernate JAR时,一个版本不能比一个应用程序的版本路径在另一个应用程序的路径上更高,这意味着两个版本都必须使用相同的版本。 解决此问题的一种方法是使用所有必需的库来膨胀应用程序(WAR),以便它们使用其本地资源,但这会导致大型应用程序难以维护。 欢迎来到JAR地狱! OSGi在此处提供了一种解决方案,因为它允许对JAR文件或捆绑软件进行版本控制,从而提供了一种机制,允许连接到特定版本的JAR文件,从而避免了扁平类路径问题。

如何调试类加载错误?

NoClassDefFoundError / ClassNotFoundException / ClassNoDefFoundException?

因此,您遇到了上述错误/异常。 好吧,这个班级真的存在吗? 不要在IDE中打扰,因为那是您编译类的地方,它必须在那里,否则您将获得编译时异常。 这是一个运行时异常,因此在运行时我们要查找它说我们缺少的类……但是您从哪里开始呢? 考虑下面的代码…

Arrays.toString((((URLClassLoader) Test.class.getClassLoader()).getURLs()));

此代码返回Test正在使用的类加载器的类路径上所有jar和目录的数组列表。 现在,我们可以看到神秘类应该存在的JAR或位置实际上是在类路径上。 如果不存在,请添加! 如果确实存在,请检查JAR /目录以确保您的类确实存在于该位置,并在缺少该类时添加它。 这是导致此错误情况的两个典型问题。

NoSuchMethodError / NoSuchFieldError / AbstractMethodError / IllegalAccessError吗?

现在变得越来越有趣了! 这些都是IncompatibleClassChangeError的所有子类。 我们知道类加载器已经找到了想要的类(按名称),但是显然它没有找到正确的版本。
在这里,我们有一个称为Test的类,它正在调用另一个类Util,但是BANG –我们遇到了异常! 让我们看一下要调试的下一个代码片段:

Test.class.getClassLoader().getResource(Util.class.getName().replace('.', '/') + ".class");

我们在Test类的类加载器上调用getResource。 这将向我们返回Util资源的URL。 请注意,我们已替换了“。” 带有“ /”,并在字符串末尾添加“ .class”。 这会将我们正在寻找的类的包和类名(从类加载器的角度来看)更改为文件系统上的目录结构和文件名-简洁。 这将向我们显示我们已加载的确切类,并且可以确保它是正确的版本。 我们可以在命令提示符下在类上使用javap -private来查看字节码并检查实际存在的方法和字段。 您可以轻松地查看该类的结构,并验证是您还是疯了的Java运行时! 相信我,在一个或另一个阶段,您都会同时问这两个问题,几乎每次都会是您!

LinkageError / ClassCastException / IllegalAccessError

如果两个不同的类加载器加载同一个类,并且它们尝试进行交互,则可能会发生这种情况。 是的,现在有点毛了。 这可能会导致问题,因为我们不知道它们是否将从同一位置加载类。 怎么会这样 让我们看下面的代码,它们仍然在Test类中:

Factory.instance().sayHello();

该代码看起来非常干净和安全,尚不清楚如何从此行出现错误。 我们正在调用静态工厂方法来获取Test类的实例,并在其上调用方法。 让我们看一下该支持图像,以显示引发异常的原因。


在这里,我们可以看到一个Web类加载器(加载了Test类)将首选本地类,因此,当它引用一个类时,将尽可能由Web类加载器加载。 到目前为止,还算简单。 Test类使用Factory类来获取Util类的实例,这在Java中是很典型的做法,但是WAR中不存在Factory类,因为它是一个外部库。 这是没有问题的,因为Web类加载器可以委托给共享类加载器,后者可以看到Factory类。 请注意,共享类加载器现在正在加载它自己的Util类版本,因为当Factory实例化该类时,它使用了共享类加载器(如前面的第一个示例所示)。 Factory类将Util对象(由共享类加载器创建)返回给WAR,WAR然后尝试使用该类,并将该类有效地强制转换为同一类的潜在不同版本(Web类加载器可见的Util类) )。 繁荣!

我们可以在两个地方(Factory.instance()方法和Test类)中运行与以前相同的代码,以查看每个Util类从何处加载。

Test.class.getClassLoader().getResource(Util.class.getName().replace('.', '/') + ".class"));

希望这可以使您对类加载的世界有所了解,而不是不了解类加载器,现在可以带着恐惧和不确定性来欣赏它! 感谢您的阅读并将其制作到最后。 我们都希望您从ZeroTurnaround祝您圣诞快乐,新年快乐! 编码愉快!

参考: 在JVM的底层– Java出现日历博客中来自JCG合作伙伴 Simon Maple的类加载器 。

翻译自: https://www.javacodegeeks.com/2012/12/under-the-jvm-hood-classloaders.html

jvm类加载机制和类加载器

jvm类加载机制和类加载器_在JVM之下–类加载器相关推荐

  1. JVM类加载机制详解(一)JVM类加载过程

    2019独角兽企业重金招聘Python工程师标准>>> 首先Throws(抛出)几个自己学习过程中一直疑惑的问题: 1.什么是类加载?什么时候进行类加载? 2.什么是类初始化?什么时 ...

  2. java类加载机制为什么双亲委派_[五]类加载机制双亲委派机制 底层代码实现原理 源码分析 java类加载双亲委派机制是如何实现的...

    Launcher启动类 本文是双亲委派机制的源码分析部分,类加载机制中的双亲委派模型对于jvm的稳定运行是非常重要的不过源码其实比较简单,接下来简单介绍一下我们先从启动类说起有一个Launcher类 ...

  3. jvm与jit编译器的区别_了解jvm和jit编译器的第1部分

    jvm与jit编译器的区别 Hello people!! 大家好!! I can see that the Java community is growing a lot but there are ...

  4. python简单装饰器_简单介绍Python装饰器(一)

    装饰器的作用 相信大家在 探索过程中已经了解装饰器的作用,也有很多花里胡哨的介绍. 这次小冰也来讲解一下关于Python装饰器的一些小知识. 它的作用: 性能测试 日志 安全验证 ...... 相信大 ...

  5. 什么是python装饰器_深入理解 Python 装饰器

    作者简介 曾凡伟,携程信息安全部高级安全工程师,2015年加入携程,主要负责安全自动化产品的设计和研发,包括各类扫描器.漏洞管理平台.安全 SaaS 平台等. Python 是一门追求优雅编程的语言, ...

  6. 打开文件管理器_会声会影影片配置文件管理器

    在会声会影菜单栏的"设置"中有"影片配置文件管理器"这一功能,下面和大家聊一下这个功能.不管是"影片配置文件管理器"还是"项目属性 ...

  7. java8默认内存收集器_使用正确的垃圾收集器将Java内存使用量降至最低

    java8默认内存收集器 大小对于软件至关重要. 很明显,与大的整体方法相比,在微服务体系结构中使用小片段具有更多优势. 最新的Java版本的Jigsaw有助于分解旧应用程序或从头开始构建新的云原生应 ...

  8. python实现视频播放器_对目前的视频播放器不满意?教你用Python做一个视频播放器...

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理. 以下文章来源于Charles的皮卡丘 ,作者白露未晞me Python爬虫.数据分析.网站开 ...

  9. java编写视频播放器_基于Java的视频播放器可以足够快吗?

    第三次谷歌搜索"视频播放java"似乎相关: http://blog.pirelenito.org/2008/08/java-movie-playback-jogl-fobs4jm ...

最新文章

  1. CMU最新《深度学习》2022春季课程,Ruslan Salakhutdinov教授主讲
  2. centos 6.x 升级内核
  3. jquery1.6中的.prop()和.attr()异同
  4. php处理json请求(php获取post请求的json数据的实现方法)
  5. Linux文件IO深入剖析
  6. 数据科学自动化_数据科学会自动化吗?
  7. sqlserver迁移数据到mysql_SQLServer数据库之将ABP的数据库从SQLSERVER迁移到MySql
  8. 设置最大值_变频器用远传压力表控制恒压供水参数设置
  9. idea修改代码后不重启项目_使用DevTool实现SpringBoot项目热部署
  10. Graph + AI 2021全球峰会圆满落幕 TigerGraph企业版3.2发布
  11. 财富信息不需要传userID后台会根据保存的session提供数据
  12. 特斯拉回应Model 3新车无USB接口:芯片短缺
  13. IOCP扩展方法AcceptEx, DisconnectEx, GetAcceptExSockaddr用法示例
  14. eclipse安装反编译工具
  15. msiafterburner并行配置不正确_dubbo常用配置及使用场景
  16. r9270公版bios_换个BIOS再来一次
  17. 杭电Oj刷题(2009)
  18. IOS 应用安全测试内容
  19. 怎么样恢复移动硬盘格式化的数据呢?
  20. Android中Gson使用,flutter调用原生sdk

热门文章

  1. Spring-SpringMVC父子容器
  2. Linux下查找命令
  3. javaWeb服务详解(含源代码,测试通过,注释)
  4. 2018蓝桥杯省赛---java---C---1(哪天返回)
  5. android merge的作用,Android学习手记-merge
  6. php accesscontrolalloworigin,设置Access-Control-Allow-Origin实现跨域访问
  7. jvm(6)-Class字节码文件结构总结
  8. spring jpa 流式_从响应式Spring Data存储库流式传输实时更新
  9. java策略设计模式_Java中的策略设计模式
  10. azure blob_使用Azure Blob存储托管Maven工件