Javaに関する様々な情報をご紹介します。

Javaに関する様々な情報をご紹介します。
評価

0

Javaの(classの?)仕様について、メモリの疑問

---例1---

class Test{
    static final int A = 0
    final int a = Test.A;
    int getA(){
        return a;
    }
}



---例2---

class Test{
    static final int A = 0
    int getA(){
        return Test.A;
    }
}



例1が、一般的なコーディングだと認識しています。
しかしながら、例2の手法を取ればint型1つ分のメモリが浮くのではないかという疑問があります。
例1と例2の実際の違いを、メモリや処理速度などの観点からご教示ください。

7

回答

7500

閲覧

7件の回答

評価

0

その「一般的な」というのは、そもそもどこから出た認識な
んだ?
そういうコードは普通書かれないのだが。

評価

0

質問者です。

すいません、一般的な、ということに関しては完全にこちらの勝手な思い込みで言ってしまいました。
私は経験が浅いことを追記させていただきます。
特に、定数をそもそもどうやって組み込むべきなのかという常識が欠けているかもしれません。

折角ですので、一般的なコードも教えていただきたいのですが、そもそもどういった目的のコードなのかを端的に言語化することができません。

上記のコードが一般的でないことは置いていただき、このコードで実装したと仮定した場合に、
・例1でgetA()を実行する場合の速度と例2でgetA()を実行する速度の差
・例1と例2で実際にメモリの消費量が変わるのか
を教えていただくだけでも助かります。



以下は今回の質問が生まれた発端ですが、私の知識不足もあり、更に混乱させてしまう可能性があります。
私としては書くにこしたことはないと思い書きましたが、わけがわからないことをやっているなと思ったら無視してください。

ある識別子(下記ではn)を元にデータを取り出すという構造を作る際に、

static final int ID0 = 0;
static final int ID1 = 1;
static final int ID2 = 2;
static final int N0 = 0;
static final int N1 = 1;
static final int M0 = 2;
static final int M1 = 3;
static final int M2 = 4;
static final Test[] DATA = {new Test(N0, M0), new Test(N0, M1), new Test(N0, M2), new Test(N1, M2)};

public static void main(String[] args){
    int n = ID0;
    Test t = DATA[n];
}

class Test{
    final int v1, v2;
    Test(int v1, int v2){
        this.v1 = v1;
        this.v2 = v2;
    }
}

とするよりも、

static final int N0 = 0;
static final int N1 = 1;
static final int M0 = 2;
static final int M1 = 3;
static final int M2 = 4;

public static void main(String[] args){
    int n = Test0.ID;
    Test p;
    switch(n){
    case Test0.ID:
        p = new Test0();
    case Test1.ID:
        p = new Test1();
    case Test2.ID:
        p = new Test2();
    case Test3.ID:
        p = new Test3();
    }
}

abstract class Test{
    abstract getV1();
    abstract getV2();
}
class Test0 extends Test{
    static final int ID = 0;
    @Override
    int getV1(){
        return NO;
    }
    @Override
    int getV2(){
        return MO;
    }
}
class Test1 extends Test{
    static final int ID = 1;
    @Override
    int getV1(){
        return NO;
    }
    @Override
    int getV2(){
        return M1;
    }
}
class Test2 extends Test{
    static final int ID = 2;
    @Override
    int getV1(){
        return NO;
    }
    @Override
    int getV2(){
        return M2;
    }
}
class Test3 extends Test{
    static final int ID = 3;
    @Override
    int getV1(){
        return N1;
    }
    @Override
    int getV2(){
        return M2;
    }
}

とすれば、switchの分処理が重くなるが、メモリの消費を抑えられるのかという疑問がありました。

評価

0

まず、実行時間については自分で測ることができる。
問題の処理を100万回くらいループして呼び出し、その前後の
システム時間の差を見ればいい。

メモリの使用量は、確かに2つある方が全体としては(格納領
域も違うので単純な話ではない)多くなる。が、それは微々
たる差だ。
定数やフィールドはクラスの目的、設計に応じるものであ
り、僅かな効率の差で選ぶものではない。

コードは、あまり良く見ていないが、基本的な指針として毎
度同じ結果にしかならない判定は、その判定自体に重要性が
ない限り、避けた方がいい。
また、オブジェクト指向の目的は判定を減らすことにあると
も言える(本当は結果としてそうなるのだが、結果だけ見れ
ば)。

ただ、何が何でも条件分岐を隠蔽すべきなどと主張するなら
それも謝りだ。作るものに適した設計を曲げてまでやるの
は、まずもって害にしかならない。

評価

0

ご返事ありがとうございます。

