如何分析堆外内存使用情况_堆上与堆外的内存使用情况
如何分析堆外內(nèi)存使用情況
總覽
最近有人問我在Java中使用堆內(nèi)存的好處和智慧。 面臨相同選擇的其他人可能會(huì)對(duì)這些答案感興趣。
堆外內(nèi)存沒什么特別的。 線程堆棧,應(yīng)用程序代碼,NIO緩沖區(qū)都在堆外。 實(shí)際上,在C和C ++中,您只有非托管內(nèi)存,因?yàn)槟J(rèn)情況下它沒有托管堆。 Java中托管內(nèi)存或“堆”的使用是該語言的一個(gè)特殊功能。 注意:Java不是執(zhí)行此操作的唯一語言。
新的Object()vs對(duì)象池vs Off堆內(nèi)存
新的Object()
在Java 5.0之前,使用對(duì)象池非常流行。 創(chuàng)建對(duì)象仍然非常昂貴。 但是,從Java 5.0開始,對(duì)象分配和垃圾清理變得便宜得多,并且開發(fā)人員發(fā)現(xiàn),通過刪除對(duì)象池并僅在需要時(shí)創(chuàng)建新對(duì)象,它們可以提高性能,并簡化代碼。 在Java 5.0之前,幾乎所有對(duì)象池,甚至是使用對(duì)象的對(duì)象池都提供了改進(jìn),而從Java 5.0池中,只有昂貴的對(duì)象才有意義,例如線程,套接字和數(shù)據(jù)庫連接。
對(duì)象池
在低延遲空間中,顯而易見的是,通過減少對(duì)CPU緩存的壓力,回收可變對(duì)象可以提高性能。 這些對(duì)象必須具有簡單的生命周期和簡單的結(jié)構(gòu),但是使用它們可以看到性能和抖動(dòng)方面的顯著改善。
使用對(duì)象池的另一個(gè)有意義的領(lǐng)域是當(dāng)使用許多重復(fù)的對(duì)象加載大量數(shù)據(jù)時(shí)。 隨著內(nèi)存使用量的顯著減少以及GC必須管理的對(duì)象數(shù)量的減少,您看到了GC時(shí)間的減少和吞吐量的增加。
這些對(duì)象池的設(shè)計(jì)比使用同步HashMap的對(duì)象池更輕巧,因此它們?nèi)匀挥袔椭?
以這個(gè)StringInterner類為例。 您將所需文本的可循環(huán)使用的可變StringBuilder作為字符串傳遞給它,它將提供匹配的字符串。 傳遞字符串會(huì)很無效率,因?yàn)槟呀?jīng)創(chuàng)建了對(duì)象。 StringBuilder可以回收。
注意:此結(jié)構(gòu)具有一個(gè)有趣的屬性,除了最低Java保證提供的安全性之外,不需要任何其他線程安全功能(例如volatile或sync)。 也就是說,您可以正確地看到String中的final字段,并且只能讀取一致的引用。
public class StringInterner {private final String[] interner;private final int mask;public StringInterner(int capacity) {int n = Maths.nextPower2(capacity, 128);interner = new String[n];mask = n - 1;}private static boolean isEqual(@Nullable CharSequence s, @NotNull CharSequence cs) {if (s == null) return false;if (s.length() != cs.length()) return false;for (int i = 0; i < cs.length(); i++)if (s.charAt(i) != cs.charAt(i))return false;return true;}@NotNullpublic String intern(@NotNull CharSequence cs) {long hash = 0;for (int i = 0; i < cs.length(); i++)hash = 57 * hash + cs.charAt(i);int h = (int) Maths.hash(hash) & mask;String s = interner[h];if (isEqual(s, cs))return s;String s2 = cs.toString();return interner[h] = s2;} }堆外內(nèi)存使用率
使用堆外內(nèi)存和使用對(duì)象池都有助于減少GC暫停,這是它們唯一的相似之處。 對(duì)象池適用于短暫的可變對(duì)象,創(chuàng)建對(duì)象的成本高以及存在大量重復(fù)的不可變對(duì)象的長壽命。 中度活潑的可變對(duì)象或復(fù)雜的對(duì)象更有可能由GC處理。 但是,中長壽命的可變對(duì)象遭受堆外內(nèi)存解決的許多方式的困擾。
堆外內(nèi)存提供;
- 可擴(kuò)展至大型內(nèi)存,例如超過1 TB且大于主內(nèi)存。
- 名義上對(duì)GC暫停時(shí)間的影響。
- 進(jìn)程之間共享,減少JVM之間的重復(fù),并使分裂JVM更容易。
- 持久性,可以更快地重新啟動(dòng)或回復(fù)測試中的生產(chǎn)數(shù)據(jù)。
在設(shè)計(jì)系統(tǒng)方面,堆外內(nèi)存的使用為您提供了更多選擇。 最重要的改進(jìn)不是性能,而是確定性。
堆外和測試
高性能計(jì)算中的最大挑戰(zhàn)之一是再現(xiàn)難以理解的錯(cuò)誤,并能夠證明已解決了這些錯(cuò)誤。 通過以持久方式將所有輸入事件和數(shù)據(jù)存儲(chǔ)在堆外,您可以將關(guān)鍵系統(tǒng)轉(zhuǎn)變?yōu)橐幌盗袕?fù)雜的狀態(tài)機(jī)。 (或者在簡單的情況下,只有一個(gè)狀態(tài)機(jī))通過這種方式,您可以在測試和生產(chǎn)之間獲得可重現(xiàn)的行為和性能。
許多投資銀行都使用這種技術(shù)來對(duì)一天中的任何事件可靠地重播系統(tǒng),并確切地說明了該事件按原樣處理的原因。 更重要的是,一旦你有了一個(gè)位置,你可以證明你有固定發(fā)生在生產(chǎn),而不是發(fā)現(xiàn)問題,并希望這是問題的問題。
確定性行為與確定性行為一起出現(xiàn)。 在測試環(huán)境中,您可以按照實(shí)際的時(shí)間重播事件,并顯示期望在生產(chǎn)中獲得的延遲分布。 如果硬件不相同,某些系統(tǒng)抖動(dòng)將無法重現(xiàn),但是從統(tǒng)計(jì)角度看您可能會(huì)非常接近。 為了避免花一天時(shí)間重放一天的數(shù)據(jù),您可以添加一個(gè)閾值。 例如,如果事件之間的時(shí)間超過10毫秒,則您可能只能等待10毫秒。 這樣一來,您就可以在不到一個(gè)小時(shí)的時(shí)間內(nèi)按實(shí)際時(shí)間重播一天的事件,并查看您的更改是否改善了延遲分配。
通過降低級(jí)別,您不會(huì)失去“一次編譯,隨處運(yùn)行”的某些功能嗎?
在某種程度上,這是正確的,但遠(yuǎn)沒有您想象的要多。 當(dāng)您靠近處理器工作時(shí),您將更加依賴處理器或OS的行為方式。 幸運(yùn)的是,大多數(shù)系統(tǒng)使用AMD / Intel處理器,就其提供的低級(jí)別保證而言,甚至ARM處理器也變得更加兼容。 操作系統(tǒng)之間也存在差異,并且這些技術(shù)在Linux上比Windows上更有效。 但是,如果您在MacOSX或Windows上進(jìn)行開發(fā)并使用Linux進(jìn)行生產(chǎn),則應(yīng)該沒有任何問題。 這就是我們?cè)诟哳l交易中所做的。
我們通過使用堆產(chǎn)生了哪些新問題?
沒有什么是免費(fèi)的,堆外情況就是這樣。 堆外的最大問題是您的數(shù)據(jù)結(jié)構(gòu)變得不太自然。 您或者需要一個(gè)可以直接映射到堆外的簡單數(shù)據(jù)結(jié)構(gòu),或者需要一個(gè)序列化和反序列化的復(fù)雜數(shù)據(jù)結(jié)構(gòu)以使其脫離堆。 顯然使用序列化有其自身的麻煩和性能損失。 因此,使用序列化要比在堆對(duì)象上慢得多。
在金融世界中,最昂貴的數(shù)據(jù)結(jié)構(gòu)是平坦且簡單的,充滿了原語,可以很好地從堆中映射出很少的開銷。 但是,這并不適用于所有應(yīng)用程序,您可以獲得復(fù)雜的嵌套數(shù)據(jù)結(jié)構(gòu)(例如圖形),最終還必須在堆上緩存某些對(duì)象。
另一個(gè)問題是,JVM限制了您可以使用的系統(tǒng)數(shù)量。 您不必?fù)?dān)心JVM會(huì)使系統(tǒng)過載太多。 有了堆外處理,就解除了一些限制,您可以使用比主內(nèi)存大得多的數(shù)據(jù)結(jié)構(gòu),并且開始擔(dān)心如果要這樣做,您將擁有哪種磁盤子系統(tǒng)。 例如,您不希望分頁至具有80 IOPS的HDD,而您可能希望具有80,000 IOPS(每秒輸入/輸出操作)或更高(即快1000倍)的SSD。
OpenHFT如何提供幫助?
OpenHFT有許多庫可以隱藏您實(shí)際上正在使用本機(jī)內(nèi)存來存儲(chǔ)數(shù)據(jù)的事實(shí)。 這些數(shù)據(jù)結(jié)構(gòu)是持久的,幾乎沒有垃圾就可以使用。 這些用于不需少量收集即可全天運(yùn)行的應(yīng)用程序中
編年史隊(duì)列 -持久的事件隊(duì)列。 支持同一臺(tái)機(jī)器上跨JVM的并發(fā)編寫器,以及跨機(jī)器并發(fā)讀取器。 微秒級(jí)的延遲和每秒數(shù)百萬條消息的持續(xù)吞吐量。
編年史地圖 –鍵值地圖的本機(jī)或持久存儲(chǔ)。 可以在同一臺(tái)機(jī)器上的JVM之間共享,通過UDP或TCP復(fù)制和/或通過TCP遠(yuǎn)程訪問。 微秒級(jí)延遲和持續(xù)的讀/寫速率,每臺(tái)機(jī)器每秒可進(jìn)行數(shù)百萬次操作。
線程親和性 –將關(guān)鍵線程綁定到隔離的內(nèi)核或邏輯CPU,以最大程度地減少抖動(dòng)。 可以將抖動(dòng)降低1000倍。
使用哪個(gè)API?
如果您需要記錄每個(gè)事件->編年史隊(duì)列
如果僅需要最新結(jié)果作為唯一鍵->編年史地圖
如果您關(guān)心20微秒抖動(dòng)->線程親和力
結(jié)論
堆外內(nèi)存可能會(huì)帶來挑戰(zhàn),但也會(huì)帶來很多好處。 您可以在其中看到最大的收益,并與其他為實(shí)現(xiàn)可伸縮性而引入的解決方案進(jìn)行比較。 與在堆緩存,消息傳遞解決方案或進(jìn)程外數(shù)據(jù)庫上使用分區(qū)/分片相比,堆外可能更簡單,更快。 通過提高速度,您可能會(huì)發(fā)現(xiàn)不再需要為達(dá)到所需性能而需要做的一些技巧。 例如,堆外解決方案可以支持對(duì)OS的同步寫入,而不必異步執(zhí)行它們,而會(huì)丟失數(shù)據(jù)。
但是,最大的收獲就是啟動(dòng)時(shí)間,使您的生產(chǎn)系統(tǒng)重新啟動(dòng)的速度更快。 例如,映射到1 TB數(shù)據(jù)集中可能需要10毫秒,并且通過重播每個(gè)事件以使您每次都獲得相同的行為,可以簡化測試的可重復(fù)性。 這使您可以創(chuàng)建可以依靠的質(zhì)量體系。
翻譯自: https://www.javacodegeeks.com/2014/12/on-heap-vs-off-heap-memory-usage.html
如何分析堆外內(nèi)存使用情況
總結(jié)
以上是生活随笔為你收集整理的如何分析堆外内存使用情况_堆上与堆外的内存使用情况的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 性能调优_Java性能调优调查
- 下一篇: Linux设置超时时间(linux设置超