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,最好是从提到证据的差异。