読んだあとこの古い記事String
いくつかのオブジェクト タイプのメモリ消費量を測定したところ、 Java でどれだけのメモリが使用されるかに驚きました。
length: 0, {class java.lang.String} size = 40 bytes
length: 7, {class java.lang.String} size = 56 bytes
この記事にはこれを最小限に抑えるヒントがいくつか記載されていますが、完全に満足できるものではありませんでした。char[]
データの保存に使用するには無駄が多いようです。ほとんどの西洋言語で明らかな改善策はbyte[]
、代わりに UTF-8 などのエンコードを使用することです。最も頻繁に使用される文字を保存するには、2 バイトではなく 1 バイトしか必要ありません。
もちろん、String.getBytes("UTF-8")
と を使用するnew String(bytes, "UTF-8")
こともできます。String インスタンス自体のオーバーヘッドもなくなります。ただし、その場合、、equals()
、などの非常に便利なメソッドが失われます...hashCode()
length()
太陽は特許byte[]
私の知る限り、文字列の表現について。
Javaプログラミング環境で文字列オブジェクトを効率的に表現するためのフレームワーク
... 適切な場合には、Java 文字列オブジェクトを 1 バイト文字の配列として作成する手法を実装できます...
しかし、その特許の API を見つけることができませんでした。
なぜ気にするのでしょうか?
ほとんどの場合、気にしません。しかし、私は大量の文字列を含む巨大なキャッシュを持つアプリケーションに取り組んでおり、メモリをより効率的に使用することでメリットが得られるはずです。
そのような API を知っている人はいますか? または、CPU パフォーマンスや見苦しい API を犠牲にしても、文字列のメモリ フットプリントを小さく保つ別の方法はありますか?
上記の記事の提案を繰り返さないでください。
- 独自のバリエーション
String.intern()
(おそらく を含むSoftReferences
) - 単一のデータを保存し
char[]
、現在のString.subString(.)
実装を利用してデータのコピーを回避する(厄介)
アップデート
この記事のコードを Sun の現在の JVM (1.6.0_10) で実行しました。2002 年と同じ結果が得られました。
ベストアンサー1
JVM のちょっとした助けを借りて...
警告:このソリューションは、新しい Java SE バージョンでは廃止されました。その他のアドホック ソリューションについては、以下を参照してください。
Java 6 アップデート 21 以降、HotSpot JVM を使用する場合は、次のコマンド ライン オプションを使用できます。
-XX:+UseCompressedStrings
のJVM オプションページには次のように書かれています:
純粋な ASCII として表現できる文字列には byte[] を使用します。(Java 6 Update 21 パフォーマンス リリースで導入)
アップデート: この機能は後のバージョンでは壊れており、Java SE 6u25で再度修正される予定でした。6u25 b03 リリースノート(しかし、6u25 最終リリースノート)。バグレポート 7016213セキュリティ上の理由から、表示されません。したがって、注意して使用し、最初に確認してください。他の-XX
オプションと同様に、これは実験的なものとみなされ、予告なしに変更される可能性があるため、実稼働サーバーの起動スクリプトでは使用しないことが常に最善とは限りません。
2013-03 更新 (コメントのおかげでアレクセイ・マキシマス): これを見て関連する質問そしてその受け入れられた答えこのオプションは廃止されたようです。これはバグでさらに確認されています7129417報告。
目的は手段を正当化する
警告:特定のニーズに対する(醜い)ソリューション
これは少し型破りで低レベルですが、あなたが尋ねたので...メッセンジャーを殴らないでください!
独自の軽量文字列表現
ASCII がニーズに合っているのであれば、独自の実装を展開してみてはいかがでしょうか?
あなたがおっしゃったように、内部でbyte[]
はなく、そうすることができますchar[]
。しかし、それだけではありません。
さらに軽量にするには、バイト配列をクラスでラップするのではなく、渡すバイト配列を操作する静的メソッドを主に含むヘルパークラスを使用するのはどうでしょうか。確かに、かなりCっぽい感じがしますが、うまく機能し、巨大なオブジェクトに付随するオーバーヘッドString
。
そして、確かに、再実装しない限り、いくつかの優れた機能が失われるでしょう。本当に必要な場合は、選択肢はあまりありません。OpenJDK やその他の優れたプロジェクトのおかげで、パラメータLiteStrings
のみを操作する独自の醜いクラスを展開することができますbyte[]
。関数を呼び出す必要があるたびにシャワーを浴びたくなるでしょうが、大量のメモリを節約できます。
クラスのコントラクトに非常に似たものにしてString
、 と を に変換するための意味のあるアダプタとビルダーを提供することをお勧めします。また、とString
間のアダプタや、必要になる可能性のある他のもののミラー実装も用意することをお勧めします。確かに多少の作業が必要ですが、それだけの価値があるかもしれません (「Make it Count!」セクションの少し下を参照してください)。StringBuffer
StringBuilder
オンザフライ圧縮/解凍
文字列をメモリ内で圧縮しておき、必要なときにその場で解凍することもできます。結局のところ、アクセスしたときに読み取れればよいだけですよね?
もちろん、それほど暴力的であるということは、次のことを意味します。
- より複雑な(したがって保守性が低い)コード
- 処理能力の向上、
- 圧縮を有効にするには、比較的長い文字列が必要です (または、独自のストア システムを実装して複数の文字列を 1 つに圧縮し、圧縮の効果を高めます)。
両方行う
頭痛がひどい場合は、もちろんこれらすべてを行うことができます。
- C風ヘルパークラス、
- バイト配列、
- オンザフライ圧縮ストア。
必ずオープンソースにしてください。:)
大切にしてください!
ちなみに、この素晴らしいプレゼンテーションをご覧くださいメモリ効率の高い Java アプリケーションの構築N. ミッチェルとG. セヴィツキー著: [2008年版]、[2009年版]。
このプレゼンテーションから、8文字の文字列は64バイトを消費します32ビットシステムでは96ビットです(64ビットシステムでは96ビットです!)そしてそのほとんどはJVMのオーバーヘッドによるものです。そしてこのことから記事私たちは、8バイト配列は「たった」24バイトしか消費しない: ヘッダー 12 バイト、アライメント 8 x 1 バイト + 4 バイト)。
本当にたくさんのものを操作するのであれば、これは価値があるように思えます (メモリの割り当てに費やす時間が減るので、おそらく少しはスピードアップしますが、それについては私の言葉を引用してベンチマークしないでください。また、実装によって大きく異なります)。