- PR -

String str = "ABC" + new String("EFG");

1
投稿者投稿内容
isseki
大ベテラン
会議室デビュー日: 2001/11/05
投稿数: 107
投稿日時: 2002-11-14 18:35
こんにちは
みさんにお伺いしたいんですが、

String str = "ABC" + new String("EFG");
のような文に対して、JVMはどのように動くのでしょうか。

新規のOBJを生成して、"ABC"と"EFG"両つのOBJの中身をコピーしてから
"ABC"と"EFG" OBJを捨てるのでしょうか。

どうぞよろしくお願いします。
ayum
常連さん
会議室デビュー日: 2002/03/28
投稿数: 44
お住まい・勤務地: 東京
投稿日時: 2002-11-14 19:17
ちょっと気になったのでJadというツールで上記のコードをバイトコードにしてみました。
おおまかには、まず文字列を結合するために新しいStringBufferが生成され、
次に文字列リテラルで囲まれている"ABC"がロードされ、
それがStringBufferにappendされる。
次に文字列リテラルで囲まれている"EFG"がロードされ、
それをコンストラクタに引数として渡す形で新しいStringオブジェクト("EFG")が生成される。
そしてその新しいStringオブジェクトをStringBufferにappendして、
StringBufferをtoStringしたものを変数strに代入、という流れになるようです。

つまり、まず入れ物としての一時オブジェクトを作成し、
そこに"ABC"と"EFG"の中身(正確にはフィールドに持っているchar[])をコピーし、
メソッドから抜けるとローカル変数である"ABC"と"EFG"は参照から外れる、と。
少なくともバイトコードレベルではそうなっているようです。
実際の実行時にJVMなんかがどう最適化するか(あるいはしないか)はわかりかねますが・・・。


String str = "ABC" + new String("EFG");
// 0 0:new #8 <Class StringBuffer>
// 1 3:dup
// 2 4:invokespecial #9 <Method void StringBuffer()>
// 3 7:ldc1 #10 <String "ABC">
// 4 9:invokevirtual #11 <Method StringBuffer StringBuffer.append(String)>
// 5 12:new #12 <Class String>
// 6 15:dup
// 7 16:ldc1 #13 <String "EFG">
// 8 18:invokespecial #14 <Method void String(String)>
// 9 21:invokevirtual #11 <Method StringBuffer StringBuffer.append(String)>
// 10 24:invokevirtual #15 <Method String StringBuffer.toString()>
// 11 27:astore_1
// 12 28:return
t-wata
大ベテラン
会議室デビュー日: 2002/07/12
投稿数: 209
お住まい・勤務地: 東京
投稿日時: 2002-11-14 19:21
要するに、コンパイル後は、

String str = (new StringBuffer().append("ABC").append(new String("EFG"))).toString();

と同じになる訳ですね。
未記入
ぬし
会議室デビュー日: 2002/03/28
投稿数: 255
投稿日時: 2002-11-14 19:33
>実際の実行時にJVMなんかがどう最適化するか(あるいはしないか)
>はわかりかねますが・・・。
そうですね.最適化はJavaVMの実装依存の部分なので,
どのJavaVMを使うかで変わってきます.ただ,

>String str = "ABC" + new String("EFG");

は冗長な処理であるため,素直に最初にソースレベルで次のように
「最適化」しておいた方が良いのでは.
String str = "ABCEFG";
#本当に早くなるかは微妙なところ.第一,この程度の処理では
#千回やそこら繰り返してもさしたる時間はかからないと思う.

ちなみに,すごーく長いテキストを書く場合に,次のような書き方を
しても,大抵はコンパイル段階で一つの文字列に繋げられますので,
実行時の速度にはまず影響しません.(コンパイラの実装依存なので,
かならずそうなるという保証があるわけではないが.)
String str = "A.....Z"+
"A.....Z"+
"A.....Z"+
"A.....Z"+
......
"A.....Z";

yu
ベテラン
会議室デビュー日: 2002/09/29
投稿数: 58
お住まい・勤務地: 東京
投稿日時: 2002-11-15 02:21
Java において、
String : 不変文字列
StringBuffer : 可変文字列
というのがポイントなんですよねぇ。

_________________
DaikiRyuto
大ベテラン
会議室デビュー日: 2002/07/23
投稿数: 200
投稿日時: 2002-11-15 11:38
引用:

String str = "ABC" + new String("EFG");
のような文に対して、JVMはどのように動くのでしょうか。

新規のOBJを生成して、"ABC"と"EFG"両つのOBJの中身をコピーしてから
"ABC"と"EFG" OBJを捨てるのでしょうか。



逆アセンブル結果も出ているので必要ありませんが、上記のコードをコンパイルした結果(すでにt-wataさんがかかれているようにコンパイル時に
String str = (new StringBuffer().append("ABC").append(new String("EFG"))).toString();
と同じに変換される)のVMの動作を言葉で簡単に解説すると、

1 StringBufferクラスの情報をロード
2 StringBufferクラスのインスタンスを作成
3 文字列プールから"ABC"をロード
4 StringBufferのappendメソッドをコール(引数:"ABC")
5 Stringクラスの情報をロード
6 文字列プールから"EFG"をロード
7 "EFG"を値に持つStringクラスのインスタンスを作成
8 StringBufferのappendメソッドをコール(引数:7で生成したインスタンス)
9 StringBufferのtoStringメソッドをコール

となります。

"ABC"に関してはロードするだけで、インスタンスの生成はしていません。
"EFG"に関してはインスタンスを生成しています。

ちなみに
String str = "ABC" + "EFG"
だと(ABCとEFGはコンパイル時に結合されるので
String str = "ABCEFG"
となる)

1 文字列プールからABCEFGをロード

だけですね。
(これはあくまでこの一文だけをコンパイルして実行させた場合の比較です)

悪夢を統べるものさんのおっしゃる通り実装時に最適化すべきですね。
(と言うかnew Stringは使うべきではないでしょう)



[ メッセージ編集済み 編集者: DaikiRyuto 編集日時 2002-11-15 12:05 ]
isseki
大ベテラン
会議室デビュー日: 2001/11/05
投稿数: 107
投稿日時: 2002-11-15 12:52
みなさん、ご教授有難うございます。
いい勉強になりました。

(jadで得られたByte-codeはまだ分かりませんが、面白そうですね。、これからです。)
1

スキルアップ/キャリアアップ(JOB@IT)