bigywater.tech
Java · 244

类加载约束:同名类型为什么不能互相替代

为什么两个同名的类型不能互相替代?为什么会有 LinkageError: loader constraint violation?这篇用自己写的类加载器把这些错误一个个复现出来,并看清它们的触发条件和两种不同的错误信息。

全名 + 加载器
类的身份由什么决定
LinkageError
签名里的类型在不同加载器里不同时抛出的错误
父加载器
共享的类型应该放在哪里

同名类在不同加载器里是不同的类

实测 · 本机执行输出
  同一个 class 文件、两个不同的类加载器各自加载 lib.Dep:d1 == d2 → false,名字相同 → true,d1 的加载器 = A,d2 的加载器 = B
  d2.isInstance(o1) = false;d1.isInstance(o1) = true;d2.isAssignableFrom(d1) = false
  d2.cast(o1) → ClassCastException: Cannot cast lib.Dep to lib.Dep
  同一个加载器第二次 defineClass 同名类 → LinkageError: loader 'A' @70dea4e attempted duplicate class definition for lib.Dep. (lib.Dep is in unnamed module of loader 'A' @70dea4e, parent loader 'bootstrap')
  两个都委派给引导加载器的 java.lang.String:s1 == s2 → true(JDK 核心类只有一份)
  l1 与 l2 的 parent 都是 null(引导加载器);Class.forName("lib.Dep", false, l1) 与 l1.loadClass 返回同一个对象:true

loader constraint violation

实测 · 本机执行输出
  Caller 的加载器 = C;C 里的 lib.Dep 的加载器 = C;Callee 的加载器 = P;P 里的 lib.Dep 的加载器 = P
  调用 Caller.go()(里面是 Callee.m(new Dep()),两个 Dep 来自不同加载器)→ LinkageError: loader constraint violation: when resolving method 'java.lang.String lib.Callee.m(lib.Dep)' the class loader 'C' @33909752 of the current class, app/Caller, and the class loader 'P' @70dea4e for the method's defining class, lib/Callee, have different Class objects for the type lib/Dep used in the signature (app.Caller is in unna…
  对照:C2 只自己定义 Caller,Dep 与 Callee 都委派给 P → Caller.go() = Callee.m 收到 Dep@P

错误顺序与共享类型

实测 · 本机执行输出
  第 1 次调用 → LinkageError: loader constraint violation: loader 'P' @70dea4e wants to load class lib.Dep. A different class with the same name was previously loaded by 'C' @33909752. (lib.Dep is in unnamed module of loader 'C' @33909752, parent loader 'P' @70dea4e)
  第 2 次调用 → LinkageError: loader constraint violation: loader 'P' @70dea4e wants to load class lib.Dep. A different class with the same name was previously loaded by 'C' @33909752. (lib.Dep is in unnamed module of loader 'C' @33909752, parent loader 'P' @70dea4e)
  之后让 P 自己加载它的 lib.Dep → LinkageError: loader constraint violation: loader 'P' @70dea4e wants to load class lib.Dep. A different class with the same name was previously loaded by 'C' @33909752. (lib.Dep is in unnamed module of loader 'C' @33909752, parent loader 'P' @70dea4e)(P 自己的 Dep 也加载不了了)
  P 加载 lib.Callee(不涉及 Dep)→ 成功:lib.Callee
  共享的接口(java.util.List)在两个加载器里是同一个 Class:true——跨加载器通信要靠双方都能看到的父加载器里的类型

分步演示

类加载约束
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12;自己写的子加载器优先(child-first)加载器
    • 没有验证:模块系统(JPMS)下的包与加载器规则、OSGi 的类加载约束、Java agent 与应用类加载器的关系、约束在 JIT 编译后代码里的表现、其它 JDK 版本的错误信息文字
    京ICP备2026067502号-1京公网安备11010502063487号