web-dev-qa-db-ja.com

メモリ使用量の分析:Java vs C ++ Negligible?

Javaで記述された整数オブジェクトのメモリ使用量は、C++で記述された整数オブジェクトのメモリ使用量とどのように比較されますか?違いは無視できますか?変わりはない?大きな違いは?言語に関係なくintはintであるため、同じだと思います(?)

私がこれを尋ねた理由は、プログラムのメモリ要件がプログラマーが与えられた問題を解決するのをいつ妨げるかを知ることの重要性について reading だったからです。

私を魅了したのは、単一のJavaオブジェクトを作成するために必要なメモリの量です。たとえば、整数オブジェクトを考えてみましょう。私が間違っているが、Java integerオブジェクトが24バイトのメモリを必要とする場合は、私を修正してください:

  • intインスタンス変数用に4バイト
  • 16バイトのオーバーヘッド(オブジェクトのクラス、ガベージコレクション情報、同期情報への参照)
  • 4バイトのパディング

別の例として、Java配列(オブジェクトとして実装されている)には48バイト以上が必要です。

  • 24バイトのヘッダー情報
  • 16バイトのオブジェクトオーバーヘッド
  • 長さは4バイト
  • パディング用に4バイト
  • さらに、値を格納するために必要なメモリ

これらのメモリ使用量は、C++で記述された同じコードとどのように比較されますか?

以前は自分が書いたC++およびJavaプログラムのメモリ使用量について気づかなかったが、アルゴリズムについて学び始めた今、コンピューターのリソースに対する理解が深まっている。

9
Anthony

それはプラットフォームと実装に依存します。

C++は、charのサイズが正確に1バイトで、少なくとも8ビット幅であることを保証します。その場合、_short int_のサイズは16ビット以上で、char以上になります。 intのサイズは、少なくとも_short int_のサイズと同じです。 _long int_のサイズは32ビット以上で、int以上です。

sizeof(char) == 1; sizeof(long int) >= sizeof(int) >= sizeof(short int) >= sizeof(bool) >= sizeof(char).

ただし、C++の実際のメモリモデルは非常にコンパクトですおよび予測可能。たとえば、オブジェクト、配列、またはポインタにはメタデータはありません。構造とクラスは配列と同じように隣接していますが、必要に応じてパディングを配置できます。

率直に言って、Javaメモリ使用量は、実行するコードよりもJava実装に依存するため、このような比較はせいぜい愚かです。

15
zxcdw

JavaとC++の両方で、これらすべてが実装定義されていることをご理解いただければ幸いです。とはいえ、Javaのオブジェクトモデルにはかなりのスペースが必要です。

C++オブジェクトは、(一般に)メンバーが必要とするものを除いてanyストレージを必要としません。 (ユーザー定義のすべてが参照型であるJavaとは異なり)、クライアントコードはオブジェクトを値型または参照型の両方として使用できます。つまり、オブジェクトは別のオブジェクトへのポインター/参照を格納するか、オブジェクトを直接格納できます。間接なし。 virtualメソッドがある場合は、オブジェクトごとに1つの追加のポインターが必要ですが、ポリモーフィズムなしでうまくいくように設計された便利なクラスが多数あり、これは必要ありません。 GCメタデータもオブジェクトごとのロックもありません。したがって、class IntWrapper { int x; public: IntWrapper(int); ... };オブジェクトはプレーンなintsよりも多くのスペースを必要とせず、コレクションや他のオブジェクトに直接(つまり、間接指定なしで)配置できます。

配列は、Java C++のJava $ ===配列に相当する事前作成された一般的なものがないため、簡単ではありません。new[]を使用して一連のオブジェクトを割り当てることができます(オーバーヘッドなし/メタデータ)しかし、長さフィールドはありません-実装はおそらくそれを格納しますが、それにアクセスすることはできません。std::vectorは動的配列であるため、追加のオーバーヘッドと大きなインターフェイスがあります。std::arrayとCスタイル配列(int arr[N];)にはコンパイル時定数が必要です。理論的には、オブジェクトのストレージと長さの単一の整数だけにする必要があります-動的なサイズ変更と完全な機能を備えたインターフェイスを少し余分なスペースがあれば、実際にはそれで十分です。これらすべて、および他のすべてのコレクションは、デフォルトでオブジェクトを値で格納するため、間接参照と参照用のスペースが節約され、キャッシュの動作が改善されることに注意してください。間接参照を取得するためのポインタ(スマートなものをお願いします)を保存します。

