用 16G 内存存放 30亿数据(Java Map)转载
在讨论怎么去重,提出用 direct buffer 建 btree,想到应该有现成方案,于是找到一个好东西:
MapDB - MapDB : http://www.mapdb.org/
以下来自:kotek.net : http://kotek.net/blog/3G_map
3 billion items in Java Map with 16 GB RAM
One rainy evening I meditated about memory managment in Java and how effectively Java collections utilise memory. I made simple experiment, how much entries can I insert into Java Map with 16 GB of RAM?
Goal of this experiment is to investigate internal overhead of collections. So I decided to use small keys and small values. All tests were made on Linux 64bit Kubuntu 12.04. JVM was 64bit Oracle Java 1.7.0_09-b05 with HotSpot 23.5-b02. There is option to use compressed pointers (-XX:+UseCompressedOops), which is on by default on this JVM.
First is naive test with java.util.TreeMap. It inserts number into map, until it runs out of memory and ends with exception. JVM settings for this test was -Xmx15G
import java.util.*;
Map m = new TreeMap();
for(long counter=0;;counter++){
m.put(counter,"");
if(counter%1000000==0) System.out.println(""+counter);
}
This example ended at 172 milion entries. Near the end insertion rate slowed down thanks to excesive GC activity. On second run I replaced TreeMap with `HashMap, it ended at 182 milions.
Java default collections are not most memory efficient option. So lets try an memory-optimized . I choosed LongHashMap from MapDB, which uses primitive long keys and is optimized to have small memory footprint. JVM settings is again -Xmx15G
import org.mapdb.*
LongMap m = new LongHashMap();
for(long counter=0;;counter++){
m.put(counter,"");
if(counter%1000000==0) System.out.println(""+counter);
}
This time counter stopped at 276 million entries. Again near the end insertion rate slowed down thanks to excesive GC activity.
It looks like this is limit for heap-based collections, Garbage Collection simply brings overhead.
Now is time to pull out the big gun :-). We can always go of-heap where GC can not see our data. Let me introduce you to MapDB, it provides concurrent TreeMap and HashMap backed by database engine. It supports various storage modes, one of them is off-heap memory. (disclaimer: I am MapDB author).
So lets run previous example, but now with off-heap Map. First are few lines to configure and open database, it opens direct-memory store with transactions disabled. Next line creates new Map within the db.
import org.mapdb.*
DB db = DBMaker
.newDirectMemoryDB()
.transactionDisable()
.make();
Map m = db.getTreeMap("test");
for(long counter=0;;counter++){
m.put(counter,"");
if(counter%1000000==0) System.out.println(""+counter);
}
This is off-heap Map, so we need different JVM settings: -XX:MaxDirectMemorySize=15G -Xmx128M. This test runs out of memory at 980 million records.
But MapDB can do better. Problem in previous sample is record fragmentation, b-tree node changes its size on each insert. Workaround is to hold b-tree nodes in cache for short moment before they are inserted. This reduces the record fragmentation to minimum. So lets change DB configuration:
DB db = DBMaker
.newDirectMemoryDB()
.transactionDisable()
.asyncFlushDelay(100)
.make();
Map m = db.getTreeMap("test");
This records runs out of memory with 1 738 million records. Speed is just amazing 1.7 bilion items are inserted within 31 minutes.
MapDB can do even better. Lets increase b-tree node size from 32 to 120 entries and enable transparent compression:
DB db = DBMaker
.newDirectMemoryDB()
.transactionDisable()
.asyncFlushDelay(100)
.compressionEnable()
.make();
Map m = db.createTreeMap("test",120, false, null, null, null);
This example runs out of memory at whipping 3 315 million records. It is slower thanks to compression, but it still finishes within a few hours. I could probably make some optimization (custom serializers etc) and push number of entries to somewhere around 4 billions.
Maybe you wander how all those entries can fit there. Answer is delta-key compression. Also inserting incremental key (already ordered) into B-Tree is best-case scenario and MapDB is slightly optimized for it. Worst case scenario is inserting keys at random order:
UPDATE added latter: there was bit confusion about compression. Delta-key compression is active by default on all examples. In this example I activated aditional zlib style compression.
DB db = DBMaker
.newDirectMemoryDB()
.transactionDisable()
.asyncFlushDelay(100)
.make();
Map m = db.getTreeMap("test");
Random r = new Random();
for(long counter=0;;counter++){
m.put(r.nextLong(),"");
if(counter%1000000==0) System.out.println(""+counter);
}
But even with random order MapDB handles to store 651 million records, nearly 4 times more then heap-based collections.
This little excersice does not have much purpose. It is just one of many I do to optimize MapDB. Perhaps most amazing is that insertion speed was actually wery good and MapDB can compete with memory based collections.
用 16G 内存存放 30亿数据(Java Map)转载的更多相关文章
- 大数据计算:如何仅用1.5KB内存为十亿对象计数
大数据计算:如何仅用1.5KB内存为十亿对象计数 Big Data Counting: How To Count A Billion Distinct Objects Using Only 1.5K ...
- Java内存区域-- 运行时数据区域
jvm在执行Java程序时,会把它所管理的内存划分为若干个不同的数据区.这些区域都有各自的用途,以及创建和销毁的时间. 有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销 ...
- Java使用极小的内存完成对超大数据的去重计数,用于实时计算中统计UV
Java使用极小的内存完成对超大数据的去重计数,用于实时计算中统计UV – lxw的大数据田地 http://lxw1234.com/archives/2015/09/516.htm Java使用极小 ...
- java内存结构(执行时数据区域)
java虚拟机规范规定的java虚拟机内存事实上就是java虚拟机执行时数据区,其架构例如以下: 当中方法区和堆是由全部线程共享的数据区. Java虚拟机栈.本地方法栈和程序计数器是线程隔离的数据区. ...
- java内存区域----运行时数据区
Java虚拟机的内存区域也叫做java运行时数据区,共分为五个部分:程序计数器,方法区,本地方法栈,虚拟机栈和堆.方法区和堆是线程之间所共有的,程序计数器,本地方法栈,虚拟机栈是线程私有的.其中虚拟机 ...
- 给定a、b两个文件,各存放50亿个url,每个url各占用64字节,内存限制是4G,如何找出a、b文件共同的url?
给定a.b两个文件,各存放50亿个url,每个url各占用64字节,内存限制是4G,如何找出a.b文件共同的url? 可以估计每个文件的大小为5G*64=300G,远大于4G.所以不可能将其完全加载到 ...
- 从SQL Server到MySQL,近百亿数据量迁移实战
从SQL Server到MySQL,近百亿数据量迁移实战 狄敬超(3D) 2018-05-29 10:52:48 212 沪江成立于 2001 年,作为较早期的教育学习网站,当时技术选型范围并不大:J ...
- Redis基本使用及百亿数据量中的使用技巧分享(附视频地址及观看指南)
作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9941208.html 主讲人:大石头 时间:2018-11-10 晚上20:00 地点:钉钉群(组织代码 ...
- Java内存管理-你真的理解Java中的数据类型吗(十)
勿在流沙筑高台,出来混迟早要还的. 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 作为Java程序员,Java 的数据类型这个是一定要知道的! 但是不管是那种数据类型最 ...
- JVM 内存区域 (运行时数据区域)
JVM 内存区域 (运行时数据区域) 链接:https://www.jianshu.com/p/ec479baf4d06 运行时数据区域 Java 虚拟机在执行 Java 程序的过程中会把它所管理的内 ...
随机推荐
- Flutter将视频或图文分享到抖音
如何在 Flutter 中分享视频到抖音 话不多说,先上效果: 原理 发布内容至抖音 H5 场景_移动/网站应用_抖音开放平台 (open-douyin.com) 本教程没有接入抖音原生 SDK 以及 ...
- Windows应急响应-Auto病毒
目录 应急背景 分析样本 开启监控 感染病毒 查看监控 分析病毒行为 autorun.inf分析 2.异常连接 3.进程排查 4.启动项排查 查杀 1.先删掉autorun.inf文件 2.使用xue ...
- ARM SMMU 与 IOMMU 的区别
ARM SMMU (System Memory Management Unit) 和 IOMMU (Input-Output Memory Management Unit) 都是用于管理系统内存访问和 ...
- iOS 数据持久化方案-Realm的使用小结
一.Realm介绍 1.1.Realm是一个跨平台移动数据库引擎,支持iOS.OS X(Objective-C和Swift)以及Android,核心数据引擎C++打造,并不是建立在SQLite之上的O ...
- 封装大屏组件 screenfull
错误场景:使用大屏插件 screenFull 报错:in ./node_modules/screenfull/index.js Module parse failed: Unexpected tok ...
- 鸿蒙 NEXT 如何使用 @Styles 装饰器来优化我的组件代码?
大家好,我是 V 哥.在鸿蒙 NEXT 开发中,@Styles 装饰器是一种非常有用的方法,用于定义可重用的样式.这使得开发者可以将多条样式设置提炼成一个方法,以便在多个组件中复用,从而提高代码的可维 ...
- 修复 KubeSphere 内置 Jenkins 的 Apache Log4j2 漏洞
作者:老Z,中电信数智科技有限公司山东分公司运维架构师,云原生爱好者,目前专注于云原生运维,云原生领域技术栈涉及 Kubernetes.KubeSphere.DevOps.OpenStack.Ansi ...
- 使用 Fluent Bit 实现云边统一可观测性
本文基于 KubeSphere 可观测性与边缘计算负责人霍秉杰在北美 KubeCon 的 Co-located event Open Observability Day 闪电演讲的内容进行整理. 整理 ...
- jmeter-模拟带参数的请求与上传文件
请求参数照着前端请求的样式填即可: header带content: header不带content_type: 总结:上传文件时headers里面不能带content_type参数
- SpringBoot 2.3 升级到 SpringBoot 2.7 爬坑-- SpringDoc & Swagger
目录 POM yml 配置自定义的 OpenAPI 规范 拦截器去除 swagger 的接口验证 模型 Controller 配置 常用注解 注意:Swagger支持SpringBoot2.0但不支持 ...