2017-10-13 70 views
2

考慮下面的代碼示例:JMM保證調用

try (AutoClosable closable = new XXX()) { 
     o.method1(closable); 
     o.method2(); 
} 

難道Java內存模型允許熱點重新排序closable.close()o.method2()過嗎?

我故意忽略像這樣的執行細節嗎?方法1捕獲可關閉嗎?在這個問題的第一部分。


我spefic的使用情況是這樣的:

我有一個C庫,可以作爲總結:

static char* value; 

void capture(char* ptr){ 
     value = ptr; 
} 

int len(void) { 
    return strlen(value); 
} 

此機庫使用JNA

interface CApi extends Library { 

static { 
    Native.register("test.so", CApi.class) 
} 

    void capture(Pointer s); 
    int test(); 
} 

包裹我的原始客戶代碼看起來像這樣:

cApi = Native.loadLibrary("test.so", CApi.class); 


byte[] data = Native.toByteArray("foo"); 
Memory m = new Memory(data.length + 1); 
m.write(0, data, 0, data.length); 
m.setByte(data.length, (byte)0); 

cApi.capture(m); 
System.out.print(cApi.len()); 

第一個版本的問題是已經從Java分配了m,並且此內存的生命週期與m緊密聯繫。 m不再是強可一旦當GC踢(JNA依靠finalize釋放本機內存)

爲了防止這一點,我提出要繼承JNA的Memory引入一個AutoClosableMemorycapture(m)和內存將被釋放。這個想法是,使用try-with-resource構造會清楚地表明資源在這個範圍內是強烈可達的。我們可以釋放原生記憶的好處就是我們不再需要它,而不必等待GC(嘿,那是RAII!)。

try (AutoClosableMemory m = [...]) { 
    cApi.capture(m); 
    cApi.test(); 
} 

我保證這m.close()永遠cApi.test()之前和m是強可調用?在這種情況下,底層C API捕獲了m,但是java編譯器無法知道它。

回答

4

理論上,JVM可以對指令進行重新排序,但方法的結果必須保持正確。 HotSpot不理解本機代碼,爲了保證正確性,它不會對本機指令進行重新排序。

+0

我一直在尋找類似於'它從不重新排列代碼圍繞本地指令'的東西,你有鏈接嗎? – Oleg

+1

原生代碼塊在JLS中被形式化爲外部操作。它們意味着在與之前和之後的代碼關係之前發生。我曾經給過這個介紹,你可能會發現有幫助:https://youtu.be/XgiXKPEILoc我在第40分鐘談到外部行爲。 –

+1

謝謝,優秀的談話。我想你應該把它添加到你的答案。你在[這裏](https://youtu.be/XgiXKPEILoc?t=40m37s)中談論的內容恰恰解決了OP的問題。 – Oleg

1

對於一個線程,一切都保證以與您的代碼相同的順序運行(任何發生的重新排序都不會影響您的程序)。重新排序只能影響其他線程看到會發生什麼。在你的問題只有一個線程,所以你的程序保證在m.close()之前調用cApi.test()

+0

這也是我的理解。但是,我想知道什麼特別會阻止編譯器重新排序關閉。從Java的角度來看,它們在兩個語句之間沒有依賴關係,因此移動關閉不會中斷_as-if-serial_。 –

+0

@ClémentMATHIEU那麼,如果它會影響你的程序,那麼它*會*打破as-if-serial。我會盡力找到更具體的東西,但通常都是如此。這不是關於Java的觀點,而是JVM和CPU,他們知道他們可以逃避什麼。 – Oleg