我的Bitset的大小是多少?

时间:2015-10-21 05:09:49

标签: java memory memory-management data-structures bits

我想以尽可能小的空间将System.currentTimeInMillis存储在内存中。因为我必须将数百万存储在内存中。

我将其转换为binaryString,后者给了我41 bits

这是我的程序

public class BitSetSize {
    public static void main(final String[] args) {
        final long currentTimeMillis = System.currentTimeMillis();
        final String currentTimeToBinaryString = Long.toBinaryString(currentTimeMillis);
        System.out.println("Size in bits: " + currentTimeToBinaryString.length());

        final BitSet bitSet = BitSet.valueOf(new long[]{currentTimeMillis});
        System.out.println("Bitset length: " + bitSet.length());
        System.out.println("Bitset size: " + bitSet.size());

        System.out.println("Size of biset object(bytes): " + MemoryMeasurer.measureBytes(bitSet));
    }
}

但是当我运行它时,我得到了

Size in bits: 41
Bitset length: 41
Bitset size: 64
Size of biset object(bytes): 48

问题
- 为什么bitSet.length()bitSet.size()有所不同?我认为length()是正确的吗? - 我正在使用memory-measurer来了解bitSet的大小,但它告诉我48 bytes,为什么不是(41/8) byte

我很困惑

5 个答案:

答案 0 :(得分:4)

首先,我想建议正确的工具来分析JVM中的对象布局方案 - JOL。在您的情况下(java -jar jol-cli/target/jol-cli.jar internals java.util.BitSet)JOL产生以下结果:

Running 64-bit HotSpot VM.
Using compressed references with 3-bit shift.
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

java.util.BitSet object internals:
 OFFSET  SIZE    TYPE DESCRIPTION                    VALUE
      0     4         (object header)                01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4         (object header)                00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4         (object header)                f4 df 9f e0 (11110100 11011111 10011111 11100000) (-526393356)
     12     4     int BitSet.wordsInUse              0
     16     1 boolean BitSet.sizeIsSticky            false
     17     3         (alignment/padding gap)        N/A
     20     4  long[] BitSet.words                   [0]
Instance size: 24 bytes (reported by Instrumentation API)
Space losses: 3 bytes internal + 0 bytes external = 3 bytes total

由于静态字段,您的计算不正确,因此空BitSet本身保留24个字节。请注意,这些计算并非100%准确,因为未将long[]对象的大小考虑在内。所以正确的结果是java -jar jol-cli/target/jol-cli.jar externals java.util.BitSet

Running 64-bit HotSpot VM.
Using compressed references with 3-bit shift.
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

java.util.BitSet@6b25f76bd object externals:
          ADDRESS       SIZE TYPE             PATH                           VALUE
        7ae321a48         24 java.util.BitSet                                (object)
        7ae321a60         24 [J               .words                         [0]

这意味着空BitSet本身使用48个字节,包括长数组。您还可以在不同的VM模式java -jar jol-cli/target/jol-cli.jar estimates java.util.BitSet

中获取估计的对象布局

答案 1 :(得分:3)

  

为什么bitSet.length()和bitSet.size()有所不同?我假设length()是正确的?

BitSet.size()是用于存储位值的内部数据结构的大小。由于BitSet内部使用long[]数组,因此大小始终是64位的倍数。例如。如果您在BitSet中设置第64位,BitSet必须增加long[]数组的容量才能存储该值,因为每个long只能"只有"存储64位。 E.g。

BitSet bitSet = new BitSet();
for (int i = 0; i <= 64; i++) {
  bitSet.set(i, true);
  System.out.println(bitSet.size());
}

BitSet.length()返回BitSet中的实际占用位。因此,如果您创建一个新的BitSet,它的长度为0.如果您设置第4位,则长度将为5. size将保持为64,因为只需要一个长度存储5位。

BitSet bitSet = new BitSet();
System.out.println(bitSet.length()); // 0
bitSet.set(4, true);
System.out.println(bitSet.size());  // 64
System.out.println(bitSet.length()); // 5
  

我正在使用内存测量器来了解bitSet的大小,但它告诉我48个字节,为什么不是(41/8)字节?

由于内存填充。也称为data structure alignmentBitSet对象在内存中需要41个字节。

  • 对象标题的8个字节
  • long[]
  • 的20个字节
  • 数组中long的8个字节
  • wordsInUse int变量
  • 的4个字节
  • sizeIsSticky boolean
  • 的1个字节

但是jvm不能分配41位,所以它将它四舍五入到8的下一个倍数。那就是48。

此大小可能会有所不同,因为对象标头大小可能因JVM实现而异。所以如果对象头是16个字节。总数将为49,jvm将其舍入到下一个8的倍数。在这种情况下为56。

答案 2 :(得分:2)

您当前的代码无法存储数百万longSystem.currentTimeInMillis)。您可以使用trove TLongHashSet,或者您应该查看sparse bitset。但是BitSet有int索引,所以你应该将currentTimeInMillis中的long压缩为int。例如。 bitSetIndex =(int)(currentTimeInMillis - initialTime)。它从initialTime开始给你2 ^ 32毫秒(~50天)的间隔。

//store sample for bitset:
bitSet.set(System.currentTimeInMillis());

修改

一个BitSet对象在堆上分配超过100个字节。因此,您应该为许多长值重用一个BitSet对象。最简单的方法是在BitSet中使用long值作为索引,并在此索引处将值设置为true。但是有几个问题(我在上面描述过):

  1. BitSet的int index不长
  2. java.util.BitSet不具有记忆效果。

答案 3 :(得分:1)

请参阅BitSet的java doc。

  

每个位集都有一个当前大小,即空间位数   目前由位集使用。请注意,大小与   实现一个位集,因此它可能会随着实现而改变。该   位集的长度与位集的逻辑长度有关,并且是   独立于实施而定义。

答案 4 :(得分:0)

正如BetaRide所提到的,BitSet占用的实际大小是特定于实现的。也就是说,在Oracle / OpenJDK实现中(至少在6,7和8中),状态的基本元素是long[] of words。这意味着大小总是64的倍数。

对于48个字节,我在代码中计算:

  • 16字节for the BitSet object itself
  • long[]对象的20个字节(对象为16个,长度为4个)
  • 数组内容的8个字节(每个元素为8个字节,但只有一个)
  • int wordsInUse
  • 的4个字节
  • boolean sizeIsSticky
  • 的1个字节

其中49收益 - 距您所看到的48不远。如果那些object headers are compressed,但也引入了填充,那么这可能就是48来自的地方。