Java对象解决提取和验证问题

Gré*_*rin 12 java

为了某些研究目的,我想提取java对象的实际地址.为了清楚起见,我实际上想要对象的48位虚拟地址,而不是ID或哈希码或任何唯一标识符,我理解这些地址是由GC移动的.我一直在阅读stackoverflow中的其他帖子,就像这里或这里一样.

对于以下我使用@Peter Lawrey - >有没有办法获得参考地址?方法.所以它使用Unsafe该arrayBaseOffset方法的类.我对这些方法感到奇怪的是,它们为每次运行(至少在我的计算机上)给出相同的结果,这是不太可能发生的.出于安全原因,应该将内存分配随机化.

此外,我尝试使用Pintools验证这些方法,这是英特尔的仪器工具,我用它来提取运行的内存痕迹.我的问题是我无法将我在Pintools的内存跟踪中看到的内容与上述方法给出的地址相关联以获取内存地址.在我的内存跟踪中永远不会访问给定的地址.

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

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

===================大编辑:我意识到我的问题并不好,所以让我得到一个更原子的例子,这是我尝试分析的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);
    }
}`
Run Code Online (Sandbox Code Playgroud)

我从堆栈溢出中看到的不同方法得到了指向i Integer的指针.然后我在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]++;
    }
}
Run Code Online (Sandbox Code Playgroud)

使用这个pintools,我生成一个内存跟踪+直方图,记录每个内存地址的访问次数.注意:使用"follow_execv"选项启动pintool以便检测每个线程.

我看到2个问题:

1)我看不到任何打印的i地址(或接近该地址)的访问.我倾向于相信Pintools,因为我之前使用过很多但是Pintools可能无法在这里找到正确的地址.

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

我对这里的GC效果并不在意,可能是在第二步.我只想从我的java应用程序中提取原生地址,我非常肯定Pintools给了我.

ego*_*nko 3

\n

因此,当我使用 pintools 检测此运行时,使用与此处类似的脚本。我没有看到对提到的地址或附近地址执行任何访问

\n
\n\n

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

\n\n

要探索对象布局,您可以使用http://openjdk.java.net/projects/code-tools/jol。

\n\n
import org.openjdk.jol.info.GraphLayout;\n\nimport java.io.PrintWriter;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.SortedSet;\n\npublic class OrderOfObjectsAfterGCMain2 {\n    public static void main(String... args) {\n    Double[] ascending = new Double[16];\n    for (int i = 0; i < ascending.length; i++)\n        ascending[i] = (double) i;\n\n    Double[] descending = new Double[16];\n    for (int i = descending.length - 1; i >= 0; i--)\n        descending[i] = (double) i;\n\n    Double[] shuffled = new Double[16];\n    for (int i = 0; i < shuffled.length; i++)\n        shuffled[i] = (double) i;\n    Collections.shuffle(Arrays.asList(shuffled));\n\n    System.out.println("Before GC");\n    printAddresses("ascending", ascending);\n    printAddresses("descending", descending);\n    printAddresses("shuffled", shuffled);\n\n    System.gc();\n    System.out.println("\\nAfter GC");\n    printAddresses("ascending", ascending);\n    printAddresses("descending", descending);\n    printAddresses("shuffled", shuffled);\n\n    System.gc();\n    System.out.println("\\nAfter GC 2");\n    printAddresses("ascending", ascending);\n    printAddresses("descending", descending);\n    printAddresses("shuffled", shuffled);\n\n}\n\npublic static void printAddresses(String label, Double[] array) {\n    PrintWriter pw = new PrintWriter(System.out, true);\n    pw.print(label + ": ");\n    // GraphLayout.parseInstance((Object) array).toPrintable() has more info\n    SortedSet<Long> addresses = GraphLayout.parseInstance((Object) array).addresses();\n    Long first = addresses.first(), previous = first;\n    pw.print(Long.toHexString(first));\n    for (Long address : addresses) {\n        if (address > first) {\n            pw.print(Long.toHexString(address - previous) + ", ");\n            previous = address;\n        }\n    }\n    pw.println();\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

使用这个工具我得到了大致相同的结果:

\n\n
Before GC\n# WARNING: Unable to attach Serviceability Agent. Unable to attach even with escalated privileges: null\nascending: 76d430c7850, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \ndescending: 76d430e4850, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \nshuffled: 76d43101850, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \n\nAfter GC\nascending: 6c782859856d88, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \ndescending: 6c78285e856eb8, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \nshuffled: 6c782863856fe8, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \n\nAfter GC 2\nascending: 6c7828570548a8, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \ndescending: 6c78285c0549d8, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \nshuffled: 6c782861054b08, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \n\nProcess finished with exit code 0\n
Run Code Online (Sandbox Code Playgroud)\n\n

通过这个例子http://hg.openjdk.java.net/code-tools/jol/file/018c0e12f70f/jol-samples/src/main/java/org/openjdk/jol/samples/JOLSample_21_Arrays.java你可以测试GC对数组的影响。

\n\n

UPD

\n\n

您提供了更多信息,当时我已尽力帮助您。\n首先引起了我的注意

\n\n
for(int a= 0 ; a < 123 ;a++){   \n    i = 10;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

Java 足够聪明,可以消除这个循环,因为结果总是 - 一条指令“i = 10;”。例如,

\n\n
import org.openjdk.jmh.annotations.Benchmark;\nimport org.openjdk.jmh.annotations.OperationsPerInvocation;\nimport org.openjdk.jmh.annotations.Scope;\nimport org.openjdk.jmh.annotations.State;\nimport org.openjdk.jmh.infra.Blackhole;\nimport org.openjdk.jmh.runner.Runner;\nimport org.openjdk.jmh.runner.options.OptionsBuilder;\n\n@State(Scope.Benchmark)\npublic class TestLoop {\n\n    static final int _123 = 123;\n    int TEN = 10;\n\n    @Benchmark\n    @OperationsPerInvocation(_123)\n    public void oneAssigment() {\n        Integer i = 1;\n        i = 10;\n    }\n\n    @Benchmark\n    @OperationsPerInvocation(_123)\n    public Integer oneAssigmentAndReturn() {\n        Integer i = 1;\n        i = TEN;\n        return i;\n    }\n\n    @Benchmark\n    @OperationsPerInvocation(_123)\n    public void doWrong() {\n        Integer i = 1;\n        for (int a = 0; a < _123; a++) {\n            i = 10;\n        }\n    }\n\n    @Benchmark\n    @OperationsPerInvocation(_123)\n    public void doWrongWithLocalVariable() {\n        Integer i = -1;\n        for (int a = 0; a < _123; a++) {\n            i = TEN;\n        }\n    }\n\n    @Benchmark\n    @OperationsPerInvocation(_123)\n    public Integer doWrongWithResultButOneAssignment() {\n        Integer i = -1;\n        for (int a = 0; a < _123; a++) {\n            i = TEN;\n        }\n        return i;\n    }\n\n    @Benchmark\n    @OperationsPerInvocation(_123)\n    public void doWrongWithConstant(Blackhole blackhole) {\n        for (int a = 0; a < _123; a++) {\n            blackhole.consume(10);\n        }\n    }\n\n    @Benchmark\n    @OperationsPerInvocation(_123)\n    public void doRight(Blackhole blackhole) {\n        for (int a = 0; a < _123; a++) {\n            blackhole.consume(TEN);\n        }\n    }\n\n    public static void main(String[] args) throws Exception {\n        new Runner(\n                new OptionsBuilder()\n                        .include(TestLoop.class.getSimpleName())\n                        .warmupIterations(10)\n                        .measurementIterations(5)\n                        .build()\n        ).run();\n    }\n\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

会提供

\n\n
Benchmark                                    Mode  Cnt             Score            Error  Units\nTestLoop.doRight                            thrpt   50     352484417,380 \xc2\xb1    7015412,429  ops/s\nTestLoop.doWrong                            thrpt   50  358755522786,236 \xc2\xb1 5981089062,678  ops/s\nTestLoop.doWrongWithConstant                thrpt   50     345064502,382 \xc2\xb1    6416086,124  ops/s\nTestLoop.doWrongWithLocalVariable           thrpt   50  179358318061,773 \xc2\xb1 1275564518,588  ops/s\nTestLoop.doWrongWithResultButOneAssignment  thrpt   50   28834168374,113 \xc2\xb1  458790505,730  ops/s\nTestLoop.oneAssigment                       thrpt   50  352690179375,361 \xc2\xb1 6597380579,764  ops/s\nTestLoop.oneAssigmentAndReturn              thrpt   50   25893961080,851 \xc2\xb1  853274666,167  ops/s\n
Run Code Online (Sandbox Code Playgroud)\n\n

正如您所看到的,您的方法与一项作业相同。也可以看看:

\n\n\n