我听一位同事说,如果我在 Java 类中删除了一个 String 成员,即使 String 为空,我也会支付“24 个字节”。那准确吗?Integer、Float、Double 一样吗?(与 int、float、double 不同,它们分别只有 4、4 和 8 个字节)。
4 回答
您将为参考支付 4 或 8 个字节。您是否会为每个“容器”对象实例支付额外的对象取决于您如何获取空字符串。例如,如果您使用字面量,""
那么所有实例都将引用同一个对象,因此您只需为引用本身付费。
如果您为每个实例创建一个单独的空字符串,那么显然这将占用更多内存。
字符串由对象头(HotSpot 上 2 个字,J9 上 3 个字)、int 字段和 char 数组引用组成。Char 数组本身由 header、int 字段和数组的其余部分组成。一切都填充到 8 个字节。通常,字符编码为每个字符 2 个字节。一些 JVM 可以为 char 使用替代编码。在某一时刻,J9 可以用 UTF8 编码字符串。如果字符串由简单字符组成(本质上是 UTF-8 的 1 字节编码字形,如英文字母、数字等),现代 HotSpot 将使用每个字符 1 个字节。否则,它对整个数组使用每个字符 2 个字节。
因此,空字符串大小的深度大小为:
- 32 位 HotSpot 上的 32 个字节(字符串 16 个字节,char[] 16 个字节)
- 64 位 HotSpot 上 56 字节(String 为 32 字节,char[] 为 24 字节)
- 64 位 HotSpot 上 40 字节,带有压缩引用(24 字节用于字符串,16 字节用于 char[])
通常,不会分配空字符数组,因为它的值将被推断出来。毕竟所有空字符数组都是相等的。因此,空字符串的实际惩罚将是浅字符串大小;对于当今最常见的情况(带有压缩参考的 64 位 HotSpot),这是24 字节。
IBM J9 JVM 有更大的对象头,它会分配更多。
借用这个答案:程序为空字符串打印 32 个字节(字符串池中的 "" 为 0)。
public static void main(String... args) {
long free1 = free();
String s = "";
long free2 = free();
String s2 = new String("");
long free3 = free();
if (free3 == free1) System.err.println("You need to use -XX:-UseTLAB");
System.out.println("\"\" took " + (free1 - free2) + " bytes and new String(\"\") took " + (free2
- free3) + " bytes.");
}
private static long free() {
return Runtime.getRuntime().freeMemory();
}
确实,您为 Integer 支付的费用要比为 int 多得多。我记得检查了几个 Java 版本,并且 Integer 多占用了大约 24 个字节。只要您的字符串指向一个空对象(也就是什么都没有),您只需要在内存中保留一个指针,而且我认为 JVM 不会保留一个位置来初始化它,在这种情况下您不会浪费了 24 个字节,只有 8 个。如果您创建字符串(甚至是空字符串“”),那么您已经在内存中拥有一个对象,并且由于所有对象都继承 Object,它们带有一些包袱并占用比您直观预期更多的内存. 根据您对字符串的使用,一个常见的解决方案是从一个空对象开始,并在需要时延迟初始化它。