ChronicleMap –具有堆外内存的Java体系结构
我的上一篇文章是在幾周前寫的,在收到一些有效的反饋后,我想澄清幾點,作為本文的序言。
“ 使用零垃圾創建數百萬個對象 ”的主要收獲應該是,使用Chronicle,在編寫Java程序時,您不會“局限于”使用jvm分配的堆內存。 也許這篇文章更恰當地標題為“使用零堆創建數百萬個對象”。 我想指出的另一點是,當您沒有堆內存時,不會引起GC活動。
我使用術語“垃圾”來描述分配在堆上的對象的事實使人感到困惑。 盡管分配的對象引起GC活動,但實際上它們不是垃圾。
我設計了一個示例來說明一個,一個是ChronicleMap不使用堆內存,而ConcurrentHashMap則使用第二個;當您使用堆內存時,您不能忽略GC。 至少您需要仔細調整系統,以確保您不會因為長時間的GC暫停而遭受痛苦。 這并不意味著從堆外分配沒有任何問題(請參閱本文的結尾),也并不意味著您無法通過堆上解決方案進行優化以消除GC。 擺脫困境絕不是解決所有Java性能問題的靈丹妙藥,但是對于非常具體的解決方案,它可以提供有趣的機會,我將在本文中討論其中的一些機會。
有時可能需要在JVM之間共享數據。
現在讓我們簡化一下,假設您有兩個JVM在同一臺計算機上運行,??其中一個或兩個都希望看到彼此的更新。 每個Java程序都有一個ConcurrentHashMap ,它將對其進行更新,這些更新將被存儲并在以后可供使用。 但是,程序如何將另一個Java程序應用的更新獲取到其映射?
從根本上講,不能在JVM之間直接共享HashMap和ConcurrentHashMap類的JDK堆集合。 這是因為堆內存由分配它的JVM包含。 因此,當JVM退出時,內存被釋放并且數據不再可用,因此沒有在JVM的生命周期之外持久保留內存的隱式方法。 因此,您需要找到其他機制來在JVM之間共享數據。 通常,您可以將數據庫用作外部共享存儲和消息傳遞服務,以將數據更新發送給其他進程,以通知他們某些數據已被更新。
這導致以下體系結構:
這種體系結構的問題是,使用會丟失HashMap的內存速度,特別是如果寫入數據庫的速度不是那么快,并且您希望在通過消息傳遞服務發送消息之前堅持寫操作。 同樣,許多解決方案都將涉及TCP調用,這可能再次成為延遲的源頭。
當然,與使用諸如將日志記錄到磁盤的機制(例如,使用ChronicleQueue之類的產品)寫入成熟的數據庫相比,存在持久化數據的方法要快得多。 但是,如果您確實使用日志,則仍然必須建立所有邏輯以在重新啟動時重新創建Map數據結構,更不用說必須在另一個JVM上保持Map類型結構的最新狀態。
(您可能根本想保留數據的原因是,這樣您應該能夠在重新啟動時進行恢復,而不必從源中重播所有數據)。 除了此體系結構引入的延遲之外,還必須處理數據庫和消息傳遞服務的額外代碼和配置。
即使接受這種功能都可以封裝在框架中,如果您的內存Map實際上在JVM外部可見,那不是很好。 Map應該能夠隱式持久化數據,以便其數據獨立于JVM的生存時間可用。 它應該允許使用與使用堆上映射相同的“內存”速度進行訪問。
這就是ChronicleMap用處ChronicleMap是java.util.ConcurrentMap的實現,但重要的是,它使用了堆外內存 ,該內存在JVM外部對于計算機上運行的任何其他進程都是可見的。 (有關堆上內存與堆外內存的討論,請參見此處 )。
每個JVM將創建一個指向相同的內存映射文件的ChronicleMap 。 當一個進程寫入其ChronicleMap ,另一個進程可以立即(約40納秒)在其ChronicleMap查看更新。 由于數據存儲在JVM外部的內存中,因此JVM退出不會導致任何數據丟失。 數據將保存在內存中(假設不需要分頁),并且在JVM重新啟動時,它可以非常快速地將其重新映射。 丟失數據的唯一方法是,如果操作系統崩潰而臟頁尚未保存到磁盤上,則該操作系統崩潰。 解決方案是Chronicle 支持的使用復制,但超出了本文的范圍。
其架構就是這樣的:
有關ChronicleMap入門的代碼示例,請參閱我的上一篇文章或此處的官方ChronicleMap教程。
在進入ChronicleMap之前,需要考慮很多注意事項和權衡取舍。
- ChronicleMap條目必須可序列化。 對于對性能非常敏感的系統,您需要實現Chronicle提供的稱為BytesMarshallable的自定義序列化。 盡管這很容易實現,但堆映射并不是必需的。 (話雖如此,將數據存儲到數據庫中當然也需要某種序列化方法。)
- 即使使用BytesMarshallable序列化,任何序列化的開銷對于某些系統而言也可能很重要。 在這種情況下,可以采用Chronicle支持的零拷貝技術(有關更多詳細信息,請參閱我的上一篇博客文章 ),以最大程度地降低序列化的成本。 但是,與使用“普通” Java相比,實現起來有些棘手。 另一方面,在對延遲敏感的程序中,它將具有不創建任何對象的巨大好處,這些對象隨后可能需要由GC清理。
- ChronicleMap不會調整大小,因此必須預先調整大小。 如果您不知道預期有多少個項目,可能會出現問題。 但是,應該指出的是,至少在Linux上,過度設置不是一個大問題,因為Linux被動分配內存。
- 編年史依靠操作系統異步刷新到磁盤。 如果要絕對確定數據已實際寫入磁盤(而不是僅保存在內存中),則需要復制到另一臺計算機。 實際上,任何關鍵任務系統都應復制到另一臺機器上,因此在采用《紀事報》時這可能不是大問題。
- ChronicleMap將受到操作系統內存分頁問題的影響。 如果內存已調出并且必須換回,則延遲會引入系統。 因此,即使您能夠創建大小遠遠超過主內存的ChronicleMaps,您也必須意識到根據數據訪問模式的不同,可能會發生分頁。
翻譯自: https://www.javacodegeeks.com/2015/04/chroniclemap-java-architecture-with-off-heap-memory.html
總結
以上是生活随笔為你收集整理的ChronicleMap –具有堆外内存的Java体系结构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jqgrid mvc_jqGrid,RE
- 下一篇: java核心面试_不正确的核心Java面