news 2026/4/18 14:30:52

RogueMap,HashMap替代品

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RogueMap,HashMap替代品

HashMap 的困境

在处理大规模数据时,传统的 HashMap 面临诸多限制:

  • 内存瓶颈- 所有数据必须存储在堆内存,受 JVM 堆大小限制
  • GC 压力- 百万级对象导致 Full GC 频繁,影响应用稳定性
  • 数据易失- 进程重启后数据全部丢失,无持久化能力
  • 容量受限- 超大数据集(10GB+)无法处理,OutOfMemoryError 噩梦
  • 冷启动慢- 每次启动都需要重新加载数据,耗时数分钟甚至更久

今天要给大家介绍的RogueMap,就是为了解决这些痛点而生的。

它能做到什么程度?

  • 🚀读性能提升2.4倍,即使对比HashMap,吞吐量都能从200万ops/s飙到500万ops/s

  • 💾内存占用减少87%,100w个对象(每个对象10个属性)实测,堆内存从304MB降到40MB

  • 进程重启秒级恢复,千万级数据自动加载

  • 📦API简单到爆,就像用HashMap一样顺滑

一、RogueMap是什么

想象一下,你在玩游戏时有三种存储道具的方式:

  1. 背包(OffHeap模式)- 随身携带,速度快,但下线就清空

  2. 仓库临时箱(Mmap临时模式)- 容量更大,关服清空,适合临时囤货

  3. 银行保险箱(Mmap持久模式)- 容量巨大,永久保存,下次登录自动恢复

RogueMap就是这样一个"三合一"的数据仓库,你可以根据需要选择存储模式:

// 模式1: 背包 - 纯内存,速度极快 RogueMap<String, User> cache = RogueMap.<String, User>offHeap() .keyCodec(StringCodec.INSTANCE) .valueCodec(new KryoObjectCodec<>(User.class)) .maxMemory(100 * 1024 * 1024) // 100MB .build(); // 模式2: 临时仓库 - 文件映射,自动清理 RogueMap<Long, Long> tempData = RogueMap.<Long, Long>mmap() .temporary() .keyCodec(PrimitiveCodecs.LONG) .valueCodec(PrimitiveCodecs.LONG) .build(); // 模式3: 保险箱 - 持久化,重启恢复 RogueMap<String, Long> scores = RogueMap.<String, Long>mmap() .persistent("data/game_scores.db") .keyCodec(StringCodec.INSTANCE) .valueCodec(PrimitiveCodecs.LONG) .build(); scores.put("玩家A", 99999L); scores.flush(); // 保存 scores.close(); // 进程重启后... scores = RogueMap.<String, Long>mmap() .persistent("data/game_scores.db") .keyCodec(StringCodec.INSTANCE) .valueCodec(PrimitiveCodecs.LONG) .build(); Long score = scores.get("玩家A"); // 99999L - 数据还在!

核心优势:用HashMap的简单API,获得超越其限制的能力。

核心模块

  • RogueMap- 主类,提供 OffHeapBuilder 和 MmapBuilder 两个构建器
  • index- 索引层
    • HashIndex- 基础哈希索引,基于 ConcurrentHashMap
    • SegmentedHashIndex- 分段哈希索引,64 个段 + StampedLock 乐观锁
    • LongPrimitiveIndex- Long 键原始数组索引,节省 81% 内存
    • IntPrimitiveIndex- Integer 键原始数组索引
  • storage- 存储引擎
    • OffHeapStorage- 堆外内存存储
    • MmapStorage- 内存映射文件存储
  • memory- 内存管理
    • SlabAllocator- Slab 分配器,7 个大小类别(16B 到 16KB)
    • MmapAllocator- 内存映射文件分配器,支持超过 2GB 的大文件
    • UnsafeOps- 底层 Unsafe API 操作
  • serialization- 序列化层
    • PrimitiveCodecs- 原始类型零拷贝编解码器
    • StringCodec- String 编解码器
    • KryoObjectCodec- Kryo 对象序列化编解码器(可选)

内存管理机制

SlabAllocator(堆外内存)
  • 分配策略: 7 个 size class (16B, 64B, 256B, 1KB, 4KB, 16KB)
  • 块大小: 1MB
  • 优化: 空闲列表重用,负载因子自适应扩容
  • 内存节省: 相比 HashMap 节省 87% 堆内存
MmapAllocator(文件映射)
  • 特点: 使用 MappedByteBuffer 将文件映射到内存
  • 大文件支持: 单个分段最大 2GB,自动分多段处理
  • 并发安全: CAS 操作分配偏移量
  • 双模式: 支持持久化和临时文件

高并发支持

SegmentedHashIndex 并发机制
  • 分段数量: 64 个独立段
  • 锁策略: 每个段独立的 StampedLock
  • 乐观读: 读操作优先使用乐观读,验证失败时降级为读锁
  • 性能: 高并发场景下读性能提升 15 倍
LongPrimitiveIndex 并发机制
  • 实现: 原始数组 (long[] keys, long[] addresses, int[] sizes)
  • 锁策略: StampedLock 乐观读
  • 内存优化: 节省 81% 内存

二、性能到底有多强

我们用100万条数据做了测试,对比结果让人眼前一亮:

📊 RogueMap vs HashMap

指标

HashMap

RogueMap(Mmap持久)

提升

写入耗时

611ms

547ms

⬆️ 12%

读取耗时

463ms

195ms

⬆️137%

读吞吐量

216万ops/s

513万ops/s

⬆️137%

堆内存占用

304MB

40MB

⬇️87%

数据容量受限于堆大小(通常 < 10GB)无限制,可达 TB 级
堆内存占用100%仅 15.3%
GC 影响严重(Full GC 秒级)几乎无影响
持久化❌ 不支持✅ 支持
进程重启数据全部丢失数据自动恢复
写性能基准1.45 倍提升
读性能基准约 1/4(反序列化开销)
临时文件❌ 不支持✅ 自动清理

🔥 RogueMap vs MapDB (竞品对比)

指标

RogueMap

MapDB

领先倍数

读取速度

202ms

3207ms

15.9x
写入速度

632ms

2764ms

4.4x
读吞吐量

495万ops/s

31万ops/s

15.9x

MapDB是业内知名的嵌入式存储引擎,但在RogueMap面前,读取性能直接被碾压15倍

综合性能对比

方案写入时间读取时间写吞吐量读吞吐量堆内存占用持久化
HashMap1,535ms158ms651K ops/s6,329K ops/s311 MB
FastUtil600ms32ms1,667K ops/s31,250K ops/s276 MB
Caffeine1,107ms2,298ms903K ops/s435K ops/s352 MB
RogueMap OffHeap1,924ms854ms520K ops/s1,171K ops/s48 MB
RogueMap Mmap 持久化1,057ms642ms946K ops/s1,558K ops/s48 MB
RogueMap Mmap 临时1,113ms704ms898K ops/s1,420K ops/s48 MB
MapDB OffHeap8,259ms8,451ms121K ops/s118K ops/s11 MB
MapDB 临时文件9,002ms7,717ms111K ops/s130K ops/s8 MB
MapDB 持久化8,117ms7,709ms123K ops/s130K ops/s8 MB

适用场景

RogueMap 适合这些场景

  • 写多读少- 数据采集、日志聚合、指标统计
  • 需要持久化- 用户会话、应用状态、缓存数据
  • 大数据集- 数据量超过 JVM 堆大小限制
  • GC 敏感- 对 Full GC 停顿零容忍的实时系统
  • 临时数据处理- 海量临时数据暂存,自动清理避免泄露

RogueMap 不适合这些场景

  • 读密集型- 如果你的应用是读多写少,HashMap 或 Caffeine 更合适
  • 微秒级延迟- 如果需要极致的读取性能,纯内存方案更好
  • 小数据集- 数据量 < 1GB 时,HashMap 的简单性更有优势

推荐使用场景

  • 🏆写多读少- 数据采集、日志聚合、消息队列
  • 💾需要持久化- 用户会话、缓存数据、临时计算结果
  • 📈大数据集- 超过堆大小的数据处理
  • GC 敏感- 对 GC 停顿零容忍的实时系统

三、为什么这么快

看到这里,你可能会问:"怎么做到的?是不是用了什么黑魔法?"

其实没有黑魔法,只有极致的工程优化。让我们用最简单的方式,揭开RogueMap的五大性能秘诀:

🎯 秘诀1: 堆外内存 - 让GC管不着

问题: HashMap把数据存在JVM堆内,数据越多,GC扫描越慢,停顿越久。

RogueMap的做法: 把数据存到堆外内存(DirectByteBuffer)或文件映射(Mmap)。

效果:

  • JVM堆只需存40MB的索引结构

  • GC扫描范围大幅减少,停顿从秒级降到毫秒级

  • 数据量可以突破JVM堆限制,想存100GB都行

类比: 就像你把大量书籍从书桌(JVM堆)搬到书架(堆外内存),书桌变整洁,找东西更快。


⚡ 秘诀2: 零拷贝序列化 - 原始类型直接怼

问题: 传统存储引擎要把Java对象序列化成字节数组,再存储。每次读写都要序列化/反序列化,慢得要命。

RogueMap的做法: 原始类型(Long、Integer等)直接写入内存,不经过任何转换。

// ❌ 传统方式: 序列化开销巨大 byte[] bytes = serialize(value); // Long -> 字节数组 storage.write(bytes); // ✅ RogueMap方式: 直接内存操作 UnsafeOps.putLong(address, value); // 8字节,一次性写入

效果: 读写延迟降低到纳秒级,吞吐量直接起飞。

类比: 就像快递员送包裹,传统方式要拆开检查再装箱,RogueMap直接扔过去。


🔓 秘诀3: 乐观并发 - 99%的操作不加锁

问题: HashMap在高并发场景要加锁,锁竞争严重时性能暴跌。

RogueMap的做法: 使用StampedLock的乐观读模式,大部分读操作完全不加锁。

// 第一步: 乐观读(无锁) long stamp = lock.tryOptimisticRead(); long value = readData(key); // 第二步: 验证是否被其他线程修改 if (!lock.validate(stamp)) { // 冲突了才降级到读锁重试 stamp = lock.readLock(); value = readData(key); lock.unlockRead(stamp); }

效果: 高并发场景下,读性能提升15倍。

类比: 图书馆不用每次借书都登记,只在发现书丢了才追查记录。


💾秘诀4: 内存映射文件(Mmap) - 操作系统帮你缓存

问题: 传统文件IO需要从磁盘读到内核缓冲区,再拷贝到用户空间,路径太长。

RogueMap的做法: 使用Mmap把文件直接映射到内存地址空间。

效果:

  • 操作系统自动管理页缓存,热数据常驻内存

  • 读取时直接访问内存地址,速度接近纯内存

  • 支持超大文件(>100GB),自动分段管理

类比: 传统方式像图书馆借书要填单子排队,Mmap就像书直接放在你桌上,拿来就看。


🧠 秘诀5: 极致的内存优化 - 原始类型数组索引

问题: HashMap的每个Entry要存储key引用、value引用、hash值、next指针,占用28字节。

RogueMap的做法: 针对Long类型键,用三个long数组存储索引,每条记录只占20字节。

传统HashMap的Entry结构: ┌────────────┬────────────┬────────┬────────┐ │ key引用(8B)│ value引用(8B)│ hash(4B)│ next(8B)│ └────────────┴────────────┴────────┴────────┘ = 28字节/条 RogueMap的LongPrimitiveIndex: keys[] : [123, 456, 789, ...] (8字节/条) addresses[] : [0x1000, 0x2000, ...] (8字节/条) sizes[] : [64, 128, 256, ...] (4字节/条) = 20字节/条

效果: 100万条数据,内存占用从104MB降到20MB,**节省81%**。

类比: 传统方式像每本书都配个厚厚的档案袋,RogueMap直接用Excel表格记录,省空间又高效。


四、三大应用场景,总有一个适合你

🎮 场景1: 游戏服务器 - 千万玩家数据秒级恢复

痛点: 游戏服务器维护重启,千万级玩家数据要重新加载,玩家等待时间长。

RogueMap方案:

// 玩家数据持久化 RogueMap<Long, Player> playerDB = RogueMap.<Long, Player>mmap() .persistent("data/players.db") .keyCodec(PrimitiveCodecs.LONG) // 玩家ID .valueCodec(new KryoObjectCodec<>(Player.class)) .allocateSize(10L * 1024 * 1024 * 1024) // 10GB .build(); // 写入数据 playerDB.put(10001L, new Player("张三", 99, "战士")); playerDB.flush(); // 服务器重启后,自动恢复! playerDB = RogueMap.<Long, Player>mmap() .persistent("data/players.db") .keyCodec(PrimitiveCodecs.LONG) .valueCodec(new KryoObjectCodec<>(Player.class)) .build(); Player player = playerDB.get(10001L); // 张三还在!

收益:

  • ✅ 重启恢复从分钟级降到秒级

  • ✅ 堆内存占用减少90%,Full GC基本消失

  • ✅ 数据持久化,玩家数据永不丢失


📊 场景2: 推荐系统 - 亿级用户特征本地缓存

痛点: 推荐系统需要缓存亿级用户特征,Redis成本高,本地HashMap内存爆炸。

RogueMap方案:

// 用户特征缓存 RogueMap<Long, UserFeature> featureCache = RogueMap.<Long, UserFeature>offHeap() .keyCodec(PrimitiveCodecs.LONG) // 用户ID .valueCodec(new KryoObjectCodec<>(UserFeature.class)) .maxMemory(50L * 1024 * 1024 * 1024) // 50GB堆外内存 .segmentedIndex(64) // 64个分段,高并发 .build(); // 高并发读取 UserFeature feature = featureCache.get(userId);

收益:

  • ✅ 本地缓存,延迟从毫秒级降到微秒级

  • ✅ 节省Redis集群成本,单机搞定

  • ✅ 堆内存占用极低,GC压力小


🔬 场景3: 大数据处理 - 百GB临时数据不落盘

痛点: 数据清洗、ETL任务产生大量临时数据,写磁盘慢,放内存炸。

RogueMap方案:

// 临时数据存储 RogueMap<String, Record> tempData = RogueMap.<String, Record>mmap() .temporary() // 自动清理 .keyCodec(StringCodec.INSTANCE) .valueCodec(new KryoObjectCodec<>(Record.class)) .allocateSize(100L * 1024 * 1024 * 1024) // 100GB .build(); // 处理海量数据 for (Record record : dataset) { String key = computeKey(record); tempData.put(key, record); } // 任务结束,自动清理临时文件 tempData.close();

收益:

  • ✅ 容量突破内存限制,可达TB级

  • ✅ 操作系统页缓存加速,速度接近内存

  • ✅ 自动清理,不留垃圾文件


五、上手简单,五分钟集成

Maven依赖

<dependency> <groupId>com.yomahub</groupId> <artifactId>roguemap</artifactId> <version>1.0.0-BETA1</version> </dependency>

快速开始

// 1. 创建RogueMap RogueMap<String, Long> map = RogueMap.<String, Long>offHeap() .keyCodec(StringCodec.INSTANCE) .valueCodec(PrimitiveCodecs.LONG) .maxMemory(100 * 1024 * 1024) .build(); // 2. 使用方式和HashMap一模一样 map.put("apple", 100L); map.put("banana", 200L); Long value = map.get("apple"); // 100L boolean exists = map.containsKey("banana"); // true map.remove("apple"); // 3. 记得关闭释放资源 map.close();

六、技术亮点总结

让我们用一张表格,快速回顾RogueMap的核心优势:

维度

HashMap

RogueMap

优势

容量限制

JVM堆大小

无限制(取决于磁盘)

⬆️ 突破内存墙

GC压力

极高

极低

⬇️ 减少87%堆内存

读性能

更快

⬆️ 提升2.4倍

持久化

不支持

支持

✅ 秒级恢复

并发性能

中等

优秀

⬆️ 乐观读提升15倍

API复杂度

简单

简单

🎯 一致体验


七、RogueMap的作者是谁

RogueMap的作者是铂赛东,他也是LiteFlow,TLog等框架的作者。

之后RogueMap也会被使用在LiteFlow中。成为本地储存规则,脚本的方案。

其实这也是一开始写这个项目的初衷。


八、常见问题FAQ

Q1: RogueMap支持哪些数据类型?