実行時間に関しては、
例1で定数を変数に直接代入する速度、getA()で代入する速度
例2でフィールド変数を直接代入する速度、getA()で代入する速度
それぞれ1000万回の試行でgetA()を介するものが遅いという結果は出しております。

メモリはこの例のまま確認するのには、同じクラス内でint a, int b, int c...(結果が目に見えてわかるまで)と増やすのと、
例1のクラスをその変数の数と同数用意するのでは結果が変わる可能性があると思い試していませんでした。
このクラスがひとつしか作られないのであれば微々たる差なのですが、10万や100万の可能性があったのです。

私の知識では忘れている部分や想像もできていない部分に差がある可能性があり、
(具体的には格納領域の話がそうです、私は完全に失念していました)
仕様を知識として知っている人なら端的にこう変わると言っていただけるだろうということで質問させていただきました。
私の知識不足(常識不足)でそれ以前の話になってしまい申し訳ないのですが…

「基本的な指針として毎度同じ結果にしかならない判定は、その判定自体に重要性がない限り、避けた方がいい。」
という話は、言われてみればそうなのかもしれないと思わされる話でした、ありがとうございます。



もうしばらくは、より詳しい回答を募集させていただきます。
こちらを確認しなくなる時に、改めて回答の募集を締め切ると報告させていただきます。

評価

0

>10万や100万の可能性があったのです。
本当にそれだけの数を同時に確保しなければならないのか、
ここだけでは分からない。
それだけ同時に確保すると、動かない環境も多そうだ。

例えばjava.awt.Colorは自分でインスタンスを生成することも
できるが、有り触れた色は定数化して確保している。
これはまさに、二番目に書いたコードの1つめだろう。
Colorクラスのロード時に勝手に生成されるこれらを無駄と見
るか、有用と見るか。

最近、メモリの使用は益々複雑になっていて、システム単位
で予測はほとんど不可能になっている。
プリミティブなローカル変数はスタックに取られるので基本
的にはメモリを消費しないと見做すことができる。
インスタンス生成は一般的に重いとされるが、最近は単純な
クラスは一瞬だし、しかしSimpleDateFormatは桁違いに重
い。
また、最近の言語はコンパイル時、実行時に様々な最適化が
成され、効率良く書いたつもりのコードが必ずしも効率良く
実行される訳ではない。
机上での予測をしないで良い程ではないが、やはり実測に勝
るものはない。

なお、私の書いていることは演繹(仕様からの敷衍)ではな
く帰納(長年様々な言語で書いたことからの類推)なので、
言語仕様の専門家であれば否定する箇所もあるかも知れな
い。

もっとも、ここもかつてほど回答者が多くないので、他に回
答が得られるか、不透明だが…。

評価

0

回答ありがとうございます。


10万や100万必要なのかという話については、具体的には、
1、キャラクターが複数(10万~)いる
2、それぞれのキャラクターは自身のパラメータに則って動く、動いた結果は自身に保存される
3、必要になってから生成されるわけではなく、最初に全て生成される(最初から全て必要である)
というような話です。
10万~いるというのは例え方としてはおかしかったのかもしれないのですが、キャラクターが複数のパラメータを持った結果、10*10,000=100,000個分のint型というようなケースが想定されたためです。

今回の質問は、現状の想定なら何キャラクターまで作ることができるのかという作品の規模に関わっています。
動作環境は確定していませんが、恐らく動かないものも多くなる方向で検討されると思います。
これに関しては目的次第ですので、問題ありません。

可能であれば早く知りたいことや、類推による思い込みの発生、知識不足などの不安要素があったため、仕様を知っている方なら知識を述べるだけなのだから大きな手間もなく答えて頂けるだろうと思い質問させていただきました。
しかしながら、単純にこの質問だけが解決したとしても(そもそも前提となる情報不足で答えることができなさそうですが)、それに関連する新たな疑問の発生も予想されてきましたので、ここで回答を締め切らせて頂こうと思います。


基本的には、とりあえず試していくという方向性でいこうと思います。
ご丁寧にありがとうございました。

評価

0

大規模MMOか。
いかに同時アクセスとは言え、常時全ての情報がメモリに格
納されている訳ではない。
常時使用する情報に絞ってあとはDBに持ったり、変更されて
も影響がない情報はクライアントに持たせたり、サーバを並
列稼働させて領域毎に分散させたり、そう言ったことが重要
になって来る。
スタンドアロンとアーキテクチャレベルで全く違うことを考
えないといけないのが、リアルタイムMMOだ。

質問から6ヶ月以上経過しているので、回答を書き込むことはできません。