2017-08-04 74 views
12

我想提取一些研究目的的java对象的实际地址。为了清楚起见,我实际上需要对象的48位虚拟地址,而不是ID或散列码或任何唯一标识符,并且我明白这些地址是由GC移动的。我一直在阅读其他帖子,比如herehereJava对象地址提取和验证

对于以下我使用@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.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); 
    } 
}` 

我得到的指针我从堆栈溢出中看到的不同方法的整数。然后,我在一个循环中对任意数量的时间进行循环,以便我可以在我的记忆痕迹中识别出它(注意:我检查了此代码中没有发生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给我的。

+0

您可能想要包含您的代码 –

+0

'由于安全原因,内存分配应该是随机分配的。'。你有任何参考这实际发生在Java?这个语句在C中有一定的意义,缓冲区溢出可能导致代码执行。 –

+0

添加了一些代码,如果这有帮助! –

回答

3

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

我想你应该提供更多的信息,你如何运行以及看到了什么。

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

正如你所看到的,你的方法是一样的一个任务。另见:

+0

对不起,随着你的回答,我意识到我不是很清楚,所以我编辑了我的帖子,谢谢你的回答。很高兴看到这些方法已被其他工具证实。也许,Pintools不仅能正常使用Java应用程序,也不能检索正确的地址 –

+0

GrégoryVaumourin我浏览了更新的帖子并添加了信息。 – egorlitvinenko