5

これは Sun JDK 1.6u21、x64 です。

単一の大きな文字列 (512k 文字) のみを含む perm gen の使用法を試すためのクラスがあります。

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

perm gen の使用状況を、パーマネント生成用getUsage().toString()MemoryPoolMXBeanオブジェクト (u21 では「PS Perm Gen」と呼ばれますが、バージョンやガベージ コレクターによって若干異なりますが) を使用して確認します。

クラスを最初に参照すると、たとえば を読んBig0.classで、 perm gen が最大 500 KB ジャンプします。これは、文字列の定数プール エンコーディングが UTF-8 であり、ASCII 文字のみを使用しているためです。

ただし、実際にこのクラスのインスタンスを作成すると、perm gen が約 2 MB ジャンプします。これはメモリ内の 1 MB の文字列 (UTF16 文字ごとに 2 バイト、確かにサロゲートなし) であるため、メモリ使用量が 2 倍になる理由について混乱しています。

文字列を静的にすると、同じ効果が発生します。final を使用した場合、65535 バイトの定数プール項目の制限を超えるため、コンパイルに失敗します (なぜ final をオフのままにしておくとそれが回避されるのかわかりません - それはおまけの質問と考えてください)。

どんな洞察も大歓迎です!

編集:これは、非静的、最終非静的、および静的文字列で発生しますが、最終静的文字列では発生しないことも指摘する必要があります。これはすでに文字列定数のベスト プラクティスであるため、おそらくこれは主に学術的な関心事です。

4

4 に答える 4

2

それはあなたのテストクラスの成果物だと思います。同様のクラスを作成し、javap で逆コンパイルしました。

[eclipse] Java コンパイラは、文字列リテラルをそれぞれが 64k を超えないチャンクに分割します。非定数フィールドを初期化するためのバイトコードは、一連の StringBuilder 操作とともにソース文字列をコブリングすることで構成されます。インターンされるのはこの最後の巨大な文字列ですが、それを構成する大きなアトムは定数プール内のスペースを占有します。

于 2011-02-23T15:19:55.793 に答える
0

Java文字の幅は1文字あたり2バイトです(ASCIIまたは255を超えるコードポイントに関係なく)。あなたが見ているのは、クラスが初期化されるとすぐに(インスタンスの作成前に行われる)文字列の内部クラスファイルストレージ(変更されたUTF8)バージョンを内部拡張形式に変換するJavaVMだと思います

于 2011-02-21T10:21:38.360 に答える
0

クラス ファイル形式では、リテラルの格納形式として修正 UTF-8 が指定されStringていますが、ランタイムの内部形式は UTF-16 です。AStringは、そのデータを UTF-16 エンコーディングのようにa に格納しますchar[](ただし、通常は実装に依存します)。ほとんどの文字は、このエンコーディングで 2 バイトを使用します (BMP 以外の文字はさらに多くを使用します)。

メモリ要件を大幅に削減する、ASCII のみの文字列用の特殊なコード パス/ストレージを備えrt.jarた実装を含む変更への参照を見てきました。java.lang.String

編集:このリファレンスによると、Java 6 Update 21 以降、このオプションは通常の Oracle JRE に組み込まれているようです。

-XX:-XX:+UseCompressedStrings

純粋な ASCII として表現できる文字列には byte[] を使用します。(Java 6 Update 21 パフォーマンスリリースで導入)

(この回答で見つかりました)。

于 2011-02-21T10:30:16.407 に答える
0

優れたメモリ プロファイラー (私は個人的に使用し、yourkit の Java プロファイラーが本当に好きです) は、メモリが使用されている場所を表示できるはずです。

于 2011-02-21T12:18:29.723 に答える