Linux 如何处理 Java 中的 LinkageErrors?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/244482/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 16:39:45  来源:igfitidea点击:

How to deal with LinkageErrors in Java?

javalinuxclassloaderlinkageerror

提问by Urs Reupke

Developing a heavily XML-based Java-application, I recently encountered an interesting problem on Ubuntu Linux.

开发一个基于 XML 的 Java 应用程序,我最近在 Ubuntu Linux 上遇到了一个有趣的问题。

My application, using the Java Plugin Framework, appears unable to convert a dom4j-created XML document to Batik'simplementation of the SVG specification.

我的应用程序使用Java Plugin Framework,似乎无法将dom4j创建的 XML 文档转换为Batik 的SVG 规范实现。

On the console, I learn that an error occurs:

在控制台上,我得知发生了错误:

Exception in thread "AWT-EventQueue-0" java.lang.LinkageError: loader constraint violation in interface itable initialization: when resolving method "org.apache.batik.dom.svg.SVGOMDocument.createAttribute(Ljava/lang/String;)Lorg/w3c/dom/Attr;" the class loader (instance of org/java/plugin/standard/StandardPluginClassLoader) of the current class, org/apache/batik/dom/svg/SVGOMDocument, and the class loader (instance of <bootloader>) for interface org/w3c/dom/Document have different Class objects for the type org/w3c/dom/Attr used in the signature
    at org.apache.batik.dom.svg.SVGDOMImplementation.createDocument(SVGDOMImplementation.java:149)
    at org.dom4j.io.DOMWriter.createDomDocument(DOMWriter.java:361)
    at org.dom4j.io.DOMWriter.write(DOMWriter.java:138)

I figure that the problem is caused by a conflict between the original classloader from the JVM and the classloader deployed by the plugin framework.

我认为问题是由来自 JVM 的原始类加载器与插件框架部署的类加载器之间的冲突引起的。

To my knowledge, it's not possible to specify a classloader for the framework to use. It might be possible to hack it, but I would prefer a less aggressive approach to solving this problem, since (for whatever reason) it only occurs on Linux systems.

据我所知,不可能为框架指定一个类加载器来使用。有可能破解它,但我更喜欢用不那么激进的方法来解决这个问题,因为(无论出于何种原因)它只发生在 Linux 系统上。

Has one of you encountered such a problem and has any idea how to fix it or at least get to the core of the issue?

你们中的一个人是否遇到过这样的问题并且知道如何解决它或至少找到问题的核心?

采纳答案by Alex Miller

LinkageError is what you'll get in a classic case where you have a class C loaded by more than one classloader and those classes are being used together in the same code (compared, cast, etc). It doesn't matter if it is the same Class name or even if it's loaded from the identical jar - a Class from one classloader is always treated as a different Class if loaded from another classloader.

LinkageError 是在经典情况下您会得到的结果,在这种情况下,您有一个由多个类加载器加载的类 C,并且这些类在同一代码中一起使用(比较、转换等)。它是相同的类名还是从相同的 jar 加载都没有关系 - 如果从另一个类加载器加载,来自一个类加载器的类总是被视为不同的类。

The message (which has improved a lot over the years) says:

该消息(多年来已经有了很大改进)说:

Exception in thread "AWT-EventQueue-0" java.lang.LinkageError: 
loader constraint violation in interface itable initialization: 
when resolving method "org.apache.batik.dom.svg.SVGOMDocument.createAttribute(Ljava/lang/String;)Lorg/w3c/dom/Attr;" 
the class loader (instance of org/java/plugin/standard/StandardPluginClassLoader) 
of the current class, org/apache/batik/dom/svg/SVGOMDocument, 
and the class loader (instance of ) for interface org/w3c/dom/Document 
have different Class objects for the type org/w3c/dom/Attr used in the signature

So, here the problem is in resolving the SVGOMDocument.createAttribute() method, which uses org.w3c.dom.Attr (part of the standard DOM library). But, the version of Attr loaded with Batik was loaded from a different classloader than the instance of Attr you're passing to the method.

因此,这里的问题在于解决使用 org.w3c.dom.Attr(标准 DOM 库的一部分)的 SVGOMDocument.createAttribute() 方法。但是,加载了 Batik 的 Attr 版本是从与您传递给方法的 Attr 实例不同的类加载器加载的。

You'll see that Batik's version seems to be loaded from the Java plugin. And yours is being loaded from " ", which is most likely one of the built-in JVM loaders (boot classpath, ESOM, or classpath).

您会看到 Batik 的版本似乎是从 Java 插件加载的。而您的正在从“”加载,这很可能是内置 JVM 加载器之一(引导类路径、ESOM 或类路径)。

The three prominent classloader models are:

