我的Bitset的大小是多少?

我想以尽可能小的空间将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()是正确的?
– 我正在使用内存测量器来了解bitSet的大小,但它告诉我48 bytes ,为什么它不是(41/8) byte

我很迷惑

首先,我想建议正确的工具来分析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

您当前的代码无法存储数百万( System.currentTimeInMillis )。 您可以使用trove TLongHashSet,或者您应该查看稀疏位集 。 但是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索引不长
  2. java.util.BitSet不具有内存效率。

请参阅BitSet的 java doc。

每个位集都有一个当前大小,即当前位集使用的空间位数。 请注意,大小与位集的实现有关,因此它可能随实现而改变。 位集的长度与位集的逻辑长度有关,并且与实现无关地定义。

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

BitSet.size()是用于存储位值的内部数据结构的大小。 由于BitSet内部使用long[]数组,因此大小始终是64位的倍数。 例如,如果在BitSet设置第64位,则BitSet必须增加long[]数组的容量才能存储该值,因为每个long都可以“仅”存储64位。 例如

 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,因为只需要一个long来存储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)字节?

因为内存填充。 也称为数据结构对齐 。 BitSet对象在内存中需要41个字节。

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

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

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

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

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

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

产量为49 – 与您所看到的48相差不远。 如果这些对象头被压缩 ,但也引入了填充,那么这可能就是48来自的地方。