知识大全 类装入问题: 类装入和调试工具介绍

Posted 路径

篇首语:挨金似金,挨玉似玉。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 类装入问题: 类装入和调试工具介绍相关的知识,希望对你有一定的参考价值。

  类装入器负责把类装入 Java 虚拟机(JVM) 简单的应用程序可以用 Java 平台内置的类装入工具装入类 更复杂的应用程序则倾向于定义自己定制的类装入器 但是 不论使用哪种类装入器 在类装入过程中都可能发生许多问题 如果想避免这类问题 需要理解类装入的基本机制 当问题发生时 对于可用的诊断特性和调试技术的了解会有助于解决问题

  在这个系列的文章中 我们将深入研究类装入的问题 并用丰富的示例演示它们 这份介绍性的文章的第一节描述类装入的基础 第二节介绍一些 JVM 调试特性 系列中剩下的三篇文章将侧重于解决类装入异常 并演示一些可能会碰到的更复杂的类装入问题

  类装入基础

  这一节描述类装入的核心概念 为系列剩下的部分提供知识基础

  类装入器委托

  类装入器委托模型 是把装入请求相互传给对方的类装入器图 引导 类装入器是这个图的根 用单一委托父类 创建类装入器 并在以下位置寻找类

缓存(Cache) 父类(Parent) 自己(Self)

  类装入器首先判断要求它装入的类是否与过去装入的类相同 如果相同 就返回上次返回的类(即保存在缓存中的类) 如果不是 就把装入类的机会交给父类 这两步递归地以深度优先的方式重复 如果父类返回 null(或抛出 ClassNotFoundException) 那么类装入器会在自己的路径中寻找类的源

  因为父类类装入器总是先得到装入类的机会 所以类装入器装入的类最靠近根 这意味着所有核心引导类都是由引导装入器装入的 这就保证装入了类(例如 java lang Object)的正确版本 这也可以让类装入器看到自己或父类或祖先装入的类 但是不能看到子女装入的类

  图 显示了三个标准的类装入器

  图 类装入器委托模型

  与其他类装入器不同 引导类装入器(也称作基本(primordial) 类装入器)不能由 Java 代码实例化 (通常是因为它是作为 VM 本身的一部分实现的 )这个类装入器可以从启动的类路径装入核心系统类 通常是位于 jre/lib 目录的 JAR 文件 但是不能用 Xbootclasspath 命令行选项修改这个类路径(稍后介绍)

  扩展(extension) 类装入器(也称作标准扩展 类装入器)是引导类装入器的一个孩子 它的主要职责是从扩展目录装入类 通常位于 jre/lib/ext 目录 这提供了简单地访问新扩展的能力 例如不同的安全扩展 不需要修改用户的类路径即可实现

  系统(system) 类装入器(也称作应用程序 类装入器)负责从 CLASSPATH 环境变量指定的路径装入代码 默认情况下 这个类装入器是用户创建的任何类装入器的父类 这也是 ClassLoader getSystemClassLoader() 方法返回的类装入器

  类路径选项

  表 总结了设置三个标准类装入器的类路径的命令行选项 命令行选项  解释 涉及的类装入器    Xbootclasspath:<用 ; 或 : 分隔的目录和 zip/JAR 文件>     设置引导类和资源的搜索路径  引导   Xbootclasspath/a:<用 ; 或 : 分隔的目录和 zip/JAR 文件>    把路径添加到启动类路径的末尾  引导   Xbootclasspath/p:<用 ; 或 : 分隔的目录和 zip/JAR 文件>    把路径添加到启动类路径的前面  引导   Dibm jvm bootclasspath=<用 ; 或 : 分隔的目录和 zip/JAR 文件>    这个属性的值被用作额外的搜索路径 它被插到 Xbootclasspath/p: 定义的值和启动类路径之间 启动类路径或者是默认值 或者是 Xbootclasspath: 选项定义的值  引导   Djava ext dirs=<用 ; 或 : 分隔的目录和 zip/JAR 文件>    指定扩展类和资源的搜索路径  扩展   cp or classpath <用 ; 或 : 分隔的目录和 zip/JAR 文件>    设置应用程序类和资源的搜索路径  系统   Djava class path=<用 ; 或 : 分隔的目录和 zip/JAR 文件>     设置应用程序类和资源的搜索路径  系统类装入的阶段

  类的装入实际上可以分成三个阶段 装入 链接和初始化

  虽然不是所有的问题 但至少大多数与类装入有关的问题都可以追溯到在这三个阶段中发生的某个问题 所以 对于每一阶段的深入理解有助于对类装入问题的诊断 图 显示了这三个阶段

  图 类装入的阶段

  装入 阶段包括 找到必要的类(通过查找每个类路径)并装入字节码 在 JVM 中 装入阶段为类对象提供了非常基本的内存结构 在这一阶段不处理方法 字段和引用的其他类 所以 类还不能使用

  链接 是三个阶段中最复杂的一个 可以把它分成三个主要阶段

