Извлечение и проверка адресов объектов Java

Я хотел бы извлечь фактический адрес java-объектов для какой-либо цели исследования. Чтобы быть ясным, мне действительно нужен виртуальный адрес 48 бит, а не идентификатор или хэш-код или какой-либо уникальный идентификатор, и я понимаю, что эти адреса перемещаются GC. Я читал другие сообщения из stackoverflow, например here или здесь.

Для следующего использования @Peter Lawrey → Есть ли способ получить ссылочный адрес?. Поэтому он использует класс Unsafe с методом arrayBaseOffset. То, что я нашел странным в этих методах, заключается в том, что они дают одинаковый результат для каждого запуска (по крайней мере, на моем компьютере), который вряд ли произойдет. Предполагается, что распределение памяти предполагается рандомизированным по соображениям безопасности.

Кроме того, я попытался проверить эти методы с помощью Pintools, который является инструментом инструментария от Intel, который я использовал для извлечения следов памяти прогона. Моя проблема в том, что я не могу сопоставить то, что я вижу в палитре памяти Pintools, с адресами, указанными вышеприведенными методами, для получения адресов памяти. Эти адреса никогда не доступны в моей памяти.

Итак, мне интересно, что возвращается этими методами и как эти результаты были проверены против других инструментов.

Некоторые сведения: моя ОС - Ubuntu x86_64, мой JVM - это openJDK 64bits 1.8.0_131, версия pintools v3.2

=================== Big Edit: Я понимаю, что мой вопрос не очень хорошо, так что позвольте мне получить более атомный пример, вот Java, который я пытаюсь проанализировать:

`import sun.misc.Unsafe;
import java.lang.reflect.Field;

public class HelloWorld {

    public static void main(String[] args) throws Exception {
    Unsafe unsafe = getUnsafeInstance(); 
    Integer i = new Integer(42);
    long addr_fromArray;
    long addr_fromObject;

/////////////////////////////////////   
    Object[] objects = {i};
    long baseOffset = unsafe.arrayBaseOffset(Object[].class);
    addr_fromArray = unsafe.getLong(objects, baseOffset);   

    long factor1 = 8;        
    long addr_withFactor = (unsafe.getInt(objects, baseOffset) & 0xFFFFFFFFL) * factor1;

    /////////////////////////////////////   
    class Pointer {
        Object pointer;
    }

    Pointer pointer = new Pointer();
    pointer.pointer = i;
    long offset =     unsafe.objectFieldOffset(Pointer.class.getDeclaredField("pointer"));
    addr_fromObject = unsafe.getLong(pointer, offset);


    System.out.println("Addr of i from Array : 0x" + Long.toHexString(addr_fromArray));
    System.out.println("Addr of i from Object : 0x" + Long.toHexString(addr_fromObject));

    System.out.println("Addr of i from factor1 : 0x" + Long.toHexString(addr_withFactor));

    System.out.println("!=1");//Launch the pintools instrumentation 
    for(int a= 0 ; a < 123 ;a++){   
        i = 10;
    }
    System.out.println("!=1");//Stop the pintools instrumentation 
}

private static Unsafe getUnsafeInstance() throws SecurityException,
NoSuchFieldException, IllegalArgumentException,
IllegalAccessException {
    Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");
    theUnsafeInstance.setAccessible(true);
    return (Unsafe) theUnsafeInstance.get(Unsafe.class);
    }
}`

Я получаю указатель на я Integer из разных методов, которые я видел при переполнении стека. Затем я делаю цикл на я в течение произвольного количества времени, чтобы распознать его в своей памяти (Примечание: я проверил, что в этом коде не происходит никаких вызовов GC)

Когда pintools видят конкретный "!= 1", записанный на стандартном выходе, он запускает/останавливает инструментарий

При каждом доступе на фазе инструментальной обработки я выполняю этот код:

VOID RecordAccess(VOID* ip, int id_thread , VOID * addr, int id)
{
    PIN_GetLock(&lock, id_thread);
    if(startInstru)
    {
        log1 << "Data accessed: " << addr << "\tThread:" << id_thread << endl;
        nb_access++;
        uint64_t dummy = reinterpret_cast<uint64_t>(addr);
        if(accessPerAddr.count(dummy) == 0)
            accessPerAddr.insert(pair<uint64_t,uint64_t>(dummy, 0));
        accessPerAddr[dummy]++;
    }
}

С помощью этих pintools я генерирую трассировку памяти + гистограмму о том, сколько раз обращаются к каждому адресу памяти. Примечание: pintool запускается с опцией "follow_execv", чтобы обрабатывать все потоки.

Я вижу 2 Проблемы:

1) Я не вижу доступа ни к одному из напечатанных адресов я (или близко к этому адресу). Я склонен доверять Pintools, потому что я использовал довольно много раньше, но, возможно, Pintools не может получить правильные адреса здесь.

2) Я не вижу адресов, к которым обращаются 123 раза (или близко к ним). Мои мысли в этом состоит в том, что, возможно, JVM выполняет оптимизацию здесь, потому что видит, что выполненный код не имеет эффекта, поэтому он не выполняет его. Тем не менее, я попытался с более сложной инструкцией (которая не может быть оптимизирована как хранение случайного числа) внутри цикла, чем просто хранилище я без лучших результатов.

Мне все равно, что эффект GC здесь, может быть, на втором этапе. Я только хочу, чтобы иметь возможность извлекать собственные адреса из моего приложения java, что я уверен, что Pintools дает мне.

Ответ 1

Итак, когда я использую этот запуск с pintools, с аналогичным script, как здесь. Я не вижу никакого доступа к указанным адресам или соседним адресам.

Думаю, вам следует дать больше информации о том, как вы бежите и что видите.

