QA@IT
この質問・回答は、@ITの旧掲示板からインポートされたものです。

static修飾子は多用するべきか

同一クラスの全てのインスタンスで共有される『staticフィールド』や『staticメソッド』。
これについては、『メモリ領域を共有する』という説明がされています。

メモリ領域を共有するなら、各インスタンスで持つよりメモリの節約になりそうだし、
publicも併用すればインスタンスを作るまでもなく『クラス名.フィールド名』や『クラス名.メソッド』といった使い方もできるので、
私としては、スレッドセーフであるなら(もしくはセーフでなくても構わないなら)、staticはガンガン使うべきだ、というふうに思っています。

ですが、以前Javaに非常に詳しい人に話を聞いたところ、
『staticはダメだ。staticがあるからJavaがダメだという評価もあるほどだ』と言われて驚きました。
その時は理由を聞けなかったのですが、正直言って、staticがダメだという理由がわかりません。
staticがダメって、どういう理由によるのでしょうか?
そして、どういう場合ならstaticを使うべきで、どういう場合だと使わない方がよいのでしょうか?
ご存知の方、ご教授をお願いします。

質問者:ほまらら

回答

[quote]
かつのりさんの書き込み (2005-12-16 18:53) より:
[quote]
staticフィールドは速いよ。
[/quote]
JDK1.1の頃の話でしょうか。
テストしてみましたが、インスタンスのフィールドアクセスと
クラスのフィールドアクセスの速度に殆ど差がありません。(JDK5.0)

現在のJVMでは最適化が行われていますし、
小手先の技ではむしろ最適化の邪魔になるケースもあるそうです。

検証コード(汚いですが・・・)
[code]
public class Test {

public static String classField = "classField";

public String instanceField = "instanceField";

public static int cCount = 0;

public static int iCount = 0;

public static void test(int n) {
    String variable;
    Test test = new Test();
    variable = Test.classField;
    long l1,l2,l3;

    l1 = System.currentTimeMillis();
    for(int i = 0; i < 50000000; i++){
        variable = Test.classField;
    }
    l2 = System.currentTimeMillis();
    for(int i = 0; i < 50000000; i++){
        variable = test.instanceField;
    }
    l3 = System.currentTimeMillis();

    System.out.println(n + ":l2 - l1 = " + (l2 - l1));
    System.out.println(n + ":l3 - l2 = " + (l3 - l2));
    if((l2 - l1) > (l3 - l2)){
        cCount++;
    }else if((l2 - l1) < (l3 - l2)){
        iCount++;
    }
}

public static void main(String[] args) {
    for(int i = 0; i < 100; i++){
        test(i);
    }
    System.out.println("cCount=" + cCount + ":iCount=" + iCount);
}

}
[/code]
[/quote]

かつのりさんのをall-in-oneで動かしたら、インスタンスの方が断然速かったんですが、以下のように書き換えたら、staticの方がはるかに速かったんですが???

public class Test {

public static String classField = "classField";

public String instanceField = "instanceField";

public static int cCount = 0;

public static int iCount = 0;

public static void stM() {

}

public void insM() {

}

public void test(int n) {
    String variable;

// Test test = new Test();
variable = Test.classField;
long l1,l2,l3;

    l1 = System.currentTimeMillis();
    for(int i = 0; i < 50000000; i++){

// variable = Test.classField;
stM();
}
l2 = System.currentTimeMillis();
for(int i = 0; i < 50000000; i++){
// variable = test.instanceField;
insM();
}
l3 = System.currentTimeMillis();

// System.out.println(n + ":l2 - l1 = " + (l2 - l1));
// System.out.println(n + ":l3 - l2 = " + (l3 - l2));
System.out.println(n + ":sta = " + (l2 - l1));
System.out.println(n + ":ins = " + (l3 - l2));
if((l2 - l1) > (l3 - l2)){
cCount++;
}else if((l2 - l1) < (l3 - l2)){
iCount++;
}
}

public static void main(String[] args) {
    Test test = new Test();
    for(int i = 0; i < 100; i++){
        test.test(i);
    }
    System.out.println("cCount=" + cCount + ":iCount=" + iCount);
}

}


投稿者:MUGEN

編集 履歴 (0)

かなり古い書き込みに・・・

手元のSunのJRE1.6(32bit)ではあんまり差がないというか、
実行毎によって変わりますね。

どっちにせよ、ランタイムのJITの最適化によってどうにでも変わりますので、
あまりstaticやインスタンスだのを気にする必要はないかと思います。
結局はVMの実装次第です。

全てのケースにおいて高速になるチューニングは難しいので、
より多数派のコードで早くなるようなチューニングを行うことが多いです。
ですので、現時点で速い遅いを気にせずに、
普通にコードを書いた方が得ですよということです。

投稿者:かつのり

編集 履歴 (0)

初心者がやってしまいがちな、
・とりあえずコンパイルできるように static に
・なんでもかんでも static
は良くないと思います。

使用すべきか、すべきでないかはケースバーケースでしょう。
意味を理解した上で利用する分には問題ないし、不要なインスタンス変数を減らすという意味でも賛成です。
簡単なツールを作るとき全部 main() とそれから呼び出される static メソッド、static 変数からなるプログラムを書いてしまうのもアリかと。必要に応じてリファクタリングしていけば良いし。

また、スレッドセーフかどうか等の感覚がない初心者に対して「なるべく static は使うな」というサジェスチョンをするのは良いことかと思います。

投稿者:山本 裕介

編集 履歴 (0)

Polymorphismが無意味になるからでしょう。

staticはSingletonやユーティリティメソッド等に限定すべきだと思います。
定数などクラスの内部的な実装の目的なら使うべきだと思いますが。

インスタンスを無駄に作りまくるような場合はともかく、それ以外の場合で、
共有してメモリを多少節約できるくらいじゃメリットが少なすぎるかと。

自分的な基準では、staticしか考えられないのであれば利用し、
多少なりとも迷う場合は使わないようにしています。

投稿者:あしゅ

編集 履歴 (0)

staticにはきちんとした目的があるものであって、
使えばいい・ダメで判断されるものではないと思います。
また、便利か便利じゃないかでも判断できませんし、
必要があって利用されるものです。

ちなみに、staticならメモリの節約に、という観点は間違いです。
staticなフィールドにインスタンスを保持させると、
そのクラスがGCの対象になるまで
そのインスタンスもGCの対象とはなりません。



ですが、以前Javaに非常に詳しい人に話を聞いたところ、 
『staticはダメだ。staticがあるからJavaがダメだという評価もあるほどだ』と言われて驚きました。

Javaに詳しいか疑問を感じます。

投稿者:かつのり

編集 履歴 (0)

ほまららさんの書き込み (2005-12-16 11:13) より:

同一クラスの全てのインスタンスで共有される『staticフィールド』や『staticメソッド』。

これについては、『メモリ領域を共有する』という説明がされています。

メモリ領域を共有するなら、各インスタンスで持つよりメモリの節約になりそうだし、

publicも併用すればインスタンスを作るまでもなく『クラス名.フィールド名』や『クラス名.メソッド』といった使い方もできるので、

私としては、スレッドセーフであるなら(もしくはセーフでなくても構わないなら)、staticはガンガン使うべきだ、というふうに思っています。

まず、staticというのは「メモリの節約」や「インスタンスを作らずにメンバにアクセスする」ためのものではないので、そのような発想で使用するのはお門違いです。
というか、何でもかんでもstaticにしたら、かなり不自然なクラス設計になってしまうと思いますが……
私の場合、シングルトンやユーティリティクラスを除いて、クラスに定数やファクトリーメソッド以外のstaticなメンバ(あるいはstaticにするかどうか迷うメンバ)が多くなった場合、クラスの設計が適切かどうか見直すようにしてます。

ですが、以前Javaに非常に詳しい人に話を聞いたところ、

『staticはダメだ。staticがあるからJavaがダメだという評価もあるほどだ』と言われて驚きました。

いや、ダメなのはstaticじゃなくてその人だと思います。

投稿者:まいるどきゃっと

編集 履歴 (0)

インギさん、あしゅさん、かつのりさん、まいるどきゃっとさん
ご返答ありがとうございます。

どうやら、忌避が必要なほどデメリットがあるわけではないようですが、
使わずに済むなら使わずに済ませたほうがよいようですね。

私としては、例えば以下のような状況を想定していたのです。

定義するクラス:固定名のファイルの内容を取得して保持しておき、要求に応じて情報を提供するクラス
呼び出し元:同時進行が予想される複数のスレッド(スレッド数不定)

このような状況だと、スレッド毎にインスタンスを作成して同じファイルの内容を取得するのは不合理かと思い、staticを使うべきか考えたのです。
staticならファイルの読み込みも保持も1回で済みますので軽量・高速化が期待できる反面、
単一のstaticフィールドに複数スレッドから同時参照があるともしや却って重くなったりしないか、迷いました。