字节码验证 类装入器对于类的字节码要做许多检测 以确保格式正确 行为正确 类准备 这个阶段准备代表每个类中定义的字段 方法和实现接口所必需的数据结构 解析 在这个阶段 类装入器装入类所引用的其他所有类 可以用许多方式引用类 超类 接口 字段 方法签名 方法中使用的本地变量

  在初始化 阶段 类中包含的静态初始化器都被执行 在这一阶段末尾 静态字段被初始化成默认值

  在这三个阶段末尾 类被完整地装入 可以使用了 请注意可以用惰性方式执行类装入 所以类装入过程的某些部分可能在第一次使用类的时候才执行 而不是在装入时执行

  显式装入与隐式装入

  类装入的方式有两种 —— 显式 或 隐式 两者之间有些细微差异

  显式 类装入发生在使用以下方法调用装入的类的时候

cl loadClass()(cl 是 java lang ClassLoader 的实例) Class forName()(启动的类装入器是当前类定义的类装入器)

  当调用其中一个方法的时候 指定的类(以类名为参数)由类装入器装入 如果类已经装入 那么只是返回一个引用 否则 装入器会通过委托模型装入类

  隐式 类装入发生在由于引用 实例化或继承导致装入类的时候(不是通过显式方法调用) 在每种情况下 装入都是在幕后启动的 JVM 会解析必要的引用并装入类 与显式类装入一样 如果类已经装入了 那么只是返回一个引用 否则 装入器会通过委托模型装入类

  类的装入通常组合了显式和隐式类装入 例如 类装入器可能先显式地装入一个类 然后再隐式地装入它引用的所有类

  JVM 的调试特性

  前面一节介绍了类装入的基本原则 这一节介绍 IBM JVM 中内置的帮助调试的特性 其他 JVM 也有类似的调试特性 请参阅相关文档来了解细节

  详细输出

  可以用 verbose 命令行选项打开 IBM JVM 的详细输出 当某些事件发生的时候(例如 类装入时) 详细输出会在控制台上显示信息 要想得到额外的类装入信息 可以用详细类输出 可以用 verbose:class 选项启动这个模式

  解释详细输出  详细输出列出已经打开的所有 JAR 文件 包括到这些 JAR 的完整路径 下面是一个示例 [Opened D:\\jre\\lib\\core jar in ms][Opened D:\\jre\\lib\\graphics jar in ms]

  所有装入的类都已经列出 同时还指出它们是从哪个 JAR 文件或目录装入的 例如 [Loaded java lang NoClassDefFoundError from D:\\jre\\lib\\core jar][Loaded java lang Class from D:\\jre\\lib\\core jar][Loaded java lang Object from D:\\jre\\lib\\core jar]

  详细类输出显示额外信息 例如在装入超类的时候 在运行静态初始化器的时候 下面是一些示例输出 [Loaded HelloWorld from file:/C:/myclasses/][Loading superclass and interfaces of HelloWorld][Loaded HelloWorldInterface from file:/C:/myclasses/][Loading superclass and interfaces of HelloWorldInterface][Preparing HelloWorldInterface][Preparing HelloWorld][Initializing HelloWorld][Running static initializer for HelloWorld]

  详细输出还显示一些内部抛出的异常(如果发生的话) 包含堆栈跟踪

  用 verbose 解决问题  详细输出有助于解决类路径问题 例如没有打开 JAR 文件(因此不在类路径中)或从错误的位置装入了类

  IBM 详细类装入

  知道类装入器在哪里寻找类 特定的类是由哪个类装入器装入的 通常很有用 可以用 IBM 详细类装入命令行选项得到这个信息 Dibm cl verbose=<class name> 可以用正则表达式声明类的名称 例如 Hello* 会跟踪所有以 Hello 开头的名称

  这个选项也可用于用户定义的类装入器 只要它们直接或间接地扩展了 URLClassLoader

  解释 IBM 详细类装入的输出  IBM 详细类装入的输出显示了要装入指定类的类装入器以及它们查找的位置 例如 假设用以下命令行 java Dibm cl verbose=ClassToTrace MainClass

  在这里 MainClass 在它的主方法中引用了 ClassToTrace 这会形成像 这里 一样的输出

  在列出类装入器的时候 父类在子女之前列出 因为标准的委托模型的工作方式是父类优先

  请注意 引导类装入器没有输出 只有扩展了 URLClassLoader 的类装入器才有输出 还请注意 类装入器列在它们的类名之下 如果类装入器有两个实例 那么可能无法区分它们

    用 IBM 详细类装入解决问题  IBM 详细类装入选项是检查所有类装入器设置的类路径的好方法 它还可以指出指定类是由哪个类装入器装入的 从哪里装入的 这样就可以容易地看出是否装入了类的正确版本

  Javadump

  Javadump(也称为 Javacore)是另一个很有用的 IBM 诊断工具 当发生以下事件时 JVM 会生成 Javadump

