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