Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/visual-studio-2008/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 字符串与字符[]_Java_String_Memory_Memory Management - Fatal编程技术网

Java 字符串与字符[]

Java 字符串与字符[],java,string,memory,memory-management,Java,String,Memory,Memory Management,我有一些来自IBM的幻灯片,名为:,也就是说,当我们使用String而不是char[]时 单个字符的最大开销为24:1 但我无法理解这里指的是什么开销。有人能帮忙吗 来源: 在JVM中,字符变量存储在单个16位内存分配中,对该Java变量的更改将覆盖相同的内存位置。这使得创建或更新字符变量的速度非常快,内存也非常便宜,但与字符串中使用的静态分配相比,JVM的开销会增加 JVM将Java字符串存储在可变大小的内存空间(本质上是一个数组)中,该内存空间的大小与创建string对象或首次为string

我有一些来自IBM的幻灯片,名为:,也就是说,当我们使用
String
而不是
char[]

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

但我无法理解这里指的是什么开销。有人能帮忙吗

来源:


在JVM中,字符变量存储在单个16位内存分配中,对该Java变量的更改将覆盖相同的内存位置。这使得创建或更新字符变量的速度非常快,内存也非常便宜,但与字符串中使用的静态分配相比,JVM的开销会增加

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


此图与JDK 6-32位相关

jdk6 在Java-7之前的世界字符串中,字符串被实现为指向
char[]
数组区域的指针:

// "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.
所以它是:

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

有关
3.75:1
比率,请参见下文@Andrey的解释。随着字符串长度的增加,该比例降至1

有用链接:

  • -获取对象大小的简单技术

我从旧的stackoverflow中读到的答案无法得到。 在Oracle的JDK中,字符串有四个实例级字段:

A character array
An integral offset
An integral character count
An integral hash value
这意味着每个字符串都会引入一个额外的对象引用(字符串本身),以及字符数组本身之外的三个整数。(偏移量和字符计数允许在通过String#substring()方法生成的字符串实例之间共享字符数组,这是一些其他Java库实现者避免的设计选择。)除了额外的存储成本之外,还有一个更高级别的访问间接性,更不用说字符串保护其字符数组的边界检查了


如果您可以只分配和使用基本字符数组,那么就可以在那里节省空间。但在Java中这样做肯定不是惯用的;明智的评论将有助于证明选择的合理性,最好是提及描述差异的证据。

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

本文描述的对象元数据通常包括:类、标志和锁

类和锁存储在对象头中,在32位VM上占用8个字节。我还没有找到任何关于JVM实现的信息,这些实现在对象头中有标志信息。可能是这样的,它存储在外部的某个地方(例如,由垃圾收集器计算对对象的引用等)

因此,假设本文讨论的是某个x32 AbstractJVM,它使用12字节的内存来存储有关对象的元信息

对于
char[]
我们有:

  • 12字节的元信息(x32 JDK 6上8字节,x64 JDK上16字节)
  • 数组大小为4字节
  • 存储的每个字符有2个字节
  • 如果字符数为奇数,则对齐2字节(在x64 JDK上:
    2*(4-(长度+2)%4)
对于
java.lang.String
我们有:

  • 12字节的元信息(x32 JDK6上8字节,x64 JDK6上16字节)
  • 字符串字段为16字节(JDK6为16字节,JDK7为8字节)
  • 如上所述,存储char[]所需的内存
因此,让我们计算一下将
“MyString”
存储为
String
对象需要多少内存:

12 + 16 + (12 + 4 + 2 * "MyString".length + 2 * ("MyString".length % 2)) = 60 bytes.
另一方面,我们知道,要仅存储数据(不包含数据类型、长度或任何其他信息),我们需要:

开销为
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

按照文章作者的逻辑,当我们存储一个空字符串时,最终实现了值无穷大的最大开销:)。

您也可以添加对源代码的引用吗?请看一些IBM的幻灯片,名为:从Java代码到Java堆:了解应用程序的内存使用情况,没有URL将此信息添加到问题中,而不是含糊不清的“某处”,这很好:)对于内存性能,我并不认为JVM需要终止字符though@ratchetfreak注意,如果您有空终止符,那么在JVM的保护下,您可以轻松地使用一些C库函数对字符串进行操作。至少,这是Python实现带有字符串长度字段和空终止符的字符串的原因之一。可能与Java的原因相同。总的来说,有时候有一些冗余是很方便的。这不是一个很好的参考
char[]
不存储零终止符。Python是另一个故事,它更面向C。@MarkoTopolnik可能是当您分配一个字符[n]时,jvm将分配一个带有空终止符的额外位置的数组,但这是一个实现细节,我知道现在发生了什么。也许你应该在答案中稍微说明一下这一点。我感到困惑,所以我相信其他人可能会。您显示的是字符串的大小,而不是字符的大小[1]。这两种方法都是必要的ratio@Darkhogg自从Java7更新6以来,它就已经消失了。@Darkhogg邮件列表上有一些东西;关键是它造成的伤害大于好处。@Darkhogg是的,运气不好,它伤害了一些用例。另一方面,对于小型stri,它更透明、更可预测、更节省空间
2 * "MyString".length = 16 bytes
12 + 16 + (12 + 4 + 2 * "a".length + 2 * ("a".length % 2)) = 48 bytes
2 * "a".length = 2 bytes
48 / 2 = 24