在 类 中加载具有父子关系的 LinkageError
LinkageError from loading in classes that have a parent-child relation
我按照 http://www.jacoco.org/jacoco/trunk/doc/examples/java/CoreTutorial.java 处的 CoreTutorial 示例 class 了解如何将 Jacoco 合并到项目中。
但是,我面临 java.lang.LinkageError 与所使用的 MemoryClassLoader class 相关的问题。
/**
* A class loader that loads classes from in-memory data.
*/
public static class MemoryClassLoader extends ClassLoader {
private final Map<String, byte[]> definitions = new HashMap<String, byte[]>();
/**
* Add a in-memory representation of a class.
*
* @param name
* name of the class
* @param bytes
* class definition
*/
public void addDefinition(final String name, final byte[] bytes) {
definitions.put(name, bytes);
}
@Override
protected Class<?> loadClass(final String name, final boolean resolve)
throws ClassNotFoundException {
final byte[] bytes = definitions.get(name);
if (bytes != null) {
return defineClass(name, bytes, 0, bytes.length);
}
return super.loadClass(name, resolve);
}
}
具体来说,我有两个 class 类,MyClass 和 MySubClass,其中 MySubClass 扩展了 MyClass。我检测了两个 classes,以便我可以获得与每个相关的覆盖信息。我发现,如果我首先检测 MySubClass,MemoryClassLoader 将使用 MySubClass 的检测字节调用 defineClass。但是,这样做时,作为父 class 的 MyClass 也会被父 class 加载器加载。因此,当我下次使用并加载 MyClass 时,我收到 java.lang.LinkageError,它声称已经有一个由 ClassLoader 加载的 MyClass 的定义。这里的问题是未检测加载的 MyClass 的初始版本。
如果我以相反的顺序加载 classes,则不会出现此问题。
我在这里尝试了一些不同的东西:https://github.com/huangwaylon/randoop/blob/bloodhound/src/main/java/randoop/main/MemoryClassLoader.java 通过在递归调用 loadClass 时立即尝试检测父 class。但是,由于我尚未弄清楚的原因,这并不完全有效。我观察到如果我使用这种错误的方法,两个 classes 的覆盖率信息都不会改变。
我的总体问题是,如何检测和加载在父子关系中彼此相关的 classes,以便行为不受顺序影响?我可以替换 class 的定义吗?另一个问题似乎是 super.load 这将是我无法控制的 MemoryClassLoader 的父 ClassLoader。
首先,您应该覆盖 findClass
而不是 loadClass
。具有特定名称的 class 只能在加载程序中定义一次的限制 总是 适用,您可能总是会遇到请求相同 class 的情况多次(在任何重要的场景中)。
从 ClassLoader
继承的 loadClass
实现已经处理了这一点,并且 return 现有定义(如果有)。因为它也首先查询父加载器,你应该注意指定正确的父加载器,如果加载器应该定义 classes 的名称也被其他 class 加载器使用。使用 loadClass
的默认实现,您的 findClass
方法可以保持如此简单:
public static class MemoryClassLoader extends ClassLoader {
private final Map<String, byte[]> definitions = new HashMap<>();
public void addDefinition(final String name, final byte[] bytes) {
definitions.put(name, bytes);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
final byte[] bytes = definitions.get(name);
if (bytes != null) {
return defineClass(name, bytes, 0, bytes.length);
}
return super.findClass(name);
}
}
可以通过两种方式确保所有预期的 classes 都得到检测。
按需检测它们,即让 findClass
方法触发所请求的检测 class.
在第一次调用 loadClass
.
之前检测所有预期的 classes 的字节码并将结果放入加载程序
final MemoryClassLoader memoryClassLoader = new MemoryClassLoader();
memoryClassLoader.addDefinition("MyClass", instrumentedMyClass);
memoryClassLoader.addDefinition("MySubClass", instrumentedMySubClass);
final Class<?> myClass = memoryClassLoader.loadClass("MyClass");
在这里,addDefinition
调用的顺序无关紧要,先调用 loadClass("MyClass")
还是先调用 loadClass("MySubClass")
也无关紧要。这甚至适用于循环依赖。
我按照 http://www.jacoco.org/jacoco/trunk/doc/examples/java/CoreTutorial.java 处的 CoreTutorial 示例 class 了解如何将 Jacoco 合并到项目中。
但是,我面临 java.lang.LinkageError 与所使用的 MemoryClassLoader class 相关的问题。
/**
* A class loader that loads classes from in-memory data.
*/
public static class MemoryClassLoader extends ClassLoader {
private final Map<String, byte[]> definitions = new HashMap<String, byte[]>();
/**
* Add a in-memory representation of a class.
*
* @param name
* name of the class
* @param bytes
* class definition
*/
public void addDefinition(final String name, final byte[] bytes) {
definitions.put(name, bytes);
}
@Override
protected Class<?> loadClass(final String name, final boolean resolve)
throws ClassNotFoundException {
final byte[] bytes = definitions.get(name);
if (bytes != null) {
return defineClass(name, bytes, 0, bytes.length);
}
return super.loadClass(name, resolve);
}
}
具体来说,我有两个 class 类,MyClass 和 MySubClass,其中 MySubClass 扩展了 MyClass。我检测了两个 classes,以便我可以获得与每个相关的覆盖信息。我发现,如果我首先检测 MySubClass,MemoryClassLoader 将使用 MySubClass 的检测字节调用 defineClass。但是,这样做时,作为父 class 的 MyClass 也会被父 class 加载器加载。因此,当我下次使用并加载 MyClass 时,我收到 java.lang.LinkageError,它声称已经有一个由 ClassLoader 加载的 MyClass 的定义。这里的问题是未检测加载的 MyClass 的初始版本。
如果我以相反的顺序加载 classes,则不会出现此问题。
我在这里尝试了一些不同的东西:https://github.com/huangwaylon/randoop/blob/bloodhound/src/main/java/randoop/main/MemoryClassLoader.java 通过在递归调用 loadClass 时立即尝试检测父 class。但是,由于我尚未弄清楚的原因,这并不完全有效。我观察到如果我使用这种错误的方法,两个 classes 的覆盖率信息都不会改变。
我的总体问题是,如何检测和加载在父子关系中彼此相关的 classes,以便行为不受顺序影响?我可以替换 class 的定义吗?另一个问题似乎是 super.load 这将是我无法控制的 MemoryClassLoader 的父 ClassLoader。
首先,您应该覆盖 findClass
而不是 loadClass
。具有特定名称的 class 只能在加载程序中定义一次的限制 总是 适用,您可能总是会遇到请求相同 class 的情况多次(在任何重要的场景中)。
从 ClassLoader
继承的 loadClass
实现已经处理了这一点,并且 return 现有定义(如果有)。因为它也首先查询父加载器,你应该注意指定正确的父加载器,如果加载器应该定义 classes 的名称也被其他 class 加载器使用。使用 loadClass
的默认实现,您的 findClass
方法可以保持如此简单:
public static class MemoryClassLoader extends ClassLoader {
private final Map<String, byte[]> definitions = new HashMap<>();
public void addDefinition(final String name, final byte[] bytes) {
definitions.put(name, bytes);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
final byte[] bytes = definitions.get(name);
if (bytes != null) {
return defineClass(name, bytes, 0, bytes.length);
}
return super.findClass(name);
}
}
可以通过两种方式确保所有预期的 classes 都得到检测。
按需检测它们,即让
findClass
方法触发所请求的检测 class.在第一次调用
之前检测所有预期的 classes 的字节码并将结果放入加载程序loadClass
.final MemoryClassLoader memoryClassLoader = new MemoryClassLoader(); memoryClassLoader.addDefinition("MyClass", instrumentedMyClass); memoryClassLoader.addDefinition("MySubClass", instrumentedMySubClass); final Class<?> myClass = memoryClassLoader.loadClass("MyClass");
在这里,
addDefinition
调用的顺序无关紧要,先调用loadClass("MyClass")
还是先调用loadClass("MySubClass")
也无关紧要。这甚至适用于循环依赖。