Чтобы изучить макеты объектов, вы можете использовать http://openjdk.java.net/projects/code-tools/jol.

import org.openjdk.jol.info.GraphLayout;

import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.SortedSet;

public class OrderOfObjectsAfterGCMain2 {
    public static void main(String... args) {
    Double[] ascending = new Double[16];
    for (int i = 0; i < ascending.length; i++)
        ascending[i] = (double) i;

    Double[] descending = new Double[16];
    for (int i = descending.length - 1; i >= 0; i--)
        descending[i] = (double) i;

    Double[] shuffled = new Double[16];
    for (int i = 0; i < shuffled.length; i++)
        shuffled[i] = (double) i;
    Collections.shuffle(Arrays.asList(shuffled));

    System.out.println("Before GC");
    printAddresses("ascending", ascending);
    printAddresses("descending", descending);
    printAddresses("shuffled", shuffled);

    System.gc();
    System.out.println("\nAfter GC");
    printAddresses("ascending", ascending);
    printAddresses("descending", descending);
    printAddresses("shuffled", shuffled);

    System.gc();
    System.out.println("\nAfter GC 2");
    printAddresses("ascending", ascending);
    printAddresses("descending", descending);
    printAddresses("shuffled", shuffled);

}

public static void printAddresses(String label, Double[] array) {
    PrintWriter pw = new PrintWriter(System.out, true);
    pw.print(label + ": ");
    // GraphLayout.parseInstance((Object) array).toPrintable() has more info
    SortedSet<Long> addresses = GraphLayout.parseInstance((Object) array).addresses();
    Long first = addresses.first(), previous = first;
    pw.print(Long.toHexString(first));
    for (Long address : addresses) {
        if (address > first) {
            pw.print(Long.toHexString(address - previous) + ", ");
            previous = address;
        }
    }
    pw.println();
}

С помощью этого инструмента у меня примерно одинаковые результаты:

Before GC
# WARNING: Unable to attach Serviceability Agent. Unable to attach even with escalated privileges: null
ascending: 76d430c7850, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
descending: 76d430e4850, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
shuffled: 76d43101850, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 

After GC
ascending: 6c782859856d88, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
descending: 6c78285e856eb8, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
shuffled: 6c782863856fe8, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 

After GC 2
ascending: 6c7828570548a8, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
descending: 6c78285c0549d8, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
shuffled: 6c782861054b08, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 

Process finished with exit code 0

В этом примере http://hg.openjdk.java.net/code-tools/jol/file/018c0e12f70f/jol-samples/src/main/java/org/openjdk/jol/samples/JOLSample_21_Arrays.java вы можете проверить влияние GC на массивы.

UPD

Вы предоставили больше информации, я пытался помочь вам к тому времени. Сначала поймали глаза.

for(int a= 0 ; a < 123 ;a++){   
    i = 10;
}

Java достаточно умен, чтобы исключить этот цикл, поскольку результат всегда - одна команда "i = 10;". Например,

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.OperationsPerInvocation;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.OptionsBuilder;

@State(Scope.Benchmark)
public class TestLoop {

    static final int _123 = 123;
    int TEN = 10;

    @Benchmark
    @OperationsPerInvocation(_123)
    public void oneAssigment() {
        Integer i = 1;
        i = 10;
    }

    @Benchmark
    @OperationsPerInvocation(_123)
    public Integer oneAssigmentAndReturn() {
        Integer i = 1;
        i = TEN;
        return i;
    }

    @Benchmark
    @OperationsPerInvocation(_123)
    public void doWrong() {
        Integer i = 1;
        for (int a = 0; a < _123; a++) {
            i = 10;
        }
    }

    @Benchmark
    @OperationsPerInvocation(_123)
    public void doWrongWithLocalVariable() {
        Integer i = -1;
        for (int a = 0; a < _123; a++) {
            i = TEN;
        }
    }

    @Benchmark
    @OperationsPerInvocation(_123)
    public Integer doWrongWithResultButOneAssignment() {
        Integer i = -1;
        for (int a = 0; a < _123; a++) {
            i = TEN;
        }
        return i;
    }

    @Benchmark
    @OperationsPerInvocation(_123)
    public void doWrongWithConstant(Blackhole blackhole) {
        for (int a = 0; a < _123; a++) {
            blackhole.consume(10);
        }
    }

    @Benchmark
    @OperationsPerInvocation(_123)
    public void doRight(Blackhole blackhole) {
        for (int a = 0; a < _123; a++) {
            blackhole.consume(TEN);
        }
    }

    public static void main(String[] args) throws Exception {
        new Runner(
                new OptionsBuilder()
                        .include(TestLoop.class.getSimpleName())
                        .warmupIterations(10)
                        .measurementIterations(5)
                        .build()
        ).run();
    }


}

Предоставляет

Benchmark                                    Mode  Cnt             Score            Error  Units
TestLoop.doRight                            thrpt   50     352484417,380 ±    7015412,429  ops/s
TestLoop.doWrong                            thrpt   50  358755522786,236 ± 5981089062,678  ops/s
TestLoop.doWrongWithConstant                thrpt   50     345064502,382 ±    6416086,124  ops/s
TestLoop.doWrongWithLocalVariable           thrpt   50  179358318061,773 ± 1275564518,588  ops/s
TestLoop.doWrongWithResultButOneAssignment  thrpt   50   28834168374,113 ±  458790505,730  ops/s
TestLoop.oneAssigment                       thrpt   50  352690179375,361 ± 6597380579,764  ops/s
TestLoop.oneAssigmentAndReturn              thrpt   50   25893961080,851 ±  853274666,167  ops/s

Как вы можете видеть, ваш метод совпадает с одним присваиванием. См. Также: