gpt4 book ai didi

java - Java常量池解析策略

转载 作者:行者123 更新时间:2023-12-04 15:04:39 26 4
gpt4 key购买 nike

来自许多地方,例如 Java SE Specifications和“Inside Java Virtual Machine”,它们都声明了当常量池条目的解析需要加载类型时,虚拟机使用与加载引用类型相同的类加载器来加载引用类型。

但是,我发现这条规则并不总是成立:

package com.alaneuler.test;

import java.io.InputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class LinkageMain {
static class MyClassLoader extends ClassLoader {
private Set<String> selfFirstClasses;
private String name;

public MyClassLoader(String name, ClassLoader parent, String... selfFirstNames) {
super(parent);
this.name = name;
selfFirstClasses = new HashSet<>(Arrays.asList(selfFirstNames));
}

@Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (selfFirstClasses.contains(name)) {
String filename = name.substring(name.lastIndexOf(".") + 1) + ".class";
try (InputStream is = getClass().getResourceAsStream(filename)) {
byte[] buf = new byte[is.available()];
int len = is.read(buf);
System.out.println(this.name + ": loading " + name);
return defineClass(name, buf, 0, len);
} catch (Exception e) {
e.printStackTrace();
}
}

if (!name.startsWith("java.")) {
System.out.println(this.name + ": super.loadClass(" + name + ")");
}
return super.loadClass(name, resolve);
}

@Override
public String toString() {
return name;
}
}

public static class User {}
public static class Login {
public void login(User u) {
System.out.println("--- login called with user loaded by " + u.getClass().getClassLoader() + " ---");
}
}
public static class Main {
public static void process() {
User u = new User();
new Login().login(u);
}
}

public static void main(String[] args) throws Exception {
MyClassLoader baseCL = new MyClassLoader("Base", LinkageMain.class.getClassLoader(),
"com.alaneuler.test.LinkageMain$User",
"com.alaneuler.test.LinkageMain$Login");
MyClassLoader specialCL = new MyClassLoader("specialCL", baseCL,
"com.alaneuler.test.LinkageMain$User",
"com.alaneuler.test.LinkageMain$Main");

specialCL.loadClass("com.alaneuler.test.LinkageMain$Main").getMethod("process").invoke(null);
}
}

我定义了一个自定义的 ClassLoader,它在其构造函数中接收一个应该由其自身定义的类名列表。基于此,baseCL 被配置为加载类 LoginUserspecialCl 以加载 UserMain(我知道这个设置破坏了类加载器委托(delegate)模型)。

运行这个例子会产生:

specialCL: loading com.alaneuler.test.LinkageMain$Main
specialCL: loading com.alaneuler.test.LinkageMain$User
specialCL: super.loadClass(com.alaneuler.test.LinkageMain$Login)
Base: loading com.alaneuler.test.LinkageMain$Login
--- login called with user loaded by specialCL ---

上面写着 login 方法是用 specialCl 加载的对象 u 调用的 not baseCL。这种现象与一开始所说的概念相悖,实在是太奇怪了,想不通哪里不对。

此外,使用 baseCl 加载类 User 总是失败并出现 LinkageError: loader constraint violation: loader (instance of com/alaneuler/test/LinkageMain$MyClassLoader) 之前为名称为“com/alaneuler/test/LinkageMain$User”的不同类型启动加载

我的问题是:

  1. login 的调用不应该立即引发错误吗?
  2. 为什么以下使用 baseCL 加载 User 失败并出现 LinkageError

如有任何帮助,我们将不胜感激!

感谢 Frank Kieviet 的 blog因为该示例是根据他对 LinkageError 的研究构建的。

最佳答案

问题 1:“调用 login 不应该立即引发错误吗?”

不,因为根据 Java SE Specification - Resolution 的分辨率只有当 anewarraycheckcastgetfieldgetstaticinstanceof , invokedynamic, invokeinterface, invokespecial, invokestatic, invokevirtual, ldcldc_wmultianewarraynewputfieldputstatic 被执行。

  • 在调用方(在 Main.process() 中)解析完成并成功
  • 在被调用方(在 Login.login() 中)没有解决,因为您没有调用任何 User类方法或访问类的任何字段。
  • 方法调用u.getClass()不算数,因为它解析为 Object.getClass()方法

问题 2:“为什么以下使用 baseCL 来加载 User 失败并返回 LinkageError?

当 JVM 加载 Login 时class 它已经有一个名为 User 的类可用 specialCL 加载.它将 (specialCL, "User") 对存储在 Login 的加载器约束中类。

当 JVM 现在需要解析 UserLogin 中的类类,它调用 Login类类加载器加载 User类(class)。然而,此加载返回另一个 User类实例 (baseCL, "User") 然后是记录在加载器约束 (specialCL, "User") 中的实例。

有关加载约束的说明,请参阅 Java SE Specification - Loading Constraints

关于java - Java常量池解析策略,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66384573/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com