我的Bitset的大小是多少?
我想用尽可能小的空间将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困惑
首先,我想建议正确的工具来分析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
请参阅Java文档。
每个位组具有电流的大小,这是目前在由比特组使用空间 的比特数。请注意,该大小与位设置的实现相关,所以它可能随实现而改变。位集的长度与位集的逻辑长度有关,并且是与实现无关地定义的 。
您目前的代码不能存储数百万的long
(System.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。但是有几个问题(我把他们描绘以上):
- BitSet中有不长
- java.util.BitSet中不存储effecient INT指数。
'不能存储数百万长',你能解释为什么吗? – daydreamer
我编辑我的答案 – sibnick
正如BetaRide所提到的,BitSet所采用的实际大小是特定于实现的。也就是说,在Oracle/OpenJDK实现中(至少在6,7和8中),状态的基本元素是long[]
of words。这意味着大小总是64的倍数。
对于48个字节,我数中的代码:
- 16字节for the BitSet object itself为
long[]
对象 - 20个字节(16为对象,4的长度)
- 8字节对于阵列的内容(每个元素是8个字节,但只有一个)
- 4个字节用于
int wordsInUse
- 1个字节用于
boolean sizeIsSticky
其中产量49--距离你所看到的48不远。如果那些object headers are compressed,但填充也引入,那么这可能是48来自何处。
为什么bitSet.length()和bitSet.size()有区别?我认为长度()是正确的?
BitSet.size()
是它用来存储位值的内部数据结构的大小。由于BitSet
内部使用long[]
阵列,因此大小总是64位的倍数。例如。如果将第64位设置为BitSet
,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,因为只需要一个长的来存储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 alignment。 BitSet
对象在内存中需要数学41个字节。
- 8字节为目标首标的 为所述阵列中的
- 8个字节用于
long
- 20字节
- 4个字节用于为
sizeIsSticky
boolean
的wordsInUse
int
可变 - 1字节
long[]
但jvm无法分配41位,因此会将其舍入到8的下一个倍数。即48。
此大小可能会有所不同,因为对象头大小可能因JVM实现而异。所以如果对象头是16字节。总数将为49,jvm将其舍入到8的下一个倍数。在这种情况下,56.
64位(可能是'long')是实际用来保存数据的BitSet的位数。 (它不能分配41位) – aioobe
已知时间在彼此的一定范围内吗?你可以扔掉每个“长”的高字节而不会丢失信息吗? –