まだ少し迷うところもありますが、staticはスレッド間でなんらかの情報のやり取りが必要な場合を除いては使わない事にします。

staticなフィールドにインスタンスを保持させると、
そのクラスがGCの対象になるまで
そのインスタンスもGCの対象とはなりません。

なるほど、知りませんでしたが、理屈から言えば確かにそうなるはずですね。
(『クラス』がGCの対象になる、という事自体知りませんでした。)

投稿者:ほまらら

編集 履歴 (0)

ほまららさんの書き込み (2005-12-16 11:13) より:

ですが、以前Javaに非常に詳しい人に話を聞いたところ、

『staticはダメだ。staticがあるからJavaがダメだという評価もあるほどだ』と言われて驚きました。

私も、数十文字程度で大まかに書くと、この「Javaに非常に詳しい人」と同じような意見になります(ニュアンスが分からないのでどこまで同意見かは分かりませんが)。
static がなくても良いとまでは言いませんが、普通のアプリケーションを書く分には、static は不要です。もっとも
public static final int HOGE = 12345;
みたいなやつは構いません。あと、static なメソッドはあっても構いません。焦点は static なフィールドを持つかどうかです(メソッドが static になるかどうかはフィールドが static かどうかに依存するので)。
大抵の状態を管理するために static を使うのは混乱の元です。また、今インスタンスがひとつで済んでいたとしても将来複数になる可能性はほとんどのケースでつきまといます。逆に言えば main が static なのは、そういう可能性がないからです。
ただ、ケータイ Java などではメモリーをケチケチにケチるために static を使うとも聞いたことはありますが、それは便法だと考えたほうが良いでしょう。

投稿者:unibon

編集 履歴 (0)

ところでstaticなメソッドに多態性がないのは何故でしょうかね

これはどの言語にも無いと思うのだけど,確か昔ARMか何かで
それについての理由が書かれていて実装上の困難に引き合うほどの利点が
それほど無いというようなことが書かれていたように思うのです
(良く覚えていないので間違っているかも)原理的には仮想テーブルを
持つだけで出来そうに思えるのですが,実装上不可能なのでしょうか
あるいは別の理由で実装しないという明確な理由があるのでしょうか?

単にC++がそうだったからjavaもそうなったというわけでも無いでしょ

投稿者:Jun

編集 履歴 (0)

staticフィールドは速いよ。

投稿者:Anthyhime

編集 履歴 (0)

staticフィールドは速いよ。

それで思い出したのですが,staticに関してもうひとつ疑問があります
ローカルなスタティックを使うことで通常再入可能では無いが(javaにはfinalは
あっても定数は無いのでおそらく)高速なメソッドを作れますよね
必ずしもメソッドがスレッドセーフであることが要求されないことを考えると
ローカルなスタティック変数ってあってもよさそうだけど,実際無いのは
何故かなって思うんですが(もしかしてあるのかも知れないが確かエラーになったような
気がします)

投稿者:Jun

編集 履歴 (0)

CGが発生するとVM内で動的にアドレスが変わってしまうので、ローカル変数へのポインタを固定するのが難しいからではないでしょうかね。

投稿者:Anthyhime

編集 履歴 (0)

定義するクラス:固定名のファイルの内容を取得して保持しておき、要求に応じて情報を提供するクラス
呼び出し元:同時進行が予想される複数のスレッド(スレッド数不定)
まさに皆さんおっしゃっている、シングルトンで良いんじゃないんでしょうか。

投稿者:mio

編集 履歴 (0)

staticフィールドは速いよ。

JDK1.1の頃の話でしょうか。
テストしてみましたが、インスタンスのフィールドアクセスと
クラスのフィールドアクセスの速度に殆ど差がありません。(JDK5.0)

現在のJVMでは最適化が行われていますし、
小手先の技ではむしろ最適化の邪魔になるケースもあるそうです。

検証コード(汚いですが・・・)



public class Test {

    public static String classField = "classField";

    public String instanceField = "instanceField";

    public static int cCount = 0;

    public static int iCount = 0;

    public static void test(int n) {
        String variable;
        Test test = new Test();
        variable = Test.classField;
        long l1,l2,l3;

        l1 = System.currentTimeMillis();
        for(int i = 0; i < 50000000; i++){
            variable = Test.classField;
        }
        l2 = System.currentTimeMillis();
        for(int i = 0; i < 50000000; i++){
            variable = test.instanceField;
        }
        l3 = System.currentTimeMillis();

        System.out.println(n + ":l2 - l1 = " + (l2 - l1));
        System.out.println(n + ":l3 - l2 = " + (l3 - l2));
        if((l2 - l1) > (l3 - l2)){
            cCount++;
        }else if((l2 - l1) < (l3 - l2)){
            iCount++;
        }
    }