三个突出的类加载器模型是:

  • delegation (the default in the JDK - ask parent, then me)
  • post-delegation (common in plugins, servlets, and places where you want isolation - ask me, then parent)
  • sibling (common in dependency models like OSGi, Eclipse, etc)
  • 委托(JDK 中的默认设置 - 先问父母,然后是我)
  • 委派后(常见于插件、servlet 和您想要隔离的地方 - 问我,然后是父母)
  • 兄弟(常见于 OSGi、Eclipse 等依赖模型中)

I don't know what delegation strategy the JPF classloader uses, but the key is that you want one version of the dom library to be loaded and everyone to source that class from the same location. That may mean removing it from the classpath and loading as a plugin, or preventing Batik from loading it, or something else.

我不知道 JPF 类加载器使用什么委托策略,但关键是您希望加载一个版本的 dom 库,并且每个人都从同一位置获取该类。这可能意味着将它从类路径中删除并作为插件加载,或者阻止 Batik 加载它,或者其他什么。

回答by matt b

Sounds like a classloader hierarchy problem. I can't tell what type of environment your application is deployed in, but sometimes this problem can occur in a web environment - where the application server creates a hierarchy of classloaders, resembling something like:

听起来像是类加载器层次结构问题。我不知道你的应用程序部署在什么类型的环境中,但有时这个问题会发生在 web 环境中 - 应用程序服务器创建类加载器的层次结构,类似于:

javahome/lib - as root
appserver/lib - as child of root
webapp/WEB-INF/lib - as child of child of root
etc

javahome/lib - 作为 root
appserver/lib - 作为 root
webapp/WEB-INF/lib 的孩子 - 作为 root
等的孩子的孩子

Usually classloaders delegate loading to their parent classloader (this is known as "parent-first"), and if that classloader cannot find the class, then the child classloader attempts to. For example, if a class deployed as a JAR in webapp/WEB-INF/lib tries to load a class, first it asks the classloader corresponding to appserver/lib to load the class (which in turn asks the classloader corresponding to javahome/lib to load the class), and if this lookup fails, then WEB-INF/lib is searched for a match to this class.

通常类加载器将加载委托给它们的父类加载器(这称为“ parent-first”),如果该类加载器找不到该类,则子类加载器会尝试。例如,如果一个在 webapp/WEB-INF/lib 中部署为 JAR 的类尝试加载一个类,它首先会要求 appserver/lib 对应的类加载器加载该类(后者又会要求 javahome/lib 对应的类加载器)加载类),如果此查找失败,则搜索 WEB-INF/lib 以查找与此类的匹配项。

In a web environment, you can run into problems with this hierarchy. For example, one mistake/problem I've run into before was when a class in WEB-INF/lib depended on a class deployed in appserver/lib, which in turn depended on a class deployed in WEB-INF/lib. This caused failures because while classloaders are able to delegate to the parent classloader, they cannot delegate back down the tree. So, the WEB-INF/lib classloader would ask appserver/lib classloader for a class, appserver/lib classloader would load that class and try to load the dependent class, and fail since it could not find that class in appserver/lib or javahome/lib.

在 Web 环境中,您可能会遇到此层次结构的问题。例如,我之前遇到的一个错误/问题是 WEB-INF/lib 中的类依赖于部署在 appserver/lib 中的类,而后者又依赖于部署在 WEB-INF/lib 中的类。这导致了失败,因为虽然类加载器能够委托给父类加载器,但它们不能委托回树。因此,WEB-INF/lib 类加载器会向 appserver/lib 类加载器请求一个类,appserver/lib 类加载器会加载该类并尝试加载依赖类,但由于在 appserver/lib 或 javahome 中找不到该类而失败/lib.

So, while you may not be deploying your app in a web/app server environment, my too-long explanation might apply to you if your environment has a hierarchy of classloaders set up. Does it? Is JPF doing some sort of classloader magic to be able to implement it's plugin features?

因此,虽然您可能不会在 web/app 服务器环境中部署您的应用程序,但如果您的环境设置了类加载器的层次结构,我的冗长解释可能适用于您。可以?JPF 是否在做某种类加载器魔法来实现它的插件功能?

回答by Adam Crume

Can you specify a class loader? If not, try specifying the context class loader like so:

你能指定一个类加载器吗?如果没有,请尝试像这样指定上下文类加载器:

Thread thread = Thread.currentThread();
ClassLoader contextClassLoader = thread.getContextClassLoader();
try {
    thread.setContextClassLoader(yourClassLoader);
    callDom4j();
} finally {
    thread.setContextClassLoader(contextClassLoader);
}

I'm not familiar with the Java Plugin Framework, but I write code for Eclipse, and I run into similar issues from time to time. I don't guarantee it'll fix it, but it's probably worth a shot.

我对 Java Plugin Framework 不熟悉,但是我为 Eclipse 编写代码,并且我不时遇到类似的问题。我不保证它会修复它,但它可能值得一试。

回答by Anselm Schuster

The answers from Alex and Matt are very helpful. I could benefit from their analysis too.

Alex 和 Matt 的回答非常有帮助。我也可以从他们的分析中受益。