A:

  • 原始类型: Long、Integer、Short、Byte、Double、Float、Boolean(零拷贝,性能最优)

  • String: 内置StringCodec

  • 对象: 使用KryoObjectCodec(需要Kryo依赖)

Q2: 堆外内存会不会泄漏?

A: RogueMap使用引用计数管理内存,调用close()会自动释放。建议使用try-with-resources:

try (RogueMap<K, V> map = RogueMap.<K, V>offHeap()...build()) { // 使用map } // 自动释放

Q3: 支持并发吗?

A: 支持!SegmentedHashIndex使用分段锁+乐观读,高并发场景性能优秀。

Q4: 和Redis、RocksDB比如何选择?

A:

  • Redis: 分布式缓存,网络开销大,适合多机共享

  • RocksDB: 功能强大,API复杂,适合复杂查询

  • RogueMap: 本地缓存,API简单,适合单机高性能场景

Q5: 能存多大的数据?

A:

  • OffHeap模式: 受限于机器内存

  • Mmap模式: 受限于磁盘空间,理论上TB级都可以

Q6:为什么现在版本是1.0.0-BETA1

A:对,这是一个新项目,BETA1已经通过了100多个测试用例,已经表现很出色了。之后还会添加List,Set,Queue的支持。大家可以先试用下。希望大家持续关注这个项目。


九、写在最后

在Java生态中,我们不缺HashMap这样的内存数据结构,也不缺RocksDB这样的重量级存储引擎。

但我们缺少一个简单、高效、堆外内存的本地KV存储方案。

RogueMap就是为了填补这个空白而生:

  • 简单: HashMap级别的API,5分钟上手

  • 高效: 读性能提升2.4倍,内存节省87%

  • 灵活: 三种存储模式,覆盖绝大多数场景

  • 可靠: 持久化+自动恢复,数据不丢失

如果你的应用正在被大数据量、高GC压力、慢速度困扰,不妨试试RogueMap。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 3:38:45

Kotaemon门店运营助手:连锁企业知识统一管理

Kotaemon门店运营助手&#xff1a;连锁企业知识统一管理 在一家拥有数百家门店的连锁咖啡品牌中&#xff0c;新员工入职第三天就遇到了棘手问题&#xff1a;“顾客拿着优惠券却无法核销&#xff0c;系统提示‘活动未生效’。”他翻遍培训手册、群聊记录和内部Wiki&#xff0c;依…

作者头像 李华
网站建设 2026/4/18 7:28:54

大模型学习基础(六) 强化学习(Reinforcement Learning,RL)初步1.3

前篇文章提到&#xff0c;为了评估actor结合s所做的action是否为好的动作&#xff08;有利于reward积累&#xff09;而引入了优势函数A&#xff0c;在不同情况下优势函数的定义不同。优势函数可以由人为来进行设定&#xff0c;然后评估action的好坏&#xff0c;但如果每一笔数据…

作者头像 李华
网站建设 2026/4/18 6:28:17

基于springboot + vue敬老院管理系统

敬老院管理 目录 基于springboot vue敬老院管理系统 一、前言 二、系统功能演示 详细视频演示 三、技术选型 四、其他项目参考 五、代码参考 六、测试参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 基于springboot vue敬老院管理系统 一、前言 博主介…

作者头像 李华
网站建设 2026/4/18 6:26:22

SSM小学教师教辅管理平台526h9(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面

系统程序文件列表系统项目功能&#xff1a;课程名称,教师,学生,教学资料,教学视频,动画片,课程表开题报告&#xff1a;基于SSM的小学教师教辅管理平台设计与实现一、研究背景与意义1. 研究背景当前小学教学中&#xff0c;教师教辅管理普遍存在“资源分散、管理低效”的问题&…

作者头像 李华
网站建设 2026/4/18 8:36:34

BERT 和 Transformer 的关系

BERT 和 Transformer 的关系&#xff0c;可以用一句话概括&#xff1a;BERT 是基于 Transformer 架构构建的预训练语言模型。换句话说&#xff0c;Transformer 是“骨架”&#xff08;架构&#xff09;&#xff0c;BERT 是“身体”&#xff08;具体模型&#xff09;。 为了让你…

作者头像 李华