在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父类,而是一个链表样式的连接。 稍后我们将对此进行更深入的研究。

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

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中是很典型的做法,但是Factory类在WAR中并不存在,因为它是一个外部库。 这是没有问题的,因为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

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

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

相关文章

matplotlib绘制饼状图

源自http://blog.csdn.net/skyli114/article/details/77508430?ticketST-41707-PzNbUDGt6R5KYl3TkWDg-passport.csdn.net pyplot使用plt.pie()来绘制饼图 1 import matplotlib.pyplot as plt 2 labels frogs, hogs, dogs, logs 3 sizes 15, 20, 45, 10 # [15,20,45,10…

自适应宽度元素单行文本省略用法探究

单行文本省略是现代网页设计中非常常用的技术,几乎每个站点都会用到。单行文本省略适用于显示摘要信息的场景,如列表标题、文章摘要等。在响应式开发中,自适应宽度元素单行文本省略容易失效不起作用,对网页开发这造成困扰。因此&a…

P3390 【模板】矩阵快速幂

题目背景 矩阵快速幂 题目描述 给定n*n的矩阵A,求A^k 输入输出格式 输入格式: 第一行,n,k 第2至n1行,每行n个数,第i1行第j个数表示矩阵第i行第j列的元素 输出格式: 输出A^k 共n行,每行n个数&…

c#精彩编程200例百度云_永安市教育局被授予“人工智能编程教育试验区”

11月28日,“第二届人工智能与机器人教育大会青少年人工智能与编程教育主题论坛”在厦门召开。永安市教育局被中国教育发展战略学会人工智能与机器人教育专委会授予“人工智能编程教育试验区”牌匾,巴溪湾小学、西门小学、三中、一中附属学校、实验小学等…

Spring Data JPA和分页

让我们从支持分页的经典JPA方法开始。 考虑一个简单的域类–一个具有名字,姓氏的“成员”。 为了支持在成员列表上进行分页,JPA方法是支持一种查找器,该查找器将获取第一个结果(firstResult)的偏移量和要检索的结果&am…

Windows环境下安装、卸载Apache

安装Apache 服务 打开 Apcahe的目录 ,打开bin目录, 如:E:\wamp\Apache24\bin ,打开目录,Shift键 鼠标右键 , 点击 在此处打开命令窗口或者W快捷键直接到此处, 也可以Window键r,输入…

css清浮动

我们在平常做项目的时候,float这个css属性经常会用到。元素浮动会让元素脱离文档流,从而不能撑开父级的内容。今天我将展示常见的清除浮动的方法。 什么是浮动 浮动元素脱离文档流并且向左或者向右移动,直到浮动元素的边缘碰到父级框或者另…

DirectX11 学习笔记7 - 支持自由移动的摄像机

如今将又一次制定一个camera摄像机。能够自由移动。比方前进 后退,上游 下潜。 各个方向渲染之类的。 首先设置按键。 这个时候须要在 XWindow.h 里面 bool XWindow::frame() {//推断是否按下ESC键if(x_input->isKeyDown(VK_ESCAPE))return false;//假设A,S,D,W,…

腾讯吃鸡 android,腾讯吃鸡手游《光荣使命》正式上线:安卓/iOS不限号测试

IT之家11月29日消息 今天下午,腾讯首款百人战术竞技手游《光荣使命》在安卓、iOS双平台正式上线,开启全面测试。(官网下载:点此链接,双平台已开放下载。)该游戏采用第三人称射击视角,玩家化身参与“使命行动”军事演习…

lazada铺货模式的选品_lazada小白的运营难点→铺货与精细化运营的优劣势详解

lazada是铺货还是精细化经营第一种铺货铺货作为平台早期都是比较受欢迎的,平台的蛮荒期,成长期当中,铺货的商家是非常受欢迎的,因为平台需要更多SKU产品,去吸引买家,铺货这个时候是最好的也是能最快的成长起…

excel数据生成sql insert语句

excel数据生成sql insert语句 excel表格中有A、B、C三列数据,希望导入到数据库users表中,对应的字段分别是name,sex,age 。 在你的excel表格中增加一列,利用excel的公式自动生成sql语句,方法如下: 1、增加一列&#xf…

重载,覆盖,隐藏

转载于:https://www.cnblogs.com/jhcelue/p/7145525.html

荣耀鸿蒙系统开机动画,荣耀赵明:鸿蒙系统首发设备欲屏蔽开机广告

来源:硅谷分析狮余承东表示8月9日会发布鸿蒙系统,而从他透露的一些细节看,鸿蒙系统将首先运用在智慧屏终端上,其配合大屏幕和自研芯片(麒麟AI芯片,鸿鹄智慧显示芯片,凌霄WIFI芯片),将实现生态上…

Hamcrest包含匹配器

与Hamcrest 1.2相比 ,针对Matchers类的Hamcrest 1.3 Javadoc文档为该类的几种方法添加了更多文档。 例如,四个重载的contains方法具有更具描述性的Javadoc文档,如下面所示的两个比较屏幕快照所示。 尽管仅通过尝试就可以弄清楚“包含”匹配器…

函数 (四) 迭代器和生成器

一 迭代器 一 迭代的概念 #迭代器即迭代的工具,那什么是迭代呢?#迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值 while True: #只是单纯地重复,因而不是迭代print(>) l[1,2,3]…

进阶-JMS 知识梳理

JMS 一、 概述与介绍 ActiveMQ 是Apache出品,最流行的、功能强大的即时通讯和集成模式的开源服务器。ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现。提供客户端支持跨语言和协议,带有易于在充分支持JMS 1.1和1.4使用J2EE企业集成模式…

android蓝牙pair,Android向更多蓝牙设备开放Fast Pair功能 配对更轻松了

原标题:Android向更多蓝牙设备开放Fast Pair功能 配对更轻松了 来源:cnBeta.COM蓝牙是一项应用非常广泛的无线技术,在无线音频配件、智能手表和智能家电中都广泛使用。不过蓝牙设备的配对体验并不优秀,而且无法实现跨平台的一致性…

python绘制帕累托图

python绘制帕累托图代码 1 import pandas as pd2 import matplotlib.pyplot as plt3 plt.rcParams[font.sans-serif][SimHei]#表示可以显示中文4 plt.rcParams[axes.unicode_minus]False#表示可以正常显示正负号5 datapd.read_csv(catering_dish_profit.csv,index_coltype)6 pr…

html5 测评游戏,暗黑之王评测:HTML5游戏铸就最华丽ARPG冒险

由白鹭时代(Egret Technology)与比悦科技联手推出的重度大型HTML5游戏《暗黑之王》,一款典型的ARPG手游,其HTML5版本推出以来,获得了来自业界、玩家和媒体的大量关注。其丰富的游戏内容和玩法,加上卓越的游戏性能表现,…

搞定flex布局

这几种方式的搭配使用可以轻松搞定 PC 端页面的常见需求,比如实现水平居中可以使用 margin: 0 auto,实现水平垂直同时居中可以如下设置:.dad {position: relative; } .son {position: absolute;margin: auto;top: 0;right: 0;bottom: 0;left…