简述垃圾对象产生_使用零垃圾创建数百万个对象
簡述垃圾對象產生
如性能優化第一規則中所述,垃圾是快速代碼的敵人。 通過使用垃圾收集器的服務,它不僅會破壞任何確定性的性能,而且我們開始用垃圾填充CPU高速緩存,這將導致程序的高速緩存未命中。
那么,我們可以在不創建垃圾的情況下使用Java嗎? 例如,在自然Java中是否可以解決此問題:
創建10m個金融工具對象,將它們存儲在地圖中,檢索它們并使用每個對象執行計算,而完全不會產生任何垃圾。
如果您使用Chronicle ! Chronicle提供了庫,因此您可以輕松地以對象的內存映射文件形式使用堆外存儲。 (有關本文的完整源代碼,請參見此處 。)
讓我們看一下實現上述問題的解決方案。
首先,讓我們看一下如何在普通Java中執行此操作,以確保我們理解問題以及如果使用標準Java庫進行實現會發生什么情況。
package zeroalloc;import org.junit.Assert; import org.junit.Test;import java.util.Map; import java.util.concurrent.ConcurrentHashMap;/*** Class to demonstrate zero garbage creation.* Run with -verbose:gc -Xmx4G*/ public class CreateOnHeapTest {private static final int ITERATIONS = 10_000_000;@Testpublic void testOnHeapMap() {System.out.println("----- HASHMAP ------------------------");Map<Integer, BondVOImpl> map = new ConcurrentHashMap<>(ITERATIONS);long actualQuantity = 0;long expectedQuantity = 0;long time = System.currentTimeMillis();System.out.println("*** Entering critical section ***");for (int i = 0; i < ITERATIONS; i++) {BondVOImpl bondVo = new BondVOImpl();bondVo.setQuantity(i);map.put(Integer.valueOf(i), bondVo);expectedQuantity += i;}long putTime = System.currentTimeMillis() - time;time = System.currentTimeMillis();System.out.println("************* STARTING GET *********************");for (int i = 0; i < map.size(); i++) {actualQuantity += map.get(i).getQuantity();}System.out.println("*** Exiting critical section ***");System.out.println("Time for putting " + putTime);System.out.println("Time for getting " + (System.currentTimeMillis() - time));Assert.assertEquals(expectedQuantity, actualQuantity);printMemUsage();}public static void printMemUsage() {System.gc();System.gc();System.out.println("Memory(heap) used " + humanReadableByteCount(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(), true));}public static String humanReadableByteCount(long bytes, boolean si) {int unit = si ? 1000 : 1024;if (bytes < unit) return bytes + " B";int exp = (int) (Math.log(bytes) / Math.log(unit));String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i");return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);} }這是程序的輸出:
*** Entering critical section *** [GC (Allocation Failure) 98816K->92120K(125952K), 0.0317021 secs] [Full GC (Ergonomics) 92120K->91917K(216576K), 0.2510530 secs] [GC (Allocation Failure) 125197K->125430K(224256K), 0.0449051 secs] [GC (Allocation Failure) 166390K->166686K(244224K), 0.0504341 secs] [Full GC (Ergonomics) 166686K->165777K(387072K), 0.6243385 secs] [GC (Allocation Failure) 226705K->226513K(388096K), 0.0785121 secs] [GC (Allocation Failure) 293073K->293497K(392704K), 0.0825828 secs] [Full GC (Ergonomics) 293497K->292649K(591872K), 1.2479519 secs] [GC (Allocation Failure) 359209K->359433K(689664K), 0.0666344 secs] [GC (Allocation Failure) 449033K->449417K(695296K), 0.1759746 secs] [GC (Allocation Failure) 539017K->539385K(747008K), 0.1907760 secs] [GC (Allocation Failure) 632569K->633009K(786944K), 0.2293778 secs] [Full GC (Ergonomics) 633009K->631584K(1085952K), 2.1328028 secs] [GC (Allocation Failure) 724768K->723368K(1146368K), 0.3092297 secs] [GC (Allocation Failure) 827816K->825088K(1174016K), 0.3156138 secs] [GC (Allocation Failure) 929536K->929952K(1207296K), 0.3891754 secs] [GC (Allocation Failure) 1008800K->1009560K(1273856K), 0.4149915 secs] [Full GC (Ergonomics) 1009560K->1007636K(1650688K), 3.4521240 secs] [GC (Allocation Failure) 1086484K->1087425K(1671680K), 0.3884906 secs] [GC (Allocation Failure) 1195969K->1196129K(1694208K), 0.2905121 secs] [GC (Allocation Failure) 1304673K->1305257K(1739776K), 0.4291658 secs] [GC (Allocation Failure) 1432745K->1433137K(1766912K), 0.4470582 secs] [GC (Allocation Failure) 1560625K->1561697K(1832960K), 0.6003558 secs] [Full GC (Ergonomics) 1561697K->1558537K(2343936K), 4.9359721 secs] [GC (Allocation Failure) 1728009K->1730019K(2343936K), 0.7616385 secs] [GC (Allocation Failure) 1899491K->1901139K(2413056K), 0.5187234 secs] [Full GC (Ergonomics) 1901139K->1897477K(3119616K), 5.7177263 secs] [GC (Allocation Failure) 2113029K->2114505K(3119616K), 0.6768888 secs] [GC (Allocation Failure) 2330057K->2331441K(3171840K), 0.4812436 secs] [Full GC (Ergonomics) 2331441K->2328578K(3530240K), 6.3054896 secs] [GC (Allocation Failure) 2600962K->2488834K(3528704K), 0.1580837 secs] *** Exiting critical section *** Time for putting 32088 Time for getting 454 [GC (System.gc()) 2537859K->2488834K(3547136K), 0.1599314 secs] [Full GC (System.gc()) 2488834K->2488485K(3547136K), 6.2759293 secs] [GC (System.gc()) 2488485K->2488485K(3559936K), 0.0060901 secs] [Full GC (System.gc()) 2488485K->2488485K(3559936K), 6.0975322 secs] Memory(heap) used 2.6 GB跳出此問題的兩個要點是:一是垃圾回收的數量和開銷(顯然可以調整),二是使用了2.6GB的堆數量。 簡而言之,無法擺脫它,該程序會產生大量垃圾。
我們這次使用ChronicleMap嘗試完全相同的操作。
這是解決問題的代碼:
package zeroalloc;import net.openhft.chronicle.map.ChronicleMap; import net.openhft.chronicle.map.ChronicleMapBuilder; import net.openhft.lang.values.IntValue; import org.junit.Assert; import org.junit.Test;import java.io.File; import java.io.IOException;/*** Class to demonstrate zero garbage creation.* Run with -verbose:gc* To run in JFR use these options for best results* -XX:+UnlockCommercialFeatures -XX:+FlightRecorder*/ public class CreateChronicleTest {private static final int ITERATIONS = 10_000_000;@Testpublic void demoChronicleMap() throws IOException, InterruptedException {System.out.println("----- CHRONICLE MAP ------------------------");File file = new File("/tmp/chronicle-map-" + System.nanoTime() + ".map");file.deleteOnExit();ChronicleMapBuilder<IntValue, BondVOInterface> builder =ChronicleMapBuilder.of(IntValue.class, BondVOInterface.class).entries(ITERATIONS);try (ChronicleMap<IntValue, BondVOInterface> map =builder.createPersistedTo(file)) {final BondVOInterface value = map.newValueInstance();final IntValue key = map.newKeyInstance();long actualQuantity = 0;long expectedQuantity = 0;long time = System.currentTimeMillis();System.out.println("*** Entering critical section ***");for (int i = 0; i < ITERATIONS; i++) {value.setQuantity(i);key.setValue(i);map.put(key, value);expectedQuantity += i;}long putTime = (System.currentTimeMillis()-time);time = System.currentTimeMillis();for (int i = 0; i < ITERATIONS; i++) {key.setValue(i);actualQuantity += map.getUsing(key, value).getQuantity();}System.out.println("*** Exiting critical section ***");System.out.println("Time for putting " + putTime);System.out.println("Time for getting " + (System.currentTimeMillis()-time));Assert.assertEquals(expectedQuantity, actualQuantity);printMemUsage();} finally {file.delete();}}public static void printMemUsage(){System.gc();System.gc();System.out.println("Memory(heap) used " + humanReadableByteCount(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(), true));}public static String humanReadableByteCount(long bytes, boolean si) {int unit = si ? 1000 : 1024;if (bytes < unit) return bytes + " B";int exp = (int) (Math.log(bytes) / Math.log(unit));String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp-1) + (si ? "" : "i");return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);} }這是程序的輸出:
[GC (Allocation Failure) 33280K->6595K(125952K), 0.0072065 secs] [GC (Allocation Failure) 39875K->12177K(125952K), 0.0106678 secs] [GC (Allocation Failure) 45457K->15289K(125952K), 0.0068434 secs] [GC (Allocation Failure) 48569K->18357K(159232K), 0.0098287 secs] [GC (Allocation Failure) 84917K->21008K(159232K), 0.0156393 secs] *** Entering critical section *** *** Exiting critical section *** Time for putting 8554 Time for getting 4351 [GC (System.gc()) 36921K->21516K(230400K), 0.0331916 secs] [Full GC (System.gc()) 21516K->15209K(230400K), 0.0630483 secs] [GC (System.gc()) 15209K->15209K(230912K), 0.0006491 secs] [Full GC (System.gc()) 15209K->15209K(230912K), 0.0234045 secs] Memory(heap) used 18.2 MB顯然,這里的要點是,關鍵部分沒有 GC,整個程序僅使用18MB的堆。 我們已經設法創建了一個程序,該程序通常會產生千兆字節的垃圾,而根本不會產生任何垃圾。
關于時間的注意事項
ChronicleMap顯然不是ConcurrentHashMap的替代品,它們的用法有很大不同,并且超出該討論范圍的內容不在本文的討論范圍之內。 但是功能上的主要區別是ChronicleMap是持久的,并且可以在許多JVM之間共享。 (ChronicleMap也具有可以復制tcp的功能。)盡管如此,快速地比較時間安排還是很有趣的,除非要確保我們在同一個球場。 ChronicleMap的投放速度更快,從32秒提高到8.5秒。 但是ConcurrentHashMap中的大部分時間都花在GC上,并且可能會在某種程度上進行調整。 ConcurrentHashMap的獲取速度比4.3s快了0.5s。 不過,在其他運行中,由于該部分中發生了GC,因此我看到ConcurrentHashMap占用了7s。 即使ChronicleMap所做的工作要多得多,但實際上由于缺少垃圾而使計時與ConcurrentHashMap相當。
重新啟動程序
ChronicleMap真正發揮作用的地方是重新啟動。 假設您的程序崩潰了,您需要重新計算與之前相同的計算。 在ConcurrentHashMap的情況下,我們必須完全按照之前的方法重新填充地圖。 使用ChronicleMap,由于地圖是持久的,因此只需將地圖指向現有文件,然后重新運行計算以產生totalQuantity。
摘要
| gc暫停 | 許多 | 沒有 |
| 更新時間 | 32秒 | 8秒 |
| 讀取允許gc | 7秒 | 4秒 |
| 不讀gc | 0.5秒 | 4秒 |
| 堆大小 | 2.6GB | 18MB |
| 堅持不懈 | 沒有 | 是 |
| 快速重啟 | 沒有 | 是 |
翻譯自: https://www.javacodegeeks.com/2015/03/creating-millions-of-objects-with-zero-garbage.html
簡述垃圾對象產生
總結
以上是生活随笔為你收集整理的简述垃圾对象产生_使用零垃圾创建数百万个对象的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计划单列市是什么意思(全国计划单列市有哪
- 下一篇: 宏?电脑怎么正确配置(电脑里宏怎么设置)