Можно ли использовать класс Object с помощью JDI? - PullRequest
0 голосов
/ 24 мая 2018

Я пытаюсь создать какой-нибудь инструментальный инструмент.Я хочу отслеживать распределение каждого объекта.Самая простая идея, которая пришла мне в голову, состояла в том, чтобы повторно преобразовать конструктор объекта, поскольку каждый объект вызывает его (я знаю, что массивы инициализируются по-разному).

Я пытался использовать механизм агента 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));

    }
}

И отладчик сделал толькоthis:

    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 байт-код, но это не является причиной проблемы.

--- EDIT2 ---

Я пытался преобразовать другой 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.Мое предположение, это как-то связано с classloader система в Java (?).Можно ли как-то избежать такой ошибки?Я не знаю много о загрузчиках классов: /

...