I had the same problem when using the Batik library in a Netbeans RCP framework, the Batik library being included as a "Library Wrapper Module". If some other module makes use of XML apis, and no dependency on Batik is needed and established for that module, the class loader constraint violation problem arises with similar error messages.

在 Netbeans RCP 框架中使用 Batik 库时,我遇到了同样的问题,Batik 库被包含为“库包装模块”。如果某个其他模块使用 XML api,并且该模块不需要和建立对 Batik 的依赖关系,则会出现类加载器约束违规问题,并带有类似的错误消息。

In Netbeans, individual modules use dedicated class loaders, and the dependence relationship between modules implies suitable class loader delegation routing.

在 Netbeans 中,各个模块使用专用的类加载器,模块之间的依赖关系意味着合适的类加载器委托路由。

I could resolve the problem by simply omitting the xml-apis jar file from the Batik library bundle.

我可以通过简单地从 Batik 库包中省略 xml-apis jar 文件来解决这个问题。

回答by vineetv2821993

May be this will help someone because it works out pretty good for me. The issue can be solve by integrating your own dependencies. Follow this simple steps

可能这会帮助某人,因为它对我来说非常好。该问题可以通过集成您自己的依赖项来解决。按照这个简单的步骤

First check the error which should be like this :

首先检查应该是这样的错误:

  • Method execution failed:
  • java.lang.LinkageError: loader constraint violation:
  • when resolving method "org.slf4j.impl.StaticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory;"
  • the class loader (instance of org/openmrs/module/ModuleClassLoader) of the current class, org/slf4j/LoggerFactory,
  • and the class loader (instance of org/apache/catalina/loader/WebappClassLoader) for resolved class, org/slf4j/impl/StaticLoggerBinder,
  • have different Class objects for the type taticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory; used in the signature
  • 方法执行失败:
  • java.lang.LinkageError:加载器约束违规:
  • 解决方法时“org.slf4j.impl。StaticLoggerBinder.getLoggerFactory()Lorg / SLF4J / ILoggerFactory;”
  • 当前类的类加载器(org/openmrs/module/ModuleClassLoader 的实例), org/slf4j/ LoggerFactory
  • 和解析类的类加载器(org/apache/catalina/loader/WebappClassLoader 的实例), org/slf4j/impl/ StaticLoggerBinder
  • taticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory 类型有不同的 Class 对象;在签名中使用


  1. See the two highlighted class. Google search for them like "StaticLoggerBinder.class jar download" & "LoggeraFactory.class jar download". This will show you first or in some case second link (Site is http://www.java2s.com) which is one of the jar version you have included in your project. You can smartly identify it yourself, but we are addicted of google ;)

  2. After that you will know the jar file name, in my case it is like slf4j-log4j12-1.5.6.jar & slf4j-api-1.5.8

  3. Now the latest version of this file is available here http://mvnrepository.com/(actually all version till date, this is the site from where maven get your dependencies).
  4. Now add both file as a dependencies with the latest version (or keep both file version same, either chosen version is old). Following is the dependency you have to include in pom.xml
  1. 请参阅两个突出显示的类。谷歌搜索它们,如“StaticLoggerBinder.class jar download”和“LoggeraFactory.class jar download”。这将显示第一个或在某些情况下第二个链接(站点是http://www.java2s.com),它是您项目中包含的 jar 版本之一。你可以自己聪明地识别它,但我们对谷歌上瘾了;)

  2. 之后你就会知道 jar 文件名,在我的例子中它就像 slf4j-log4j12-1.5.6.jar & slf4j-api-1.5.8

  3. 现在该文件的最新版本可在http://mvnrepository.com/ 获得(实际上是迄今为止的所有版本,这是 maven 获取依赖项的站点)。
  4. 现在将这两个文件添加为最新版本的依赖项(或保持两个文件版本相同,任一选择的版本都是旧版本)。以下是您必须包含在 pom.xml 中的依赖项


<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.7</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.7</version>
</dependency>


How to get dependecy definition from Maven Site

如何从 Maven 站点获取依赖定义

回答by Per Lundberg

As specified in this question, enabling the -verbose:classwill make the JVM log information about all classes being loaded, which can be incredibly helpful to understand where the classes are coming from in more complex scenarios & applications.

正如在这个问题中所指定的,启用-verbose:class将使 JVM 记录有关正在加载的所有类的信息,这对于了解更复杂的场景和应用程序中类的来源非常有帮助。

The output you get looks roughly like this (copied from that question):

您获得的输出大致如下(从该问题复制):

[Opened /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/sunrsasign.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/jsse.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/jce.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/charsets.jar]
[Loaded java.lang.Object from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.io.Serializable from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.Comparable from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.CharSequence from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.String from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]

回答by zero zero

I find this class be loaded twice. Find the reason is that parallelWebappClassLoader load class by itself first rather than use it's parent classLoader.

我发现这个类被加载了两次。找到原因是parallelWebappClassLoader首先自己加载类而不是使用它的父类加载器。