2017-08-04 13 views
12

Tôi muốn trích xuất địa chỉ thực tế của các đối tượng java cho một số mục đích nghiên cứu. Chỉ cần rõ ràng, tôi thực sự muốn địa chỉ ảo 48bits của đối tượng, không phải ID hoặc mã băm hoặc bất kỳ mã định danh duy nhất nào và tôi hiểu rằng các địa chỉ đó được chuyển xung quanh bởi GC. Tôi đã đọc các bài đăng khác từ stackoverflow như here hoặc here.Khai thác địa chỉ đối tượng Java và xác minh

Đối với những điều sau, tôi sử dụng phương pháp @Peter Lawrey -> Is there a way to get a reference address?. Vì vậy, nó sử dụng lớp Unsafe với phương thức arrayBaseOffset. Những gì tôi thấy lạ về những phương pháp đó là họ đưa ra kết quả tương tự cho mỗi lần chạy (ít nhất là trên máy tính của tôi) mà rất khó xảy ra. Phân bổ bộ nhớ được cho là ngẫu nhiên vì lý do bảo mật.

Hơn nữa, tôi đã thử xác minh những phương pháp đó với Pintools là công cụ thiết bị đo đạc từ Intel mà tôi đã sử dụng để trích xuất các dấu vết bộ nhớ của chạy. Vấn đề của tôi là tôi không thể tương quan với những gì tôi thấy trong dấu vết bộ nhớ của Pintools với các địa chỉ được đưa ra bởi các phương thức trên để lấy địa chỉ bộ nhớ. Các địa chỉ đã cho không bao giờ được truy cập trong dấu vết bộ nhớ của tôi.

Vì vậy, tôi tự hỏi những gì được trả về bởi những phương pháp đó và cách những kết quả đó đã được xác minh nó so với các công cụ khác.

Một số infos: OS của tôi là một x86_64 Ubuntu, JVM của tôi là 64bits OpenJDK 1.8.0_131, pintools phiên bản v3.2

================ === Big Edit: tôi nhận ra rằng câu hỏi của tôi cũng không phải là đặt, vì vậy hãy để tôi có được một ví dụ nguyên tử hơn, đây là java mà tôi cố gắng phân tích:

`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); 
    } 
}` 

tôi nhận được con trỏ tới i Integer từ các phương thức khác nhau mà tôi đã thấy trên stack overflow. Sau đó, tôi lặp lại i trong một khoảng thời gian tùy ý để tôi có thể nhận ra nó trong dấu vết bộ nhớ của mình (Lưu ý: Tôi đã kiểm tra xem không có cuộc gọi GC nào xảy ra trong mã này)

Khi pintools thấy cụ thể "! = 1" ghi trong đầu ra tiêu chuẩn, nó bắt đầu/ngừng thiết bị đo đạc

trên mỗi truy cập trong giai đoạn thiết bị đo đạc, tôi thực thi mã này:

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]++; 
    } 
} 

với pintools này, tôi tạo ra một dấu vết bộ nhớ + một biểu đồ tần suất về cách nhiều lần mỗi địa chỉ bộ nhớ được truy cập. Lưu ý: pintool được khởi chạy với tùy chọn "follow_execv" để phát từng chuỗi.

Tôi thấy 2 Sự cố:

1) Tôi không thấy quyền truy cập vào bất kỳ địa chỉ in nào (hoặc gần địa chỉ này). Tôi có xu hướng tin tưởng Pintools vì tôi đã sử dụng khá nhiều trước đây nhưng có lẽ Pintools không thể truy xuất địa chỉ chính xác ở đây.

2) Tôi không thấy địa chỉ nào được truy cập 123 lần (hoặc gần với địa chỉ này). Suy nghĩ của tôi cho điều này là có lẽ JVM thực hiện tối ưu hóa ở đây vì nó thấy rằng mã được thực thi không có hiệu lực nên nó không thực thi nó. Tuy nhiên, tôi đã thử với một lệnh phức tạp hơn (không thể được tối ưu hóa như lưu trữ một số ngẫu nhiên) bên trong vòng lặp hơn là chỉ một cửa hàng cho i mà không có kết quả tốt hơn.

Tôi không quan tâm nhiều về hiệu ứng GC ở đây, có thể trong bước thứ hai. Tôi chỉ muốn có thể trích xuất các địa chỉ gốc từ ứng dụng java của mình mà tôi khá chắc chắn Pintools đang cho tôi.

+0

Bạn có thể muốn bao gồm mã của mình –

+0

'phân bổ bộ nhớ được cho là ngẫu nhiên vì lý do bảo mật'. Bạn có bất kỳ tham chiếu này thực sự xảy ra trong java? Câu lệnh này có ý nghĩa trong C, nơi tràn bộ đệm có thể dẫn đến việc thực thi mã. –

+0

Đã thêm một số mã nếu điều này hữu ích! –

Trả lời

3

Vì vậy, khi tôi đang thiết kế trang này chạy với pintools, với tập lệnh tương tự như ở đây. Tôi không thấy bất kỳ quyền truy cập nào được thực hiện trên các địa chỉ được đề cập hoặc địa chỉ lân cận

Tôi nghĩ bạn nên cung cấp thêm thông tin về cách bạn chạy và những gì bạn thấy.

Để khám phá bố cục đối tượng, bạn có thể sử dụng 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(); 
} 

Với công cụ này, tôi có khoảng kết quả tương tự:

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 

Với ví dụ này http://hg.openjdk.java.net/code-tools/jol/file/018c0e12f70f/jol-samples/src/main/java/org/openjdk/jol/samples/JOLSample_21_Arrays.java bạn có thể kiểm tra GC ảnh hưởng trên mảng.

UPD

Bạn đã cung cấp thêm thông tin, tôi đã cố gắng trợ giúp bạn vào thời điểm đó. đầu tiên đánh bắt mắt của tôi

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

Java là đủ thông minh để loại bỏ chu kỳ này, vì kết quả luôn luôn là - một hướng dẫn "i = 10;". Ví dụ,

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(); 
    } 


} 

sẽ cung cấp

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 

Như bạn có thể thấy, phương pháp của bạn cũng giống như một bài tập. Xem thêm:

+0

Xin lỗi, với câu trả lời của bạn, tôi nhận ra tôi không hoàn toàn rõ ràng, vì vậy tôi đã chỉnh sửa bài đăng của tôi, Cảm ơn câu trả lời của bạn. Nó là tốt để thấy rằng các phương pháp này đã được xác nhận đối với các công cụ khác.Có lẽ, Pintools không chỉ hoạt động bình thường với các ứng dụng Java và không thể truy xuất địa chỉ chính xác –

+0

GrégoryVaumourin Tôi đã đi qua bài đăng cập nhật của bạn và thêm thông tin. – egorlitvinenko

Các vấn đề liên quan