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


8

アーロノートの質問への回答に応じて:

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

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

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。プライベートメソッドが含まれます。コールとsuper.callPrivateMethod()その呼び出しreadFromFile。乱雑ですが、各オブジェクトのメモリオーバーヘッドが多少あります。
  4. readFromFileobjのスコープ外に実装するため、vecのクラスまたは呼び出し元のクラスに実装します。私の意見では、これはデータのカプセル化を壊します。

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


7
コードはインスタンスごとに重複していると思いますか?これは、Java、C#、C ++では発生しません。
joshp 2016年

2
まあ、それはその初期のいくつかの動的言語で、または乱用された反射の副作用として起こるかもしれません。しかし、一般的に、いいえ、バイトはそれが含むバイトよりもはるかに大きくなることはありません。
マシューマークミラー

1
このメモリを浪費する動作をどこで得ることができるかについて私が知っている唯一の言語はJavascriptですが、それでもプロトタイプOOで完全に失敗しなければなりません。JSで行うことになっているのは、プロトタイプオブジェクトに(静的および非静的の両方の)メソッドを置くことです。これにより、そのプロトタイプから継承するすべてのオブジェクトは、重複することなく1つの関数を共有します。
Ixrec

2
何千ものそのようなオブジェクトを作成する小さなプログラムを書き、測定を自分で行うことを検討したことがありますか?
ブライアンオークリー

1
オブジェクト指向に関連するすべての質問への答えではない:依存性注入?あなたはオーバーヘッドメモリについては気にしている場合、あなたはちょうどあなたが1つのインスタンスを作るそのうち「セーバー」オブジェクトを設計でき、それは....注入することができる
ピーター・B

回答:


19

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

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

あなたが説明した方法でうまくいったなら、オブジェクト指向言語はあまり人気がありません!オブジェクトに必要なだけメソッドを追加してください。メモリ使用量には影響しません。


各クラスには、仮想メソッドごとのオーバーヘッドもあります。また、Javaはデフォルトで仮想化されています...
Deduplicator

1
@Deduplicator:その通りですが、関数/メソッドを最初から用意することにはオーバーヘッドがあると主張することもできます。各関数はメモリを消費し、仮想関数はさらにわずかに(約4バイト)消費します。
Bart van Ingen Schenau

@BartvanIngenSchenauまたは8バイト、および各派生クラスについて。両方の見積もりはJavaのコストを非常に過小評価していますが
Deduplicator

3
@Deduplicatorそれでも、そのコストはインスタンスごとではなく、クラスごと(および派生クラスごと)です。
Craig

7

OOの設計が不十分であっても、静的メソッドの呼び出しにどのくらいのオーバーヘッドが関係していても、将来の頭痛の種であっても、実行時に使用するメモリが少なくないのではないでしょうか。

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


2

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

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


2
良いアドバイスですが、実際には質問には答えません。
JacquesB 2016年

1
これはXYの質問であるという強い仮定のもとに、そうなります。
gnasher729 2016年

1

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

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

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

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

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

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

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

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


this関数で使用されていない場合、C ++コンパイラはへの参照を渡さない可能性があると思います。JavaではおそらくJITがこれを行うことができます。
Oliv 2016年

0

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.