大数值使用BitSet存储导致的内存溢出

背景:

在日常的工作中,使用Redis的bitmap统计每天的登录用户数,使用java的BitSet进行统计总数或者与或非等操作时,我们可以看到BitSet/Redis的Bitmap操作的身影,他们也的确能减少内存的使用量以及操作的性能,但是我们不能神话这两个结构的作用,认为什么数据都适合转成用BitSet/Redis的Bitmap然后操作.

BitMap/BitSet不适用的场景:

举一个最简单的例子,保存一个Integer.max/2的正整数值,我们正常使用Int四个字节就够了,把这个整数放到Set或者List集合进行操作即可,但是有些人非要使用BitSet来存放,那么你知道仅仅存储这一个整数,BitSet需要多少字节吗?

    public static void bitSetTest() {
        BitSet bitSet = new BitSet();
        bitSet.set(Integer.MAX_VALUE / 2, true);
        System.out.println(bitSet.toByteArray().length);
    }

程序运行结果如下: 134217728, 看到没,BitSet需要将近134217728/1024/1024=130M的字节来存放这一个整数,惊讶不?同理这个结论可以推理到使用Redis的BitMap结构,那么为什么这些结构需要这么多的字节来存放仅仅一个整数?我们先大概看一下BitSet/Bitmap的存储数据结构:
在这里插入图片描述
可以看到所谓的bit操作只是说第一个字节可以存放8这个绝对值,第一个字节+第二个字节可以存放16这个绝对值,也就是说绝对值越大,所需要的字节数组越大,其实只有最后一个bit存放着1这个位,其他前面的字节数组都是存放0,完全被浪费掉了,不过还是需要分配这些字节数组,这就是存放Integer.MAX_VALUE /2 这样一个整数需要134M字节数组的原因。
其实从这里你可以发现类似BitSet和Redis的BitMap的结构,它占用的字节数只和要存放的数字的最大值有关,也就是说和这个BitSet存放进去多少数量没有关系,比如下面的代码:

    public static void bitSet1Test(){
        BitSet bitSet = new BitSet();
        bitSet.set(Integer.MAX_VALUE /2, true);
        for(int i=0;i<1000000;i++){
            bitSet.set(RandomUtils.nextInt(0, Integer.MAX_VALUE /2));
        }
        System.out.println(bitSet.toByteArray().length);
    }

程序运行结果如下: 134217728, 可以看到BitSet里面存放了10w个元素,不过元素的最大值是Integer.max/2,最终它占用的内存大小是134217728/1024/1024=130M,有没有发现存放1个Integer.max/2和存放10w个最大值不超过Integer.max/2的两个BitSet的内存占用是一样的?没错,就是一样的

所以我们可以得出结论: 想要高效使用BitSet和Redis的Bitmap数据结构,里面存放的数值要尽可能小,这样才能占用尽可能小的内存。

那么有人就问了,如果我的数值就是这么大呢?有办法使用BitSet和Redis的Bitmap数据结构吗?
答案是:你需要经过一层转换,也就是你需要把你这个大的整数值映射到某个小的整数值再放到BitSet/Redis的Bitmap中,等到从BitSet/Redis的Bitmap把这个小的数值取出时,再映射成原来的大的数值,这样就可以高效的使用BitSet/Redis的Bitmap结构进行操作了

彩蛋:Roaring64Bitmap/Roaring32Bitmap 和这里的BitSet/Redis的Bitmap的结论一样吗?

Roaring64Bitmap/Roaring32Bitmap和这里的BitSet/Redis的Bitmap的存储结构不一样,他们占用的内存虽然也是受到数值绝对值大小的影响,但是并没有像BitSet/Redis的Bitmap这样极端,所以使用Roaring64Bitmap和Roaring32Bitmap结构时最好首先进行测试,不过可以肯定的是,这些结构在保存大的数值时内存占用很大,和直接使用Long数组存放相差并不大,所以原则是一致的,首先把大的数值转成小的数值后再放到这些Bit数据结构中进行存储和运算.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值