    public static void main(String[] args) {
        for(int i = 0; i < 100; i++){
            test(i);
        }
        System.out.println("cCount=" + cCount + ":iCount=" + iCount);
    }
}

投稿者:かつのり

編集 履歴 (0)

小手先の技ではむしろ最適化の邪魔になるケースもあるそうです。

そういえば、このまえ StringBuffer よりも String を使用したほうが速いという話をどこかで聞きました。従来、String よりも StringBuffer のほうが高速である、というのが定説でした。ところが、J2SE 5.0 の最適化では String は StringBuilder と同等のコードに置き換えられるため、同期不要な分だけ StringBuffer よりも速くなる可能性があるということでした。StringBuilder のインスタンス生成コストがあるので常に StringBuffer より速くなるわけではありませんが、速くなることもあると。

気にせずに String の連結をしまくってきた私は勝ち組ですね。

投稿者:未記入

編集 履歴 (0)

速度だのメモリの節約だの、仕様書に規定があるの?
実装の結果、ぶっちゃけ偶然の産物じゃないのかよ。

staticの多態性にしたって、
それを可能にすることによって、オブジェクト指向に
どんな貢献や混乱をもたらすか、全く触れられてない。
少なくとも、Jun氏はSingletonのなんたるかを理解してはいらっしゃらなさそうだ。
[ メッセージ編集済み 編集者: さいくろう 編集日時 2005-12-17 02:14 ]

投稿者:さいくろう

編集 履歴 (0)

staticは概念にもとづいて使うべきでしょ?
概念的に合っているなら使えばいいし、合ってないなら使っちゃだめ。

投稿者:ぶさいくろう

編集 履歴 (0)

そういえば、このまえ StringBuffer よりも String を使用したほうが速いという話をどこかで聞きました。従来、String よりも StringBuffer のほうが高速である、というのが定説でした。ところが、J2SE 5.0 の最適化では String は StringBuilder と同等のコードに置き換えられるため、同期不要な分だけ StringBuffer よりも速くなる可能性があるということでした。StringBuilder のインスタンス生成コストがあるので常に StringBuffer より速くなるわけではありませんが、速くなることもあると。

多分、コンパイラレベルでの最適化ですね。
JDK5.0以前からもJava言語仕様で規定されていますが、
JDK5.0からはStringBufferではなくStringBuilderに置き換えられるみたいですね。

これも聞いた話ですが、最近のSunやIBMのJVMは実行時の最適化に力を入れているらしく、
例えばバイトコード操作ライブラリで、有名なものではJavassistとASMがありますが、

・ASMは下手な最適化が行われており、実行時の最適化が行われにくい
・JavassistはJava言語仕様どおりにしかコンパイルしない。
 そのため、実行時の最適化の恩恵が受けられる

との事で、実行速度はJavassistの方が速いらしいです。
Seasar2のAOPも、上記理由からASMをやめてJavassistに変更したと、
作者のブログで読んだ覚えがあります。

投稿者:かつのり

編集 履歴 (0)

Junさんの書き込み (2005-12-16 17:40) より:

ローカルなスタティックを使うことで通常再入可能では無いが(javaにはfinalは

あっても定数は無いのでおそらく)高速なメソッドを作れますよね

必ずしもメソッドがスレッドセーフであることが要求されないことを考えると

ローカルなスタティック変数ってあってもよさそうだけど,実際無いのは

何故かなって思うんですが(もしかしてあるのかも知れないが確かエラーになったような

気がします)

メソッド内でローカルに使うオブジェクトは、
生成・破棄のコストを減らすため、
static変数に置いたインスタンスを使い回すようにできないか
…ということを言っていますか?

だとしたら、JVMの進化はそういう小手先の技を
必要としないところまで来ています。
http://www-06.ibm.com/jp/developerworks/java/051104/j_j-jtp09275.shtml

エスケープ分析といえば、こういう話もありますね。
http://pcweb.mycom.co.jp/news/2005/12/13/020.html
この結果、StringBufferの余計な同期化が排除されるようになれば、
StringBuilderとの性能差は無くなってくるのかもしれません。

投稿者:yamasa

編集 履歴 (0)
ウォッチ

この質問への回答やコメントをメールでお知らせします。