web-dev-qa-db-ja.com

メソッドを静的にすると、多くのインスタンスを持つクラスのメモリを節約できますか?

次の質問に対するアーロノートの回答に応じて:

すべての静的メソッドを使用することはできませんか?

静的メソッドに使用されるメモリは少なくありませんか?各オブジェクトインスタンスは、非静的メンバー関数の独自の実行可能バージョンを持ち歩いているように見えます。

静的なメソッドの呼び出しにどの程度のオーバーヘッドが伴うかに関係なく、貧弱なOO設計、および将来の頭痛の可能性に関係なく、使用量が少なくない実行時のメモリ?

次に例を示します。

ゼロで初期化されたオブジェクトのベクトルを作成します。各オブジェクトには、1つのデータ(9つのdoubleで構成される三角形)が含まれています。各オブジェクトは、.stlファイルから読み取られたデータから順に読み込まれます。必要な静的メソッドは1つだけです。適切なOO設計では、データを直接処理するメソッドを各オブジェクトに配布する必要があります。ここに標準的なOOソリューションがあります:

_foreach(obj in vec) {
  obj.readFromFile(fileName);
}
_

各objには、データとともにreadFromFileのコンパイル済みコードが含まれています。

この場合、パフォーマンスよりもメモリの方が重要であり、制約されたシステムには大量のデータがあります。

ソリューション:

  1. 名前空間メソッド(C++には最適ですが、Javaでは不可能)
  2. Objのクラスの1つの静的メソッド。実行可能コードは実行時に1か所に保持されます。メソッドを呼び出すための小さなオーバーヘッドがあります。
  3. Objの派生元の親クラス。プライベートメソッドreadFromFileが含まれます。 readFromFileを呼び出すsuper.callPrivateMethod()で呼び出します。乱雑ですが、各オブジェクトのメモリオーバーヘッドが多少あります。
  4. Objのスコープ外にreadFromFileを実装するため、vecのクラスまたは呼び出し元のクラスに実装します。私の意見では、これはデータのカプセル化を壊します。

大量のデータの場合、三角形ごとに1つの明示的なオブジェクトを使用するのは最善の方法ではないことに気づきました。これは単なる例です。

8
panlex

仮想メソッドを使用しても、メソッドはインスタンスごとに保存されません。それらは単一のメモリ位置に格納され、それらが呼び出されるときにthisポインタが渡されるため、それらはどのオブジェクトに属するかを「知る」だけです。

C++で必要な唯一の追加メモリは、仮想メソッドを使用している場合で、仮想メソッドテーブルを指すインスタンスごとに1つの追加のポインターが必要です(Javaもちろん、仮想メソッドを含むクラス、Objectなので、不可避です)。

説明したとおりに機能した場合、OO言語はあまり人気がありません!オブジェクトに必要なだけメソッドを追加しても構いません。メモリの使用量には影響しません。

19
vrostu

不十分なOO設計、および将来の頭痛の可能性に関係なく、静的メソッドの呼び出しにどのくらいのオーバーヘッドが関係しているかに関係なく、実行時に使用するメモリが少なくなりませんか?

これはかなり複雑ですが、「ほとんどない」と言います。

7
user204677

メソッドがクラスのインスタンスを参照しない場合、メソッドは「静的」である必要があります(多くの言語は「クラスメソッド」という用語を使用します)。クラスのインスタンスを参照する場合は、インスタンスメソッドである必要があります。

クラスメソッドとインスタンスメソッドを適切に使用する代わりに、メモリを節約できるという漠然とした可能性に基づいて設計を決定することは、非常に非常に悪い考えです。

2
gnasher729

原則として、静的メンバー関数であるかどうかにかかわらず、コードのコピーは1つだけです。

  • C++では、生成されたコードのアセンブラリストを確認することで、これを簡単に確認できます。
  • 私はJavaエキスパートではありませんが、JNI仕様から同じロジックであると推測できます。関数への単一の参照をフェッチして、さまざまなオブジェクトに対して何度も呼び出すことができます(引数としてのクラス参照、および静的メソッドでない場合はオブジェクト参照)。

したがって、静的関数または非静的関数を使用しても、メモリフットプリントは変わりません。

実際には、生成されたコードと関数の実行時にスタックで消費されたメモリの両方に非常に小さな違いがあります。

  • 非静的関数の呼び出しでは、関数/メソッドが適用されるオブジェクトへの参照/ポインターを渡す必要があります。これには通常、呼び出しシーケンスに追加のプッシュおよびポップ命令が必要です。
  • 静的関数の呼び出しには、このオーバーヘッドは必要ありません。

しかし、これは本当に無視できます。完全なコードと比較して、多かれ少なかれ1つまたは2つの機械語命令について話しています。つまり、心配する必要はまったくありません。

実行時のスタック消費量の差は、関数の実行時、ポインターのサイズに制限されます。再帰的に膨大な回数(数百万回)呼び出される関数を考えていない限り、これも無視できます。

結論として、私はgnasher729意見を完全に共有します:時期尚早な最適化はすべての悪の根です。関数がオブジェクトから独立している場合は、それを静的にし、それが唯一の基準である必要があります。

1
Christophe

メモリ使用量の最適化やコード行の削減などに集中するのは簡単ですが、他のプログラマーが使用/破壊するプログラムを維持する必要が生じた場合、他のプログラマーが機能を追加したり、バグを導入したりする可能性があります...元のプログラムではありません過去のテストでバグが発生している可能性があります。コードの可読性/保守性に重点を置くことがより重要である場合があります。一連のデータポイントを追跡するための高速ビット配列を作成することはできますが、そのビット配列を理解可能なアクセサーでオブジェクトにラップしない限り、そのコードに触れた次のプログラマーはおそらくそれを使用せず、置き換えるか、恐ろしい魔術を実行します。

0
Jamy Spencer