轉自: Imcache :一個Java新的緩存框架
堆Heap是內存中動態分配對象居住的地方。
如果使用new一個對象,它就被分配在堆內存上。
這是相對于Stack,如果你有一個局部變量則它是位于Stack棧內存空間。
BigMemory是用來避免GC對堆的開銷,從幾MB或GB大。
BigMemory通過直接的ByteBuffers使用JVM進程的內存地址空間,不像其他原生Java對象接受GC管束。
EHCache(Terrcotta BigMemory)的 off-heap將你的對象從堆中脫離出來序列化,然后存儲在一大塊內存中,這就像它存儲到磁盤上上一樣,但它仍然在RAM中。
對象在這種狀態下不能直接使用,它們必須首先反序列化。
也不受垃圾收集。序列化和反序列化會影響性能。(FST-serialization還是很快)。
使用堆外內存能夠降低GC導致的暫停。
應用場景:
Session會話緩存,保存不激活的用戶session,比如用戶沒有正常退出,我們也無法確定他會不會短時間內再回來,將其會話存到堆外內存。一旦再次登錄,無需訪問數據庫可再次激活。
計算結果的緩存,大量查詢的結果等,擊中率比較低的都可以遷移到堆外。
原文地址:http://www.infoq.com/cn/news/2014/12/external-memory-heap-memory/
一般情況下,Java中分配的非空對象都是由Java虛擬機的垃圾收集器管理的,也稱為堆內內存(on-heap memory)。
虛擬機會定期對垃圾內存進行回收,在某些特定的時間點,它會進行一次徹底的回收(full gc)。
徹底回收時,垃圾收集器會對所有分配的堆內內存進行完整的掃描,這意味著一個重要的事實——這樣一次垃圾收集對Java應用造成的影響,跟堆的大小是成正比的。
過大的堆會影響Java應用的性能。
對于這個問題,一種解決方案就是使用堆外內存(off-heap memory)。
堆外內存意味著把內存對象分配在Java虛擬機的堆以外的內存,這些內存直接受操作系統管理(而不是虛擬機)。
這樣做的結果就是能保持一個較小的堆,以減少垃圾收集對應用的影響。
但是Java本身也在不斷對堆內內存的實現方式做改進。
兩者各有什么優缺點?Vanilla Java博客作者Peter Lawrey撰寫了一篇文章,在文中他對三種方式:
用new來分配對象
對象池(object pool)
堆外內存,
進行了詳細的分析。
用new來分配對象內存是最基本的一種方式,Lawery提到:
在Java 5.0之前,分配對象的代價很大,以至于大家都使用內存池。但是從5.0開始,對象分配和垃圾回收變得快多了,研發人員發現了性能的提升,紛紛簡化他們的代碼,不再使用內存池,而直接用new來分配對象。從5.0開始,只有一些分配代價較大的對象,比如線程、套接字和數據庫鏈接,用內存池才會有明顯的性能提升。
對于內存池,Lawery認為它主要用于兩類對象。
第一類是生命周期較短,且結構簡單的對象,在內存池中重復利用這些對象能增加CPU緩存的命中率,從而提高性能。
第二種情況是加載含有大量重復對象的大片數據,此時使用內存池能減少垃圾回收的時間。對此,Lawery還以StringInterner為例進行了說明。
最后Lawery分析了堆外內存,它和內存池一樣,也能縮短垃圾回收時間,但是它適用的對象和內存池完全相反。內存池往往適用于生命期較短的可變對象,而生命期中等或較長的對象,正是堆外內存要解決的。
堆外內存有以下特點:
- 對于大內存有良好的伸縮性
- 對垃圾回收停頓的改善可以明顯感覺到
- 在進程間可以共享,減少虛擬機間的復制
Lawery還提到對外內存最重要的還不是它能改進性能,而是它的確定性。
當然堆外內存也有它自己的問題,最大的問題就是你的數據結構變得不那么直觀,如果數據結構比較復雜,就要對它進行串行化(serialization),而串行化本身也會影響性能。
另一個問題是由于你可以使用更大的內存,你可能開始擔心虛擬內存(即硬盤)的速度對你的影響了。
Lawery還介紹了OpenHFT公司提供三個開源庫:
Chronicle Queue、
Chronicle Map
Thread Affinity
這些庫可以幫助開發人員使用堆外內存來保存數據。采用堆外內存有很多好處,同時也帶來挑戰,
對堆外內存感興趣的讀者可以閱讀Lawery的原文來了解更多信息。