我的 Bitset 的大小是多少?
What is size of my Bitset?
我想在内存中存储 System.currentTimeInMillis
,尽可能少 space。因为我必须在内存中存储数百万个。
我将其转换为 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 的 java 文档。
Every bit set has a current size, which is the number of bits of space
currently in use by the bit set. Note that the size is related to the
implementation of a bit set, so it may change with implementation. The
length of a bit set relates to logical length of a bit set and is
defined independently of implementation.
您当前的代码无法存储数百万 long
(System.currentTimeInMillis
)。你可以使用 trove TLongHashSet 或者你应该看看 sparse bitset。但是 BitSet 有 int 索引,所以你应该将 long 从 currentTimeInMillis 压缩为 int。例如。 bitSetIndex = (int)(currentTimeInMillis - initialTime)。它为您提供从 initialTime 开始的 2^32 毫秒(~ 50 天)间隔。
//store sample for bitset:
bitSet.set(System.currentTimeInMillis());
编辑
一个 BitSet 对象在堆上分配了 100 多个字节。所以你应该为很多 long 值重用一个 BitSet 对象。最简单的方法是在 BitSet 中使用 long 值作为索引,并将该索引处的值设置为 true。但是有几个问题(我在上面描述过):
- BitSet 的 int index 不长
- java.util.BitSet 内存效率不高。
正如 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 个字节
- 1 个字节用于
boolean sizeIsSticky
结果是 49 - 与您看到的 48 相差不远。如果那些 object headers are compressed,但也引入了填充,那么这可能就是 48 的来源。
Why does bitSet.length() and bitSet.size() differ? I assume length() is correct?
BitSet.size()
是它用来存储位值的内部数据结构的大小。由于 BitSet
内部使用 long[]
数组,因此大小始终是 64 位的倍数。例如。如果在 BitSet
中设置第 64 位,则 BitSet
必须增加 long[]
数组的容量才能存储该值,因为每个 long 可以 "only" 存储 64 位.例如
BitSet bitSet = new BitSet();
for (int i = 0; i <= 64; i++) {
bitSet.set(i, true);
System.out.println(bitSet.size());
}
BitSet.length()
returnsBitSet
中的实际占用位。因此,如果您创建一个新的 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
I am using memory-measurer to learn about the size of bitSet, but it tell me 48 bytes, why is it not (41/8) byte?
因为内存填充。也称为 data structure alignment。
BitSet
object 在内存中需要数学上的 41 个字节。
- 8 个字节用于 object header
- 20 个字节用于
long[]
- 数组中的
long
为 8 个字节
- 4 个字节用于
wordsInUse
int
变量
- 1 个字节用于
sizeIsSticky
boolean
但是 jvm 无法分配 41 位,所以它四舍五入到下一个 8 的倍数。即 48。
此大小可能会有所不同,因为 object header 大小可能因一个 JVM 实现而异。所以如果 object header 是 16 个字节。总数将为 49,jvm 将其四舍五入为下一个 8 的倍数。在本例中为 56。
首先,我想推荐正确的工具来分析 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
,尽可能少 space。因为我必须在内存中存储数百万个。
我将其转换为 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 的 java 文档。
Every bit set has a current size, which is the number of bits of space currently in use by the bit set. Note that the size is related to the implementation of a bit set, so it may change with implementation. The length of a bit set relates to logical length of a bit set and is defined independently of implementation.
您当前的代码无法存储数百万 long
(System.currentTimeInMillis
)。你可以使用 trove TLongHashSet 或者你应该看看 sparse bitset。但是 BitSet 有 int 索引,所以你应该将 long 从 currentTimeInMillis 压缩为 int。例如。 bitSetIndex = (int)(currentTimeInMillis - initialTime)。它为您提供从 initialTime 开始的 2^32 毫秒(~ 50 天)间隔。
//store sample for bitset:
bitSet.set(System.currentTimeInMillis());
编辑
一个 BitSet 对象在堆上分配了 100 多个字节。所以你应该为很多 long 值重用一个 BitSet 对象。最简单的方法是在 BitSet 中使用 long 值作为索引,并将该索引处的值设置为 true。但是有几个问题(我在上面描述过):
- BitSet 的 int index 不长
- java.util.BitSet 内存效率不高。
正如 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 个字节
- 1 个字节用于
boolean sizeIsSticky
结果是 49 - 与您看到的 48 相差不远。如果那些 object headers are compressed,但也引入了填充,那么这可能就是 48 的来源。
Why does bitSet.length() and bitSet.size() differ? I assume length() is correct?
BitSet.size()
是它用来存储位值的内部数据结构的大小。由于 BitSet
内部使用 long[]
数组,因此大小始终是 64 位的倍数。例如。如果在 BitSet
中设置第 64 位,则 BitSet
必须增加 long[]
数组的容量才能存储该值,因为每个 long 可以 "only" 存储 64 位.例如
BitSet bitSet = new BitSet();
for (int i = 0; i <= 64; i++) {
bitSet.set(i, true);
System.out.println(bitSet.size());
}
BitSet.length()
returnsBitSet
中的实际占用位。因此,如果您创建一个新的 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
I am using memory-measurer to learn about the size of bitSet, but it tell me 48 bytes, why is it not (41/8) byte?
因为内存填充。也称为 data structure alignment。
BitSet
object 在内存中需要数学上的 41 个字节。
- 8 个字节用于 object header
- 20 个字节用于
long[]
- 数组中的
long
为 8 个字节 - 4 个字节用于
wordsInUse
int
变量 - 1 个字节用于
sizeIsSticky
boolean
但是 jvm 无法分配 41 位,所以它四舍五入到下一个 8 的倍数。即 48。
此大小可能会有所不同,因为 object header 大小可能因一个 JVM 实现而异。所以如果 object header 是 16 个字节。总数将为 49,jvm 将其四舍五入为下一个 8 的倍数。在本例中为 56。
首先,我想推荐正确的工具来分析 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