2017-08-04 76 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