2006-01-07 [長年日記]

_ [java.lang]StringBuffer vs StringBuilder[日記]

 スレッドの同期用処理があるかないかくらいの違いなんだけど、処理速度に案外差がでる。
 StringBufferを内部で多用する文字列処理系のライブラリではStringBuilder or 自作の非
 同期似非StringBufferに差し替えるだけで処理速度が改善される。
 String#+、String#concat、StringBuffer#append、StringBuilder#appendは呼出回数
 が多いほど、トータルの処理速度に差が出る。(参考記事:文字列連結のスピード(on off))
 複数スレッドから参照している場合のみStringBuffer、それ以外はStringBuilder。

 あと、文字列リテラルをメソッドに埋め込んでいるのをよく見かけるけど、Static変数にするのが定石。
 処理速度的にメリットはないけど、メンテナンス性や可読性が向上するので。
 メソッドに埋め込まれた文字列リテラルはメソッド呼出時に毎回オブジェクトとして生成されるのに対して、
 Static変数にした文字列リテラルはオブジェクトとして生成されない。(厳密にいうと、Static変数にした
 文字列リテラルは属するクラスがメモリにロードされるときに一度だけオブジェクトとして生成される。)
 文字列リテラルが埋め込まれたメソッドも文字列リテラルを埋め込まない場合との間に呼出回数が
 多いほど、差が出る。


 #正に「塵も積もれば、山になる」。処理速度の改善は小さなことからコツコツと。

 #javanさんのツッコミへの反応
 #Javaおぼえがき(on off)に拠れば
 #「String str = "文字列リテラル";」は「String str = new String("文字列リテラル").intern();」と
 #同じ処理。メソッドに組み込まれたリテラル文時列は一度"オブジェクトとして生成"された後、(String#intern
 #を介して?)VMの持つ文字列リテラルプールにアクセスし、メソッドに組み込まれた
 #リテラル文時列と等価な文字列が文字列リテラルプール内にあればその参照を返し、なければ
 #自分自身を文字列リテラルプールに登録し、その参照を返すことになります。

 #Javaコンパイラが優秀でバイトコード生成時に"メソッドに組み込んだ文字列リテラル"をStatic変数とほぼ同じ
 #扱いとなるように変換するのか...。
 #誤認識を訂正できたので、javanさんのツッコミに感謝。

 #->(on off)

_ [java.build]Maven Repository Manager(on off)

 メモメモφ。/* from wildcatsの日記 */

_ [java.fw.***]アスペクトによるバイトコード自動生成

 あぼ〜ん

_ [java.fw]Message redelivery and DLQ tips for ActiveMQ 3.x(on off)

 メモメモφ。/* from wildcatsの日記 */

_ [svn]Subversion 1.3.0(on off)

 でてます。/* to MYCOM(on off) */

_ [php]XOOPSCube 2.1 開発者向けプレビュー版(12/30スナップショット)(on off)(on off)

 XOOPS次世代版。1/8〜1/15にエンドユーザ向けアルファ版がでる模様。

_ [web.design]CSSテンプレートいろいろ(on off)

 メモメモφ。

_ [web.design]Open Source Web Design(on off)

 メモメモφ。

_ [system]不良システムを作らないプロジェクトの枠組み(on off)

 メモメモφ。/* from wildcatsの日記 */

_ [moz]Firefox のタブの中で IE を起動する IE Tab(on off)

 メモメモφ。

_ [elec.hard]SatuGo:投撮用ボール型デジカメ(on off)

 メモメモφ。

本日のツッコミ(全3件) [ツッコミを入れる]
# javan (2006-01-07 16:59)

public class LocalStr {
void print() {
System.out.println("Local String");
}
のバイトコード(javap -cで出力)は
void print();
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3; //String Local String
5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
となり、
static final String FINAL = "FinalStr";
public static void main(String[] args) {
System.out.println(FINAL);
}
のバイトコード(javap -cで出力)は
public static void main(java.lang.String[]);
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3; //String FinalStr
5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
で、どちらも#3としてあらかじめ割り当て済みの文字列オブジェクトをロードする同一のバイトコードに見えるのですが、メソッドに埋め込んでいる場合には同一バイトコードから「メソッドに埋め込まれた文字リテラルはメソッド呼出時に毎回オブジェクトとして生成」という複雑な処理が実行されるのでしょうか? まったくの勘違いか都市伝説の再生に見えるのですが、論理的な背景をご教示いただけないでしょうか。
なお、バイトコードの生成に用いたjavacはSunのJ2SDK 1.5の
> javac -version
javac 1.5.0_05
です。

# javan (2006-01-07 19:35)

それは間違いですね。元の文書を見ましたが「という作業を行なっているのと等価ということです。」となっています。
では、問題は、いつinternを呼んでいるかですが、それはクラスのロード時です。当然ですね。同一のバイトコードであるメソッドの呼び出しの都度、生成することは相当めんどうであり、かつ意味がありません(そもそも、文字列そのものをどのようにクラスファイルへ保持しているかご存知ですか?)。
でたらめな解釈でも、Googleは等しく検索対象にしてしまいます。
この誤った記述を鵜呑みにして(On Off)とかで参照する人が出てくるかも知れません。
まず、言語仕様、実際の動作、そういった一時情報に当たって確認するか、あるいは「等価」という言葉の示す意味を理解するようにして頂ければと思います。

# javan (2006-01-07 19:42)

わかりにくかったかも知れません。
static final Stringの場合は、
static final String str = new String("文字列リテラル").intern();
と等価になります。
また、直定数の場合は
static final String #コンパイラ生成シンボル = new String("文字列リテラル").intern();
ですね。
早い話が、JVMの扱いはまったく「等価」です。
表現を変えると直定数のStringはstatic final Stringと「等価」です。