用 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 程序的过程中会把它所管理的内 ...
随机推荐
- string的find()与npos
在 C++ 中,std::string::find() 是一个用于在字符串中查找子字符串或字符的成员函数.查找成功时返回匹配的索引位置,查找失败时返回 std::string::npos,表示未找到. ...
- `std::optional` 函数返回值
std::optional 是 C++17 中引入的一个模板类,用于表示一个值可能存在也可能不存在的情况. 它可以存储一个值,或者表示没有值的状态,类似于其他编程语言中的"可选"类 ...
- C# 中的数组使用
· // 数组 /// 数组是一组相同类型的数据(ps:js中的数组可以不同类型) 访问通过索引访问数组元素 /// 数组的声明 要使用 new 使用 {} 来初始化数组元素 还需要指定数组的大小 / ...
- mysql外联数据库
- 谈谈你对 vue 的理解
vue 是创建用户界面的 js 框架 ,是创建 spa 应用的框架 :使用 mvvm 模式,数据驱动视图模型 ,业务逻辑和页面解构分离开发:使用高效的 diff 算法渲染页面结构 : 采用组件化模式, ...
- 7.flask 源码解析:session
目录 一.flask 源码解析:session 1.1 session 简介 1.2 解析 1.2.1 请求过程 1.2.2 session 对象 1.2.3 签名算法 1.2.4 应答过程 1.3 ...
- CMDB实践指南:项目规划与实施策略解析
随着现代企业IT系统的日益复杂,如何有效管理这些庞大的IT资产和资源,成为每个企业必须面对的重要问题.CMDB应运而生,帮助企业集中管理IT资源,维护系统的稳定性,并支持故障排查与决策制定.本文将深入 ...
- C语言常见概念
目录 1.C语言是什么? 2.编译器的选择Clion 3.第一个C语言程序 4.main函数 5.printf和库函数 6.关键字介绍 7.字符和ASCII编码 8.转义字符 9.语句和语句分类 9. ...
- mysql 备份还原命令备忘
1.备份 mysqldump -u username -p databasename tableName > backupfile.sql 2.还原 mysql -u username -p p ...
- php如何快速入门
PHP交流群 656679284 为PHP广大爱好者提供技术交流,有问必答,相互学习相互进步! 学习教程 学习前期,首要的WEB前端基础知识,比如html5/css3/java/jquery有个简 ...