jvm类加载机制和类加载器_在JVM之下–类加载器

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类加载机制和类加载器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/350165.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

光源时间_您的国美备战时间已不足一个月!绝密国美考点、考场、光源分析送上!...

中国美术学院2020年本科招生考试报名公告,终于在前几天公布了!今年国美一共设置了三个考点,报名也已经正式开启啦!考点设置杭州考点:不设报考人数上限,接受全国所有省份考生报考(含广东省、河南省)深圳考点…

ltp-ddt eth_iperf_tcp iperf dualtest遇到的问题

ltp-ddt eth_iperf_tcp server端:iperf -s -i 5 -w 1M client端将ddt的核心代码抠出来: iperf -c 1921.68.40.41 -m -M 1500 -f M -d -t 60 -w 8K 手动运行三次后,server端挂了,client端报错:connect failed: Connecti…

beam search算法

转载 https://blog.csdn.net/xyz1584172808/article/details/89220906 https://blog.csdn.net/batuwuhanpei/article/details/64162331 https://www.zhihu.com/question/54356960 beam search算法 在看论文Sequence to Sequence Learning with neural networks时看到了beam …

微信支付遇到的坑--签名错误

遇到签名错误,因为微信没有给出具体错误信息,所以我们只能自己排查。 下面是我整理出可能导致签名错误的情况: 1.可以先去微信公众平台用支付接口调试工具验证签名方法是否有问题 https://pay.weixin.qq.com/wiki/tools/signverify/2.如果签名…

Apache Kafka / Spark流系统的性能调优

电信行业的实际案例研究 调试实际的分布式应用程序可能是一项艰巨的任务。 至少在一开始,最常见的Google搜索并没有什么用。 在这篇博客文章中,我将详细介绍如何将Apache Kafka / Spark Streaming / Apache Ignite应用程序加速近十倍,并将开…

mysql 8服务配置文件my.ini 不生效_2、mysql的启动选项和配置文件介绍

mysql服务端可以指定允许同时接入的客户端数量max-connections、客户端与服务器的通信方式、表的默认存储引擎default_storage_engine等等设置项,使得我们可以更加灵活地使用它。进入正题我们讲的是基于linux系统的,本文我们主要讲一下:1、有…

数学中常见的maxmin,min max

数学中常见的maxmin,min max 1、数学中公式(1)与公式(2)含义是不同的 (1) (2) 公式(1)的直接理解,先找出每个班里的身高最高的同学,再由这些同学中挑出身高最低的;同理,公式(2)的直接理解,先找出每个…

Mac入门--Apache/Php/Mysql的开启关闭

注意:mac自带apache环境和PHP环境 一 php服务的启动和关闭 php-fpm 启动 sudo php-fpm php-fpm 关闭 1 查看php-fpm端口是否在被php-fpm进程 netstat -an | grep 9000 查看进程pid sudo lsof -i:9000 杀死进程 sudo kill -9 [pid] 或者 sudo killall php-fpm 2 查看…

method=post 怎么让查看源代码看不到_网上文档无法复制怎么办?试试这几个方法!...

从网上复制资料很简单吗?平常在网上查找资料的时候,总是会有一些文档无法下载、甚至有时连复制都不行。这可怎么办?别急,今天我们就来介绍一些网页复制的方法,帮助大家更好地收集资料。一:复制到Word剪贴板…

python 加载mat文件

python 加载mat文件 直接上程序 import scipy.io as sciomat_path ./USPS_vs_MNIST.matload_mat scio.loadmat(mat_path) # load_mat为字典类型, <class dict> print(type(load_mat)) # 访问load_mat即为访问字典 X_src load_mat[X_src] # 这个X_src为numpy类型&…

[Sdoi2010] 地精部落

F. 地精部落 题目描述 传说很久以前&#xff0c;大地上居住着一种神秘的生物&#xff1a;地精。 地精喜欢住在连绵不绝的山脉中。具体地说&#xff0c;一座长度为 N 的山脉 H可分 为从左到右的 N 段&#xff0c;每段有一个独一无二的高度 Hi&#xff0c;其中Hi是1到N 之间的正 …

怎么判断冠词用a还是an_英文写作常见错误学习笔记 | 冠词

写在前面的话在知乎中看到“英文学术论文写作&#xff0c;有什么好书可以推荐”话题中有人推荐了香港大学出版社Steve Hart写的《English Exposed》一书&#xff0c;从副标题“Common Mistakes Made by Chinese Speakers”可以看出书是针对中国人常犯的错误而写的&#xff0c;作…

工厂模式 构建者模式_实践中的构建者模式

工厂模式 构建者模式我将不深入讨论该模式&#xff0c;因为已经有大量的文章和书籍对此进行了详细的解释。 相反&#xff0c;我将告诉您为什么以及何时应该考虑使用它。 但是&#xff0c;值得一提的是&#xff0c;这种模式与《 四人帮》一书中介绍的模式有些不同。 虽然原始模式…

python 反复访问迭代器iter,反复使用next

python 反复访问迭代器iter&#xff0c;反复使用next test [1, 2, 3]# 定义迭代器 iter_test iter(test)# 使用for循环访问迭代器 # 由于next()只向前走&#xff0c;所以当跌代到test最后一个值3之后&#xff0c;就会爆发异常StopIteration # 这里使用了一个try语句&#xf…

PPT转PDF方法

引用&#xff1a; using Microsoft.Office.Interop.PowerPoint;方法&#xff1a; ///<summary> /// PPT转PDF ///</summary> ///<param name"sourcePath">源文件路径</param> ///<param name"targetPath&qu…

打印容器_3D打印:增材点阵结构在压力容器优化设计中的应用

“增材制造是未来制造业的发展趋势&#xff0c;其优势显而易见&#xff0c;它可以实现传统加工工艺难以制造的设计&#xff0c;比如复杂薄壁结构、点阵结构、一体化结构等。其中&#xff0c;点阵结构作为一种新型的轻量化结构&#xff0c;具有良好的比刚度、比强度等力学性能。…

Java平台模块系统公众审查未能通过

在过去的几周里&#xff0c;Java世界中的戏剧&#xff0c;阴谋和政治活动异常高涨&#xff0c;最终在本周的JSR 376 Java平台模块系统公共评审投票中达到了顶峰。 Java模块化&#xff08;包括Java平台模块系统&#xff08;JPMS&#xff09;&#xff09;可以说是JDK 9最重要的部…

区间素数个数查询

给出一个闭区间的两端l和r&#xff0c;输出[l,r]中素数的个数。 输入格式: 两个整数l和r&#xff0c;之间用空格隔开&#xff08;1<l<r<10000&#xff09;。 输出格式: 输出一个结果。 输入样例: 2 10输出样例: 4 import java.util.Scanner;public class Main {static…

jtabel 遍历_使用抽象表模型获取JTable中选定的行

在下面的示例中&#xff0c;TableModel更新Set checked在执行setValueAt()..相邻模型JList侦听表的模型并显示当前选定的行号。该示例假定所选行数与行数相比较小。注意使用TreeSet&#xff0c;其迭代器保留了元素的自然顺序。import java.awt.Dimension;import java.awt.Event…

neo4j cypher_优化Neo4j Cypher查询

neo4j cypher上周&#xff0c;我花了很多时间来尝试优化大约20个使用实时系统数据执行的灾难性的Cypher查询&#xff08;36866ms至155575ms&#xff09;。 经过一番尝试和错误&#xff0c;以及来自Michael的大量投入&#xff0c;我能够大致确定对查询进行哪些操作才能使它们性能…