from: http://www.cnblogs.com/whjiang/articles/1387364.html
1. 盡可能不要創建global reference和global weak reference. 創建這兩類引用的JNI接口NewGlobalReference和NewGlobalWeakReference內部實現有一個鎖。這個鎖使得在多處理器上的可擴展性非常差,因為各個線程都在等待這個鎖。所以盡量不要在native保存java 對象的引用,情愿在每次JNI call時都帶點參數。當然,在native保持java對象的local reference是非常危險的,絕對不能那樣干。2. 盡量不要使用GetPrimitiveArrayCritical/ReleasePrimitiveArrayCritical來pin住Java內存。JVM本身沒有提供任何只pin住一塊Java內存而不影響GC的操作,所以這個操作是會阻止GC進行的。作為補償,ReleasePrimitiveArrayCritical會產生一次隱式的GC調用。這樣就可能出現在需要GC的時候無法GC,而在不需要GC時進行無意義GC的情況。另外,這兩個操作的實現中在某些情況下也可能觸發鎖。解決方法:如果是小塊內存的話,情愿使用Get<Type>ArrayRegion和Set<Type>ArrayRegion來在native和Java之間復制內存。3. 在Java appliation中盡量不要創建phantom reference或者soft reference。這些reference會極大的影響GC。我們先來談談JVM的GC。GC分為minor GC和full GC。Java內存分為young和old兩代。在young memory中,每個線程都有自己的內存分配塊(不和其它線程共享),而old memory是由所有線程共享的。minor GC只對young memory作GC,而full GC對所有內存都做GC。minor GC是可以多線程并行進行的,而full GC默認只能單線程執行。所以,一次full GC需要的時間可以是minor GC是10倍以上(可以用-verbose:gc觀察)。所以在一般應用中,minor GC的次數應該是full GC的10倍左右是比較理想的。minor GC會將無法收集的對象移動到old memory中去。minor GC不會對phantom reference和soft reference進行收集,只有full GC才會。這樣的問題就是大量的這類對象積聚起來,產生許多的內存復制。
這樣每次minor GC可能就基本上沒有釋放多少內存,使得full GC就會被頻繁觸發。可能出現minor GC和full GC次數1:1的情況,甚至全是full GC。
這樣,無論是性能還是可擴展性都是非常差的。
weak reference的影響好像小一些,但也應該盡量避免。
在JNI開發中,使用這3種reference的主要目的是保證native資源的釋放。因為java對象的finalize方法是不保證被調用的,所以必須用這些
reference來幫助實現native資源釋放。為了避免上述的問題,一種可行的方法是將native資源serialize成一塊內存,然后放到java對象中保存,從而避免使用這些reference。4. NIO是不錯的Java和native之間share memory的方法。但要注意它的性能。首先一定要設置對big endian還是little endian,防止JVM做額外的endian轉換動作。其次是在啟動JVM時一定要加上-server選項,否則nio性能會非常差。在-server mode下,nio性能大概比java數組慢30%~50%.在-client mode下,性能差1倍以上。5. 盡量不要在JNI去new Java String對象。這個比在java層new慢很多。6. 對java大對象(比方說數組),一定要仔細的tune他的大小。一般來說,小對象對GC比較友好。因為對象分配時先看每個線程自己的young memory,如果找的到足夠大的內存的話,就分配。否則在old memory中分配。因為old memory是shared,所以可能有鎖開銷。而且old memory中的對象只有full GC才能釋放它。所以對象比較小是比較好的。JVM的內存分配算法是為小對象優化過的,大量小對象的分配是很高效的,所以不用怕把大對象拆小。在某些情況下,如果知道對象會活的很久的話,就讓它大一點,增加它直接分配在old memory中的概率。這樣可以節約young GC拷貝這個對象到old memory中的開銷。