上記の比較は完全に公平ではありません。これらの節約の一部は、機能Java includeを含めないことで実現され、C++の同等機能はJava =同等(*)。C++でvirtualを実装する一般的な方法は、Javaでvirtualを実装する一般的な方法とまったく同じくらいのオーバーヘッドを課します。ロックを取得するには、完全な機能が必要ですmutexオブジェクト。これはおそらく数ビットよりも大きい可能性があります。参照カウントを取得するには、(notGCと同等であり、そのように使用しないでください。ただし、便利な場合もあります)、参照カウントフィールドを追加するスマートポインターが必要です。オブジェクトが注意深く構築されない限り、参照カウント、スマートポインターオブジェクト、および参照オブジェクトは完全に別の場所にあり、正しく構築した場合でも、共有ポインタは、(1つではなく)2つのポインタである必要があります(そうしなければなりません)。この場合も、優れたC++スタイルでは、これらの機能を十分に活用できません。ラリーのオブジェクトはより少なく使用します。これは必ずしも全体のメモリ使用量が少ないことを意味するわけではありませんが、C++がこの点で優れたスタートを切ることを意味します。

(*)たとえば、タイプ情報をさまざまなフラグとマージし、オブジェクトのロックビットを削除することで、仮想呼び出し、IDハッシュコード、および一部のオブジェクトの1ワードのみ(他の多くのオブジェクトの2ワード)でのロックを取得できます。ロックが必要になることはほとんどありません。 Java Object Model (PDF))の空間効率と時間効率の高い実装を参照してください。DavidF. Bacon、Stephen J. Fink、およびDavid Groveこの最適化とその他の最適化の詳細については、.

9
user7043

ほとんどの回答は、いくつかのかなり重要なポイントを無視しているようです。

まず、ひどいJavaの場合、生のintが実際に表示されることはありません。ほとんどすべての使用法がIntegerであるため、intは(約)CまたはC++のintと同じサイズはほぼ無関係ですが、代わりにintのみを使用するコードのパーセンテージ(私の経験では小さい)を除きます/ Integer

第二に、個々のオブジェクトのサイズは、プログラム全体のメモリフットプリントとはほとんど関係ありません。 Javaでは、プログラムのメモリフットプリントは、主にガベージコレクターの調整方法に関連しています。ほとんどの場合、GCは速度を最大化するように調整されます。これは、(大部分)GCをできるだけ頻繁に実行しないことを意味します。

現時点では便利なリンクはありませんが、JavaはCと同じ速度で実行できることを示すテストがいくつかありますが、そのためには、GCを実行する必要はほとんどありませんそれは、約7倍のメモリを使用するのに十分です。これは、個々のオブジェクトが7倍の大きさであるためではありませんが、頻繁に実行すると、GCが非常に高価になる可能性があります。さらに悪いことに、GCがメモリを解放できるのは、それがオブジェクトにアクセスする方法がなくなったのは、オブジェクトの使用が完了したことがわかったときだけではありません。つまり、GCをより頻繁に実行してメモリ使用量を最小限に抑えても、通常のプログラムを計画することができます。メモリーフットプリントが大きい場合。このような場合、係数を7ではなく2または3に減らす可能性があります。ただし、大幅にオーバーボードしても、2つが10または20以内にあるとは期待しないでください。割り当てたオブジェクトの数とサイズが同じであっても、同じメモリ使用量の割合1

状況に応じて、重要な場合と重要でない場合がある別の要因があります。それは、JVM自体が占有するメモリです。これは多かれ少なかれ修正されているため、アプリがそれ自体のストレージをあまり必要としない場合は割合が大きくなる可能性があり、アプリが多くを保存する必要がある場合は非常に小さい可能性があります。少なくとも私のマシンでは、最も些細なものでもJavaアプリは20-25メガバイトのようなものを占有しているようです(些細なプログラムでは1000xを超えるか、大きなプログラムではほとんど計り知れないほど小さい)。


1 C++で得られるフットプリントに近いフットプリントでJavaを書くことができなかった可能性があると言っているわけではありません。justオブジェクトの数とサイズが同じで、GCを実行しても、通常、そこに到達することはほとんどありません。

9
Jerry Coffin

Javaの単純なintは、C++のintとまったく同じ容量を使用します。ただし、両方の実装で同じ整数サイズとメモリアライメントを使用する必要があります。

Int 'object'(a boxed integer、つまり、インスタンスInteger)は、Javaのクラスインスタンスのすべてのオーバーヘッドを運ぶため、 C++ではint。ただし、C++のオブジェクトにJavaオブジェクトがそのまま使用できる(ポリモーフィズム、ボクシング、ガベージコレクション、RTTI)と同じ機能を備えている場合)、 dおそらく同じサイズのオブジェクトになります。

そして、最適化に関する考慮事項があります。実行モデルとプログラミングパラダイムが異なるため、重要な問題が両方の言語で同じように解決される可能性は低いため、このレベルでストレージサイズを比較しても、あまり意味がありません。

はい、JavaオブジェクトはデフォルトでC++クラスよりもオーバーヘッドが多くなりますが、それらにはより多くの機能があり、これは異なるプログラミングスタイルにつながります-優れたプログラマはどちらかの長所と短所を活用できます言語。

3
tdammers