2015-10-21 94 views
7

我想用尽可能小的空间将System.currentTimeInMillis存储在内存中。因为我必须将数百万个数据存储在内存中。我的Bitset的大小是多少?

我把它转化为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

我的BitSet困惑

+0

64位(可能是'long')是实际用来保存数据的BitSet的位数。 (它不能分配41位) – aioobe

+0

已知时间在彼此的一定范围内吗?你可以扔掉每个“长”的高字节而不会丢失信息吗? –

回答

4

首先,我想建议正确的工具来分析JVM中的对象布局方案 - JOL。在你的情况(java -jar jol-cli/target/jol-cli.jar internals 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 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] 

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

这意味着一个空BitSet本身使用的48个字节包括长阵列。你也可以在不同的虚拟机模式下得到估计的对象布局java -jar jol-cli/target/jol-cli.jar estimates java.util.BitSet

1

请参阅Java文档。

每个位组具有电流的大小,这是目前在由比特组使用空间 的比特数。请注意,该大小与位设置的实现相关,所以它可能随实现而改变。位集的长度与位集的逻辑长度有关,并且是与实现无关地定义的 。

2

您目前的代码不能存储数百万的longSystem.currentTimeInMillis)。你可以使用trove TLongHashSet,或者你应该看看sparse bitset。但是BitSet有int索引,所以你应该从currentTimeInMillis长时间压缩到int。例如。 bitSetIndex =(int)(currentTimeInMillis - initialTime)。它会给你从initialTime开始的2^32毫秒(〜50天)间隔。

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

EDIT

一个位集合对象分配在堆上多于100个字节。所以你应该重用一个BitSet对象来获得很多很长的值。最简单的方法是在BitSet中使用long值作为索引,并在此索引处将value设置为true。但是有几个问题(我把他们描绘以上):

  1. BitSet中有不长
  2. java.util.BitSet中不存储effecient INT指数。
+0

'不能存储数百万长',你能解释为什么吗? – daydreamer

+0

我编辑我的答案 – sibnick

0

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

对于48个字节,我数中的代码:

  • 16字节for the BitSet object itselflong[]对象
  • 20个字节(16为对象,4的长度)
  • 8字节对于阵列的内容(每个元素是8个字节,但只有一个)
  • 4个字节用于int wordsInUse
  • 1个字节用于boolean sizeIsSticky

其中产量49--距离你所看到的48不远。如果那些object headers are compressed,但填充也引入,那么这可能是48来自何处。

1

为什么bitSet.length()和bitSet.size()有区别?我认为长度()是正确的?

BitSet.size()是它用来存储位值的内部数据结构的大小。由于BitSet内部使用long[]阵列,因此大小总是64位的倍数。例如。如果将第64位设置为BitSetBitSet必须增加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,因为只需要一个长的来存储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 

我使用内存测量,以了解位集合的大小,但它告诉我48个字节,为什么不(第41/8)字节?

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

  • 8字节为目标首标的
  • 为所述阵列中的 long[]
  • 8个字节用于long
  • 20字节
  • 4个字节用于为sizeIsStickybooleanwordsInUseint可变
  • 1字节

但jvm无法分配41位,因此会将其舍入到8的下一个倍数。即48。

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