string与字符

我有一些来自IBM的幻灯片,命名为: “从Java代码到Java堆:了解应用程序的内存使用情况” ,也就是说,当我们使用String而不是char[]

单个字符的最大开销是24:1!

但我无法理解这里提到的开销。 任何人都可以帮忙吗?

资源 :

在这里输入图像说明

这个数字涉及JDK 6- 32位。

JDK 6

在作为指向char[]数组区域的指针实现的Java-7之前的世界string中:

 // "8 (4)" reads "8 bytes for x64, 4 bytes for x32" class String{ //8 (4) house keeping + 8 (4) class pointer char[] buf; //12 (8) bytes + 2 bytes per char -> 24 (16) aligned int offset; //4 bytes -> three int int length; //4 bytes -> fields align to int hash; //4 bytes -> 16 (12) bytes } 

所以我数了:

 36 bytes per new String("a") for JDK 6 x32 <-- the overhead from the article 56 bytes per new String("a") for JDK 6 x64. 

JDK 7

只是为了比较,在JDK 7+ String是一个只持有char[]缓冲区和hash字段的类。

 class String{ //8 (4) + 8 (4) bytes -> 16 (8) aligned char[] buf; //12 (8) bytes + 2 bytes per char -> 24 (16) aligned int hash; //4 bytes -> 8 (4) aligned } 

所以就是:

 28 bytes per String for JDK 7 x32 48 bytes per String for JDK 7 x64. 

UPDATE

对于3.75:1比例,请参阅下面的@安德烈的解释。 随着琴弦长度的增加,这个比例下降到1。

有用的链接:

  • Javastring和string相关对象的内存使用情况 。
  • 计算Map Entry的内存 – 一种简单的技术来获取对象的大小。

在JVM中,一个字符variables被存储在一个单独的16位内存分配中,并且改变为该Javavariables覆盖相同的内存位置。这使得创build或更新字符variables非常快并且内存便宜,但是与JVM相比,string中使用的静态分配。

JVM将Javastring存储在一个可变大小的内存空间(本质上是一个数组)中,当string对象被创build或首次赋值时,该string与string的大小完全相同(加上string终止字符的1)。 因此,具有初始值“HELP!”的对象 将被分配96位存储器(6个字符,每个16位大小)。 这个值被认为是不可变的,允许JVM内联对该variables的引用,使得静态string赋值非常快速,非常紧凑,而且从JVM的angular度来看非常高效。

参考

我会尝试解释源文章中引用的数字。

本文描述的对象元数据通常包含:class,flags和lock。

类和锁存储在对象头中,并在32位虚拟机上占用8个字节。 我还没有find任何关于对象头中有标志信息的JVM实现的信息。 这可能是这样的,这是存储在外部的地方(例如通过垃圾收集器来计算对象的引用等)。

所以我们假设文章谈到了一些使用12字节内存的x32 AbstractJVM来存储关于对象的元信息。

那么对于char[]我们有:

  • 12个字节的元信息(x32 JDK 6上的8个字节,x64 JDK上的16个字节)
  • 数组大小为4个字节
  • 每个字符存储2个字节
  • 如果字符数为奇数,则为2个字节(x64 JDK: 2 * (4 - (length + 2) % 4)

对于java.lang.String我们有:

  • 12个字节的元信息(x32 JDK6上的8个字节,x64 JDK6上的16个字节)
  • string字段为16字节(JDK6为8字节,JDK7为8字节)
  • 如上所述存储char []所需的内存

那么,让我们来计算需要多less内存来存储"MyString"作为String对象:

 12 + 16 + (12 + 4 + 2 * "MyString".length + 2 * ("MyString".length % 2)) = 60 bytes. 

从另一方面,我们知道只存储数据(没有关于数据types,长度或其他任何信息)我们需要:

 2 * "MyString".length = 16 bytes 

开销是60/16 60 / 16 = 3.75

对于单个字符数组,我们也得到“最大开销”:

 12 + 16 + (12 + 4 + 2 * "a".length + 2 * ("a".length % 2)) = 48 bytes 2 * "a".length = 2 bytes 48 / 2 = 24 

遵循文章作者的逻辑,当我们存储一个空string时,最终实现了价值无穷大的最大开销:)。

我从旧的阅读stackoverflow答案不能得到它。 在Oracle的JDK中,一个String有四个实例级别的字段:

 A character array An integral offset An integral character count An integral hash value 

这意味着每个string引入一个额外的对象引用(string本身),除了字符数组本身之外还有三个整数。 (偏移量和字符数可以允许在通过String#substring()方法生成的String实例之间共享字符数组,这是一些其他Java库实现者避免的deviseselect。)除了额外的存储成本之外,还有一个访问间接的更多级别,更不用说String保护字符数组的边界检查了。

如果你只能分配和使用基本的字符数组,那么这里就有空间可以保存。 虽然在Java中这样做肯定不是惯用的, 明智的意见将是合理的select,最好是从提到证据的差异。