有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

Java对象地址提取和验证

出于某些研究目的,我想提取java对象的实际地址。需要明确的是,我实际上想要对象的48位虚拟地址,而不是ID或哈希代码或任何唯一标识符,我知道这些地址是由GC移动的。我一直在读stackoverflow的其他帖子,比如herehere

对于以下内容,我使用@Peter Lawrey -> Is there a way to get a reference address?方法。因此它使用Unsafe类和arrayBaseOffset方法。我发现这些方法的奇怪之处在于,它们每次运行(至少在我的计算机上)都会给出相同的结果,这是不太可能发生的。出于安全原因,内存分配应该是随机的

此外,我还尝试用Pintools验证这些方法,这是英特尔的检测工具,我用来提取运行的内存跟踪。我的问题是,我无法将Pintools的内存跟踪中看到的内容与上述方法给出的地址关联起来,以获得内存地址。我的内存跟踪中从未访问过给定的地址

所以我想知道这些方法返回了什么,以及这些结果是如何与其他工具进行验证的

一些信息:我的操作系统是Ubuntu x86_64,我的JVM是openJDK 64位1.8.0_131,pintools版本是v3。二,

=================== 大编辑:我意识到我的问题没有很好地表达出来,所以让我举一个更原子化的例子,下面是我试图分析的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);
    }
}`

我从堆栈溢出上看到的不同方法中获得指向I整数的指针。然后我在I上循环任意次数,这样我就可以在内存跟踪中识别它(注意:我检查了这段代码中没有发生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”选项启动的,以便为每个线程插入仪器

我看到两个问题:

1)我看不到任何打印的I地址(或接近此地址)的访问权限。我倾向于相信Pintools,因为我以前使用过很多,但也许Pintools无法在这里检索到正确的地址

2)我看不到地址被访问123次(或接近此次数)。我的想法是,可能JVM在这里执行优化,因为它看到执行的代码没有效果,所以它不会执行它。然而,我尝试在循环中使用更复杂的指令(不能像存储随机数那样进行优化),而不仅仅是存储到I,没有更好的结果

我不太关心GC效应,也许在第二步。我只希望能够从我的java应用程序中提取本机地址,我非常确定Pintools给了我这个应用程序


共 (1) 个答案

  1. # 1 楼答案

    So when I am instrumenting this run with pintools, with a similar script as here . I don't see any access performed on the mentionned addresses or nearby addresses

    我认为你应该提供更多关于你如何跑步以及你看到了什么的信息

    要探索对象布局,可以使用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
    

    正如你所见,你的方法和一个作业是一样的。另见: