我正在尝试创建一些检测工具。我想跟踪每个对象分配。我想到的最简单的想法是在每个对象调用它时重新转换 Object 构造函数(我知道数组的初始化方式不同)。
我尝试使用 java 代理机制,但它导致java.lang.instrument.UnmodifiableClassException
. 显然,Java 代理无法转换 Object 类,因为它是不可修改的。
然后我尝试使用JDI,我调试的程序看起来像:
public class First {
public static int value = 1;
public static void main(String... args) throws InterruptedException {
while (true) {
print();
Thread.sleep(1000);
}
}
public static void print() {
System.out.println("Hello" + new Integer(value));
}
}
调试器只做了这个:
VirtualMachine vm = new VMAcquirer().connect(8000);
List<ReferenceType> referenceTypes1 = vm.classesByName("java.lang.Object");
ReferenceType object = referenceTypes1.get(0);
if (vm.canRedefineClasses()) {
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.get("java.lang.Object");
CtConstructor constructor = ctClass.getConstructors()[0];
constructor.insertAfter("First.value += 1;");
HashMap<ReferenceType, byte[]> redefine = new HashMap<>();
redefine.put(object, ctClass.toBytecode());
ctClass.writeFile();
vm.redefineClasses(redefine);
}
vm.resume();
在该目标程序退出并显示消息后:
ERROR: JDWP Transport dt_socket failed to initialize, OUT_OF_MEMORY(110)
Exception: java.lang.StackOverflowError thrown from the UncaughtExceptionHandler in thread "main"
我在这里做错了吗?是否可以这样转换 Object 类?我知道JVMTI但我想避免使用 C 代码,那么还有其他不需要本机代码的方法吗?
免责声明我知道这里已经提出了一些类似的问题(例如,入侵 java.lang.Object:调用自定义外部类使 JVM或JDI 崩溃、Java 字节码检测和 Java 代理(JWDP、JVMTI)),但他们没有向我解释一切。
- -编辑 - -
转换后Object
的类如下所示:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package java.lang;
import jdk.internal.HotSpotIntrinsicCandidate;
public class Object {
private static native void registerNatives();
@HotSpotIntrinsicCandidate
public Object() {
Object var2 = null;
++First.value;
}
@HotSpotIntrinsicCandidate
public final native Class<?> getClass();
@HotSpotIntrinsicCandidate
public native int hashCode();
public boolean equals(Object obj) {
return this == obj;
}
@HotSpotIntrinsicCandidate
protected native Object clone() throws CloneNotSupportedException;
public String toString() {
return this.getClass().getName() + "@" +
Integer.toHexString(this.hashCode());
}
@HotSpotIntrinsicCandidate
public final native void notify();
@HotSpotIntrinsicCandidate
public final native void notifyAll();
public final void wait() throws InterruptedException {
this.wait(0L);
}
public final native void wait(long var1) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0L) {
throw new IllegalArgumentException("timeout value is negative");
} else if (nanos >= 0 && nanos <= 999999) {
if (nanos > 0) {
++timeout;
}
this.wait(timeout);
} else {
throw new IllegalArgumentException("nanosecond timeout value out of range");
}
}
/** @deprecated */
@Deprecated(
since = "9"
)
protected void finalize() throws Throwable {
}
static {
registerNatives();
}
}
我也做了更多的测试,如果我把类似的东西放进int i = 1; int j = 2 + i;
去。
我还尝试了修改Integer
构造函数 - 这导致了另一个异常:
Exception in thread "main" java.lang.NoClassDefFoundError: First
at java.base/java.lang.Integer.<init>(Integer.java:1075)
at First.print(First.java:13)
at First.main(First.java:7)
类已成功转换,但在运行时链接到该类时出现问题。我不知道它是否以某种方式链接。Object
当一些内部的东西试图创建新实例时,可能会发生类似的事情。
我对线很好奇Object var2 = null;
。Javaassist 总是放置ACONST_NULL
字节码,但这不是问题的原因。
---编辑2---
我试图改变另一种Object
方法。转换成功,但在运行时再次出现错误:
Exception in thread "main" java.lang.NoClassDefFoundError: First
at java.base/java.lang.Object.toString(Object.java:246)
at First.print(First.java:15)
at First.main(First.java:7)
对我来说,真正的问题似乎在于NoClassDefFoundError
. 我的假设是它与java中的类加载器系统有某种关系(?)。我能以某种方式避免这样的错误吗?我对类加载器了解不多:/