用16G内存在Java
在一个下雨的夜晚,我在思考Java中内存管理的问题,以及Java集合对内存使用的效率情况。我做了一个简单的实验,测试在16G内存条件下,Java的Map可以插入多少对象。
这个试验的目的是为了得出集合的内部上限。所以,我决定使用很小的key和value。所有的测试,都是在64w位linux环境下进行的,操作系统是ubuntu12.04。JVM版本为Oracle Java 1.7.0_09-bo5 (HotSpot 23.5-b02)。在这个JVM中,压缩指针(compressed pointers(-XX:+UseCompressedOops))的选项是默认打开的。
首先是简单的针对java.util.TreeMap的测试。不停向其中插入数字,直到其抛出内存溢出异常。JVM的设置是-xmx15G
import java.util.*;
Map m = new TreeMap();
for(long counter=0;;counter++){
m.put(counter,"");
if(counter%1000000==0) System.out.println(""+counter);
}
这个用例插入了1 7200 0000条数据。在接近结束的时候,由于高负荷的GC插入效率开始降低。第二次,我用HashMap代替TreeMap,这次插入了182 000 000条数据。
Java默认的集合并不是最高效利用内存的。所以,这回我们尝试最后化内存的测试。我选择了MapDB中的LongHashMap,其使用原始的long key并且对封装的内存占用进行了优化。JVM的设置仍然是-Xmx15G。
import org.mapdb.*
LongMap m = new LongHashMap();
for(long counter=0;;counter++){
m.put(counter,"");
if(counter%1000000==0) System.out.println(""+counter);
}
这次,计数器停止在了276 000 000。同样,在插入接近结束的时候,速度开始减慢。看起来这是基于堆的结合的限制所在。垃圾回收带来了瓶颈 。
现在是时候祭出杀手锏了:-)。我们可以采用非基于堆的方式存储,这样GC就不会发现我们的数据。我来介绍一下MapDB,它提供了基于数据库引擎的并发同步的TreeMap和HashMap。它提供了多样化的存储方式,其中一个就是非堆内存的方式。(声明:我是MapDB的作者)。
现在,让我们再跑一下之前的用例,这次采用的是非堆的Map。首先是配置并打开数据库,它打开的直接基于内存存储并且关闭事物的模式。接下来的代码是在这个db中创建一个新的map。
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);
}
这是非堆的Map,所以我们需要不同的JVM配置: -XX:MaxDirectMemorySize=15G -Xmx128M。这次测试在达到980 000 000条记录的时候出现内存溢出。
但是,MapDB还可以优化。之前样例的问题在于记录的破碎分散,b-tree的节点每次插入都要调整它的容量。变通的方案是,将b-tree的节点在其插入前短暂的缓存起来。这使得记录的分散降到最低。所以,我们来改变一下DB的配置:
DB db = DBMaker
.newDirectMemoryDB()
.transactionDisable()
.asyncFlushDelay(100)
.make(); Map m = db.getTreeMap("test");
这次记录数达到了 1 738 000 000。速度也是达到了惊人的31分钟完成了17亿数据的插入。
MapDB还能继续优化。我们把b-tree的节点容量从32提升到120并且打开透明(OneCoder理解为对用户不可见的)压缩:
DB db = DBMaker
.newDirectMemoryDB()
.transactionDisable()
.asyncFlushDelay(100)
.compressionEnable()
.make(); Map m = db.createTreeMap("test",120, false, null, null, null);
这个用例在大约3 315 000 000条记录时出现内存溢出。由于压缩,他的速度 有所降低,不过还是在几个小时内完成。我还可以进行一些优化(自定义序列化等等) ,使得数据量达到大约40亿。
也许你好奇所有这些记录是怎么存储的。答案就是,delta-key压缩。当然,向B-Tree插入已经排好序的递增key是最佳的使用场景,并且MapDB也对此进行了一些小小的 优化。最差的情形就是key是随机的.
后续更新:很多朋友对压缩有一些困惑。在这些用例中,Delta-key 压缩默认都是启用的。在下面的用例中,我又额外开启了zlib方式的压缩:
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);
}
即使在随机序列情况下,MapDB也可以存储652 000 000条记录,大概4倍于基于堆的集合。
这个简单的试验没有太多的目的。这仅仅是我对MapDB的一种优化。也许,更多的惊喜在于插入效率确实不错并且MapDB可以抗衡基于内存的集合。
用16G内存在Java的更多相关文章
- IOS IAP APP内支付 Java服务端代码
IOS IAP APP内支付 Java服务端代码 场景:作为后台需要为app提供服务,在ios中,app内进行支付购买时需要进行二次验证. 基础:可以参考上一篇转载的博文In-App Purcha ...
- JAVA项目之苹果IAP内购JAVA服务器验证流程详解
1.前言 本博客是经历过多个项目检验的, 绝对真实, 适应于对苹果iap内购稍微有些了解的JAVA开发人员, 认真看, 定能完美解决苹果内购问题. 苹果IAP内购支付实际上是"将客户端支 ...
- Kotlin 在kotlin内使用Java的一些注意(长篇)
首先呢,大部分的java在kotlin内是可以使用的. 但是有些java的关键字和kotlin的一样,而意义不一样就需要转义.(单引号括起来的)这一点需要注意. 这是一个长篇 我会不断更新.毕竟我也在 ...
- 如何规避容器内做Java堆dump导致容器崩溃的问题
写在前边 最近公司生产环境的容器云上出了个性能问题,为了做性能分析,使用 JDK 自带的 jmap 收集堆dump,出现了内存溢出导致了容器崩溃. 本篇文章将带你探究,如何规避容器内做堆 dump 导 ...
- 如何判断一个GPS点是否在以另一个GPS点为圆心100米为半径的圆内(Java代码)
题目乍一看,无从下手,仔细想了一下,原来只需要判断两个GPS点的直线距离是否<100米即可. Java代码如下: /** * 将两个经纬度坐标转化成距离(米) * * @param 2个GPS经 ...
- javac编译同一个包内的java文件
问题描述:包a.b.c里有d.java e.java f.java三个文件,其中d中包含main. 错误: 第一种:javac d.java 报错:里面用到其他类,找不到 第二种:javac *.ja ...
- Mybatis 内置 Java 类型别名与 typeHandlers
aliases There are many built-in type aliases for common Java types. They are all case insensitive, n ...
- 我在组内的Java问题排查分享
前言 最近翻看以前写的 PPT, 发现了在2019年做的一次技术分享,关于 Java 问题排查,由于没什么公司机密可言,整理下分享给大家~ 线上问题处理流程 直接放PPT截图吧,现在看来依然不过时 问 ...
- Java 方法内联
什么是Java 方法内联? 我们先来看看普遍的内联函数含义.在维基百科中解释为: 内联函数:在计算机科学中,内联函数(有时称作在线函数或编译时期展开函数)是一种编程语言结构,用来建议编译器对一些特殊函 ...
随机推荐
- spring boot redis缓存入门
摘要: 原创出处 泥瓦匠BYSocket 下载工程 springboot-learning-example ,工程代码注解很详细.JeffLi1993/springboot-learning-exam ...
- YTU 1007: Redraiment猜想
1007: Redraiment猜想 时间限制: 1000 Sec 内存限制: 10 MB 提交: 83 解决: 23 题目描述 redraiment在家极度无聊,于是找了张纸开始统计素数的个数. ...
- JS窗口
<SCRIPT LANGUAGE="javascript"> <!-- window.open ('page.html', 'newwindow', 'heigh ...
- Masonry 动画更新约束
前言 说到iOS自动布局,有很多的解决办法.有的人使用xib/storyboard自动布局,也有人使用frame来适配.对于前者,笔者并不喜欢,也不支持.对于后者,更是麻烦,到处计算高度.宽度等,千万 ...
- BZOJ:2819 NIM(树链剖分||DFS序 &&NIM博弈)
著名游戏设计师vfleaking,最近迷上了Nim.普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取.谁不能取谁输.这个游戏是有必胜策略的.于是v ...
- AutoIT: 对文件系统的菜单进行操作,有专门的语句WinMenuSelectItem
对文件系统的菜单进行操作,有专门的语句WinMenuSelectItem: Run("notepad.exe") WinWaitActive("[CLASS:Notepa ...
- bzoj2743 [HEOI2012]采花——树状数组+离线
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2743 和 HH的项链 那道题很像,也是类似的做法: 但不同的是这里的点只有有前驱时才起作用: ...
- 小程序-demo:小程序示例-page/component2
ylbtech-小程序-demo:小程序示例-page/component2 以下将展示小程序官方组件能力,组件样式仅供参考,开发者可根据自身需求自定义组件样式,具体属性参数详见小程序开发文档. 1. ...
- 使用root用户登录到AWS EC2服务器,上传文件到/var/www目录
关键词 1.aws ec2中上传文件到/var/www目录(使用filezilla) 2.使用root用户登录aws ec2实例 上一篇随笔中记录了在aws ec2实例中部署apache服务器的过程, ...
- P3161 [CQOI2012]模拟工厂
传送门 先枚举选择哪些订单,然后转为判定是否可行 在能完成的情况下肯定是花越多时间提高生产力越优 我们设可以有\(x\)单位时间来提高生产力,那么如果当前离下一个订单的时间为\(T\)时,这个订单要\ ...