2014-09-12 56 views
1

我正在設計一個JNI接口,將字符串參數從Java傳遞給C++。我需要高性能,並且能夠使用Direct ByteBuffer和String.getBytes()很好地完成這項工作,但將字符串傳遞給C/C++的代價仍然相當高。我最近閱讀了關於Open JDK的Unsafe類。 This excellent page讓我開始了,但我發現不安全是可悲的,但可以理解的是記錄不完善。固定不安全的指針

我想知道,如果我使用不安全類獲取指向字符串的指針並將其傳遞給C++,那麼在輸入C++代碼之前是否存在該對象已移動的風險?即使在C++正在執行?還是由不安全代碼提供的這些地址固定?如果他們不固定,這些不安全指針如何有用?

+0

如果您發佈了一些代碼,它將有很大的幫助。特別是,它將有助於瞭解C++代碼是否從Java位同步調用,或者是否有多個線程。另外,通過指向字符串的指針是指'char *'還是'std :: string *'?用代碼回答很多! – Luis 2014-09-12 19:43:29

+0

看來懲罰是從UTF-16轉換爲你的C++代碼使用的任何編碼。如果您的C++代碼可以處理已計數(未終止)的UTF-16,則不需要使用'GetStringChars'和'GetStringLength'進行轉換或複製。這不是你所說的「指向一個字符串的指針?」 – 2014-09-13 21:44:03

+0

湯姆。即使你對它沒有做任何事情,從Java-> JNI-> C++傳遞一個對象(包括字符串)似乎會有性能損失。這個成本明顯高於通過很長時間的成本。我不確定這個成本是否僅僅是因爲創造了一個隱含的本地參考。不管什麼原因,這個代價是痛苦的。另外GetStringChars也有一個可以衡量的成本。我想我讀過那是因爲有人收到一個指向字節COPY的指針......或者該對象必須被固定。無論哪種方式,你可以想象,造成延遲。 – user3624334 2014-09-16 18:40:51

回答

1

不安全並不意味着與JNI互操作。所以通過不安全獲得可能會改變任何時間(即使與您的C++並行)。

JNI API能夠將內存中的對象固定到內存中以訪問陣列內容(在HotSpot JVM中,它會阻止GC,因此可能會對GC暫停持續時間產生負面影響)。

特別是,Get * ArrayElements會固定數組,直到您顯式執行Release * ArrayElements。 GetStringChars的工作方式類似。

直接字節緩衝區保存指向堆外的內存緩衝區的指針,但是此緩衝區不移動,您可以訪問它以獲得本機代碼。

1

我讀了java.misc.Unsafe的Java source,並有更多的見解。

不安全有至少兩種處理內存的方式。

  1. allocateMemory/reallocateMemory/freeMemory /等 - 據我可以告訴這個分配的內存外堆所以沒有面臨GC'ing挑戰。我已經間接測試了這一點,看起來很長的返回只是一個指向內存的指針。這種類型的內存很可能很安全地通過JNI傳遞給本地代碼。應用程序Java代碼應該能夠在JNI調用之前和之後快速修改/查詢它,方法是使用支持此類型內存指針的其他一些內置Unsafe方法。

  2. object + offset - 這些方法接受一個指向對象的指針和一個「偏移」標記,以指示對象在何處獲取/修改該值。這些對象大概總是在Java堆中,但將對象傳遞給這些方法可能有助於解決GC併發症。它聽起來像「偏移量」有時是一個「餅乾」,而不是一個實際的偏移量,但它也聽起來像在數組的情況下,arrayBaseOffset()返回一個「偏移量」,可以算術操縱。我不知道這個對象+偏移對於JNI代碼是否安全。我沒有看到一種方法可以直接生成一個指向堆中的Java對象的指針,而該對象可以(危險地)通過JNI。可以傳遞一個對象和偏移量,但是考慮到通過JNI傳遞對象的成本,這種方法無論如何都不具吸引力。

贊(1),與我在帖子中引用的頁面相關聯的code可能是用於JNI的相互作用非常安全的。它在處理String時採用對象+偏移方法,但在處理直接ByteBuffer時使用方法(1),它總是位於Java堆外部。 Direct ByteBuffer的JNI非常友好,並且通常可以避免JNI Object傳遞成本的方式,這些成本是我在上面對Tom的評論中提到的。