简述垃圾对象产生_使用零垃圾创建数百万个对象
简述垃圾对象产生
如性能优化第一规则中所述,垃圾是快速代码的敌人。 通过使用垃圾收集器的服务,它不仅会破坏任何确定性的性能,而且我们开始用垃圾填充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
简述垃圾对象产生
简述垃圾对象产生_使用零垃圾创建数百万个对象相关推荐
- 零垃圾创建数百万个对象
如性能优化第一规则中所述,垃圾是快速代码的敌人. 通过使用垃圾收集器的服务,它不仅会破坏任何形式的确定性性能,而且我们开始在CPU高速缓存中填充垃圾,这将导致程序的高速缓存未命中. 那么,我们可以在不 ...
- java判断垃圾已回收_[译] 现代垃圾回收
关于 Go 语言最新的垃圾回收器(garbage collector),我最近阅读了许多篇赞扬它的文章,但是它们都让我将信将疑,其中的不少来自 Go 语言的官方团队博客.他们像是暗示着在垃圾回收领域已 ...
- java垃圾回收机制_笔记 | Java垃圾回收机制
本文经授权转载自程序员杂货铺(ID:speakFramework) 垃圾回收 最近上海的小伙伴是不是要被强垃圾分类搞疯了???哈哈哈哈 上海是个走在前列的城市啊,不光骑自行车闯红灯要被罚钱,垃圾不分类 ...
- python垃圾回收离职_谈谈python垃圾回收机制
什么是垃圾回收机制? 首先,咱先来解释名词,垃圾回收是不是就是将没用的,废弃的东西回收起来. 在坐的各位都没有女朋友对吧,那难以想象你们的房间会是一个什么样子,可能会有很多垃圾,很凌乱,自己也不收拾. ...
- jvm垃圾回收机制_干货|JVM垃圾回收机制
前言 不同于C++程序员必须自己完成内存的分配.使用和释放,JAVA语言提供了垃圾回收机制(GC,Garbage Collection),所以JAVA程序员仅需要负责分配和使用内存即可,而释放内存则由 ...
- python垃圾回收价格表_主流的垃圾回收机制都有哪些?
常用的垃圾收集算法 目录如下:引用计数法 标记-清除算法(Mark-Sweep) 复制算法(Copying) 标记-整理算法(Mark-compact) 分代收集算法 Generational Col ...
- 6种java垃圾回收算法_学习java垃圾回收
垃圾回收(GC)一直是Java受欢迎背后的重要特性之一.垃圾回收是Java中用于释放未使用的内存的机制.本质上,它追踪所有仍在使用的对象,并将剩下的标记为垃圾.Java的垃圾回收被认为是一种自动内存管 ...
- python垃圾回收离职_详细解说python垃圾回收机制
不同于C/C++,像Python这样的语言是不需要程序员写代码来管理内存的,它的GC(Garbage Collection)机制 实现了自动内存管理.GC做的事情就是解放程序员的双手,找出内存中不用的 ...
- 每个python对象都具有布尔值_百度T7架构师透析Python对象你需要知道这些
1. Python对象 Python使用对象模型来存储数据.构造任何类型的值都是一个对象. 所有的Python对象都拥有三个特性:身份,类型和值. 身份: 每一个对象都有一个唯一的身份标识自己,任何对 ...
最新文章
- Ext.app.controller的refs
- android源码分析-Zygote
- 用Netscaler的Variable和Assignment来实现计数控制
- Ubuntu 16.04 下安装VMware Tools(三行命令搞定,亲测好使)
- WebIDE push files to ABAP repository
- nyoj164——卡特兰数(待填坑)
- Docker系列之一:入门介绍
- C#回顾 - 3.NET的IO:字节流
- ORACLE查询表最近更改数据的方法
- logback日志pattern_Logback 配置文件这么写,TPS提高10倍
- 想去掉抖音短视频里的水印,有没有一键去水印的方法?
- 如何在ps添加箭头_照片一键自动添加烟雾特效!赠送PS动作插件 50张高清烟雾素材...
- 2019/9/10谷歌开发者大会汇总
- Elasticsearch方案管理 Spring Boot 和 Nginx 日志
- 见面会 | 创新工场执行董事王嘉平:高性能可伸缩性区块链系统揭秘!
- OV7725之AL422B-FIFO及摄像头的驱动原理
- 超舒适!超强续航!南卡N2S蓝牙耳机全面评测!
- 题目内容: 你的程序要读入一个整数,范围是[-100000,100000]。然后,用汉语拼音将这个整数的每一位输出出来。 如输入1234,则输出: yi er san si 注意,每个字的拼音
- Linux基本指令之网络通信命令
- 思科—计算机网络课程设计—第一章路由概念测试