发生致命的本机异常 JVM 用光了堆空间 向 JVM 发送了一个信号(例如 在 Windows 上按下了 Control Break 或在 Linux 上按下了 Control \\) 调用了 ibm jvm Dump JavaDump() 方法

  触发 Javadump 的时候 会把详细信息记录到在当前工作目录下保存的一个有日期戳的文本文件中 信息包括线程 锁 堆栈等方面的数据 以及关于系统中类装入器的丰富信息

    解释 Javadump 中的类装入部分  Javadump 文件中提供的确切信息取决于 JVM 在哪个平台上运行 类装入器部分包括

定义的类装入器和它们之间的关系 每个类装入器装入的类的列表

  以下是从 Javadump 提取的类装入器信息的快照 CL subponent dump routine============================ Classpath Z(D:\\jre\\lib\\core jar)  Oldjava mode false Bootstrapping false Verbose class dependencies false Class verification VERIFY_REMOTE Namespace to classloader x  Start of cache entry pool x D  Start of free cache entries x D  Location of method table x C AA  Global namespace anchor x  System classloader shadow x  Classloader shadows x D BA  Extension loader x ADB  System classloader x ADB B  Classloader summaries         : =primordial =extension =shareable =middleware                    =system =trusted =application =delegating         ta Loader sun/misc/Launcher$AppClassLoader( x D BA )                   Shadow x ADB B                   Parent sun/misc/Launcher$ExtClassLoader( x ADB )                 Number of loaded classes                  Number of cached classes                  Allocation used for loaded classes                  Package owner x ADB B          xh st Loader sun/misc/Launcher$ExtClassLoader( x D )                   Shadow x ADB                   Parent *none*( x )                 Number of loaded classes                  Number of cached classes                  Allocation used for loaded classes                  Package owner x ADB          p h st Loader *System*( x ) Shadow x                  Number of loaded classes                  Number of cached classes                  Allocation used for loaded classes                  Package owner x  ClassLoader loaded classes         Loader sun/misc/Launcher$AppClassLoader( x D BA )                 HelloWorld( x ACF E )         Loader sun/misc/Launcher$ExtClassLoader( x D )         Loader *System*( x )                 java/io/WinNTFileSystem( x CD )                 java/lang/Throwable( x C A )                 java/lang/IndexOutOfBoundsException( x D )                 java/lang/UnsatisfiedLinkError( x D D ) classes left out to save space                  [Ljava/lang/Class;( x CA E )                 java/io/InputStream( x C )                 java/lang/Integer$ ( x C E )                 java/util/Dictionary( x C )

  在这个示例中 只有三个标准类装入器

系统类装入器( sun/misc/Launcher$AppClassLoader) 扩展类装入器( sun/misc/Launcher$ExtClassLoader) 引导类装入器( *System*

  Classloader 汇总部分提供了系统中每个类装入器的细节 在这个系列的文章中 感兴趣的类型是基本 扩展 系统 应用程序 和 委托(用在反射中) 其他类型(共享的 中间件 和信任的)用在 Persistent Reusable JVM 中 它们超出了这个文章系列的范围(请参阅 Persistent Reusable JVM User Guide 以获得更多信息 在下面的 参考资料 一节中有一个链接) 这个汇总部分还显示了父类类装入器 系统类装入器的父类是 sun/misc/Launcher$ExtClass loader( x ADB ) 这个父类地址对应于父类类装入器的原始数据结构(叫作 shadow)

  类装入器装入的类部分列出了每个类装入器装入的类 在这个示例中 系统类装入器只装入了一个类 HelloWorld(在地址 x ACF E 上)

    用 Javadump 解决问题  使用 Javadump 提供的信息 可以确定系统中存在哪些类装入器 这包括任何用户自定义的类装入器 从装入的类列表中 可以找出特定的类是由哪个类装入器装入的 如果找不到某个类 说明系统中的任何一个类装入器都没有装入它(通常会形成 ClassNotFoundException 异常)

  可以用 Javadump 诊断的其他类型的问题包括

类装入器命名空间问题 类装入器的命名空间是类装入器和它装入的所有类的组合 例如 如果某个类存在 但是由错误的类装入器装入(有时会造成 NoClassDefFoundError 异常) 那么命名空间就是错误的 —— 也就是说 类在错误的类路径中 为了纠正这种问题 可以试着把类放到不同的位置(例如放在正常的 Java 类路径中) 并确保由系统类装入器装入它 类装入器约束问题 在这个系列的最后一篇文章中将讨论这种问题的一个示例

  Java 方法跟踪

  IBM JVM 有一个内置的方法跟踪工具 这样 不需要修改 Java 代码 就可以跟踪任何 Java 代码(包括核心系统)中的方法 因为这个工具可以提供大量数据 所以可以控制跟踪的级别 只获取需要的信息

  启动跟踪的选项取决于 JVM 的发行版 下面是一些命令行示例

  在 IBM Java 中运行 HelloWorld 时要跟踪所有 java lang ClassLoader java thods=java/lang/ClassLoader *() Dibm dg trc print=mt HelloWorld

  跟踪 ClassLoader 中的 loadClass() 方法和 HelloWorld 中的方法 也在 IBM Java 中

java thods=java/lang/ClassLoader loadClass() HelloWorld *()  Dibm dg trc print=mt HelloWorld

    解释方法跟踪的输出  这里 是方法跟踪输出的一个示例(用前面一段的第二个命令行)

  跟踪的每一行都提供了比上面显示的更多的信息 我们来完整地看看上面的一行

: : x A C D   > java/lang/ClassLoader loadClass Bytecode method        This = x D B Signature: (Ljava/lang/String;Z)Ljava/lang/Class;

  这个跟踪包括

: : 方法进入或退出的时间戳 x A C 线程 ID D 某些高级诊断使用的内部 JVM 跟踪点 余下的信息显示是进入( >)还是退出了( <)方法 后面跟着方法的细节 性能热点 使用时间戳 可以发现需要花费相当多时间来执行的方法 挂起 最后的方法项通常是很好的线索 可以指明应用程序是否挂起 错误对象 使用地址 通过与对象的构建函数调用的地址进行比对 可以检查出是不是在正确的对象上调用方法 意外的代码路径 通过跟踪进入和退出点 可以看出程序是否采用了意外的代码路径 其他错误 最后的方法项是对错误发生位置的良好提示 cha138/Article/program/Java/hx/201311/26487

相关参考

自动调节系统的调试必须具备哪些条件。

自动调节系统的调试必须具备哪些条件。参考答案:1、计算机控制系统的硬件已恢复,基本软件已装入,系统已受电且工作正常;2、DAS系统已调试完毕,各技术指标符合设计要求;3、自动调节系统内的所有单体仪表和

轮胎使用保养九注意

一是安装要正确。装入内胎时,注意不要将泥沙、颗粒状坚硬物及油污类异物带入胎内,最好在外胎的内壁和内胎的表面打一层光蜡或涂一层滑石粉,以减小摩擦,防止内胎被擦伤。安装好内胎充气时,要边充气边用手锤敲打胎

轮胎使用保养九注意

一是安装要正确。装入内胎时,注意不要将泥沙、颗粒状坚硬物及油污类异物带入胎内,最好在外胎的内壁和内胎的表面打一层光蜡或涂一层滑石粉,以减小摩擦,防止内胎被擦伤。安装好内胎充气时,要边充气边用手锤敲打胎

怎样安排开炉料的装入位置?

  安排开炉料装入位置的原则是前面轻,后面紧跟,必须有利于加热炉缸。为此首先要确定第一批正常料的位置,一般是在炉腰或炉身下部,小高炉要偏高一些。第一批正常料以下所加净焦和空焦量占全部净焦空焦的比例,随

怎样安排开炉料的装入位置?

  安排开炉料装入位置的原则是前面轻,后面紧跟,必须有利于加热炉缸。为此首先要确定第一批正常料的位置,一般是在炉腰或炉身下部,小高炉要偏高一些。第一批正常料以下所加净焦和空焦量占全部净焦空焦的比例,随

怎样选择球磨机内装入球的大小?

  球磨机装入球的球径大小取决于许多因素,如被磨物料粒度、硬度、密度;球磨机直径、转速;球的密度;矿浆质量分数等,但实际生产中主要依据磨机内被磨物料的粒度组成而定。磨矿机的给料是由若干种大小不同的粒子

怎样选择球磨机内装入球的大小?

  球磨机装入球的球径大小取决于许多因素,如被磨物料粒度、硬度、密度;球磨机直径、转速;球的密度;矿浆质量分数等,但实际生产中主要依据磨机内被磨物料的粒度组成而定。磨矿机的给料是由若干种大小不同的粒子

知识大全 贪婪算法之——背包问题

  在/背包问题中需对容量为c的背包进行装载从n个物品中选取装入背包的物品每件物品i的重量为wi价值为pi对于可行的背包装载背包中物品的总重量不能超过背包的容量最佳装载是指所装入的物品价值最高即n?i

为什么要对烧结矿过筛再装入高炉?

  筛除炉料中粉末(炉料中<5mm的部分)增加料柱空隙率,降低压差,为高炉强化冶炼创造条件。

为什么要对烧结矿过筛再装入高炉?

  筛除炉料中粉末(炉料中<5mm的部分)增加料柱空隙率,降低压差,为高炉强化冶炼创造条件。