メモリ使用量の分析:JavaとC ++は無視できるか?


9

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

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

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

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

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

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

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

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


6
C ++の「整数オブジェクト」とは何ですか?int?その場合は、C ++のintが32ビットである限りint、Java と比較する必要がありIntegerます。
マット

+1 int変数が1つだけのc ++クラスを作成した場合、それをインスタンス化しました
Anthony

3
intはintではありません-プラットフォームに依存します

1
intメンバーが1つだけのC ++には、通常オーバーヘッドがありません。プラットフォームがint(通常、現在のPCプラットフォームでは4バイト)を格納するために使用するスペースとまったく同じスペースを使用します。
Dirk Holsopple 2012

メモリに興味があるJavaプログラマーの+1。何よりも、メモリ認識は、最新のアーキテクチャのパフォーマンスを決定する最も重要な要素です。
イマレット2014

回答:


15

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

C ++は、のサイズcharが正確に1バイトで、少なくとも8ビット幅であることを保証します。そして、aのサイズはshort int少なくとも16ビットであり、より小さくはありませんchar。のサイズはint少なくとものサイズと同じですshort int。のサイズはlong int32ビット以上で、int以上です。

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

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

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


1
確かに、しかし比較のために、サイズが等しい整数型を想定するべきだと思います(たとえ、それらが異なる名前でしか利用できない場合でも)。結局のところ、異なるサイズは異なるセマンティクスを意味し、多くの(すべてではない)一般的なプラットフォームでは、サイズが同一であるか、同じサイズの整数が異なる名前で使用できます。

OPへの注意:整数のサイズを自分で選択した方がよい場合があります。C++で32ビットのintが必要な場合は、を使用できますint32_t
K.Steff

9

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

まず、ひどい多くのJavaでは、実質的にrawが表示されることはありませんint-ほとんどすべての用途はIntegerなので、CまたはC ++のと(ほぼint)同じサイズである可能性があるという事実は、それ以外intほとんど関係ありません(私の経験では、小さい)のint代わりにのみ使用するコードの割合Integer

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

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

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


1それは、C ++で得られるものに近いフットプリントでJavaをなんとか書くことができなかったと言っているのではありません。オブジェクトの数/サイズが同じで、GCを頻繁に実行しても、通常はそこにアクセスできないと言っているだけです。


7
あなたの最初のポイントについて:私はJavaの男だありませんが、Java APIを私は使ったことがない見てきましたInteger(理由は、彼らのでしょうか?)の代わりにintInteger型の消去のためにジェネリックコレクションのみを使用する以外に選択肢はありませんが、気にしたい場合は、それらを特別intな実装または必要なプリミティブ型に置き換えることができます。そして、一般的なラッピングコード(必要と例えばすべてを通過するための一時的なボクシングがありますObject[])。それとは別に、GCスペースのオーバーヘッドのソースはありますか?本当に疑いはありませんが、興味があるだけです。


9

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

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

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

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

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


3

intJava のplainは、intC ++のとまったく同じスペースを取りますが、両方の実装で同じ整数サイズとメモリ配置を使用している必要があります。

int 'オブジェクト'(ボックス化された整数、つまりIntegerclassのインスタンス)は、Javaのクラスインスタンスのすべてのオーバーヘッドを運ぶためint、C ++のin よりも大幅に大きくなります。ただし、Javaオブジェクトに標準で付属しているのと同じ機能(ポリモーフィズム、ボクシング、ガベージコレクション、RTTI)をC ++のオブジェクトに装備する場合は、おそらく同等のオブジェクトになります。サイズ。

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

はい、JavaオブジェクトはデフォルトでC ++クラスよりもオーバーヘッドが多くなりますが、より多くの機能が備わっているため、プログラミングスタイルが異なります。優れたプログラマーは、どちらの言語の利点と欠点も活用できます。


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