5

这是 Sun JDK 1.6u21,x64。

我有一个类用于试验 perm gen 的使用,它只包含一个大字符串(512k 个字符):

public class Big0 {
     public String bigString =
         "A string with 2^19 characters, should be 1 MB in size";
}

getUsage().toString()我检查了永久代对象上使用的 perm gen 使用情况MemoryPoolMXBean(在 u21 中称为“PS Perm Gen”,尽管它具有不同版本或不同垃圾收集器的名称略有不同。

当我第一次引用该类时,例如通过 read Big0.class, perm gen 跳跃了 ~500 KB - 这是我所期望的,因为字符串的常量池编码是 UTF-8,并且我只使用 ASCII 字符。

然而,当我实际创建这个类的一个实例时,perm gen 会跳跃约 2 MB。由于这是一个 1 MB 的内存字符串(每个 UTF16 字符 2 个字节,当然没有代理),我对为什么内存使用量是两倍感到困惑。

如果我将字符串设为静态,也会出现同样的效果。如果我使用 final,它会编译失败,因为我超过了 65535 字节的常量池项目的限制(不知道为什么关闭 final 也可以避免这种情况——考虑一下这是一个额外的问题)。

任何见解表示赞赏!

编辑:我还应该指出,这发生在非静态、最终非静态和静态字符串中,但不适用于最终静态字符串。由于这已经是字符串常量的最佳实践,也许这主要是学术兴趣。

4

4 回答 4

2

我认为这是您的测试课程的人工制品。我创建了一个类似的类,然后用 javap 反编译它。

[eclipse] java 编译器将字符串文字分成块,每块不超过 64k。用于初始化非常量字段的字节码包括将源字符串与一系列 StringBuilder 操作拼凑在一起。虽然最终被拘留的是这个巨大的字符串,但它所构成的大原子却占据了常量池中的空间。

于 2011-02-23T15:19:55.793 回答
0

Java 字符的宽度为每个字符 2 个字节(不管它是 ASCII 还是高于 255 的代码点)。我认为您看到的是 Java VM 在类初始化后立即将字符串的内部类文件存储(修改后的 UTF8)版本转换为其内部扩展形式(这是在创建实例之前完成的)

于 2011-02-21T10:21:38.360 回答
0

虽然类文件格式指定修改后的 UTF-8作为其String文本的存储格式,但运行时的内部格式是 UTF-16。AString将其数据以 UTF-16 编码形式存储在 a char[](通常,它是依赖于实现的) 中。大多数字符在此编码中占用 2 个字节(BMP 之外的字符占用更多)。

我已经看到对一个修改的引用rt.jar,其中包含一个java.lang.String具有专用代码路径/仅用于 ASCII 字符串的存储的实现,这显着减少了内存需求。

编辑:根据此参考,自 Java 6 Update 21 以来,此选项似乎已进入正常的 Oracle JRE :

-XX:-XX:+UseCompressedStrings

对可以表示为纯 ASCII 的字符串使用 byte[]。(在 Java 6 Update 21 Performance Release 中引入)

(通过这个答案找到)。

于 2011-02-21T10:30:16.407 回答
0

一个好的内存分析器(我个人使用并且非常喜欢 yourkit java 分析器)应该能够向您显示内存的使用位置。

于 2011-02-21T12:18:29.723 回答