web-dev-qa-db-ja.com

Javaでのオブジェクト作成を避けるべきですか?

ある同僚から、Javaでは、オブジェクトの作成が実行できる最も負荷の高い操作であると言われました。そのため、できる限り少数のオブジェクトを作成することしかできません。

これは、オブジェクト指向プログラミングの目的をいくらか無効にするようです。オブジェクトを作成しない場合は、最適化のために1つの長いクラスCスタイルを作成しているだけですか?

253
Slamice

あなたの同僚は彼らが何について話しているのか全く分かりません。

あなたの最も高価な操作はそれらを聞くことです。彼らは、10年以上古い情報にあなたを誤って誘導するあなたの時間を無駄にしました(この回答が投稿された元の日付で)そしてここに投稿するのに時間を費やす必要がありますそして真実のためにインターネットを研究します。

うまくいけば、彼らは10年以上前に聞いたり読んだりしたものを無知に逆流しているだけで、それ以上のことは知りません。私は彼らが疑いがあると彼らが言う他の何でもとります、これはどちらかの方法で最新に保つ誰でもよく知られている誤りであるべきです。

すべてがオブジェクトです(primitivesを除く)

プリミティブ(int, long, doubleなど)はJavaのオブジェクトです。 Javaでのオブジェクトの作成を回避する方法はありません。

Javaでのオブジェクト作成は、そのメモリ割り当て戦略により、ほとんどの場合C++よりも高速であり、JVMの他のすべてと比較して、すべての実用的な目的で考えることができます "無料"

1990年代の終わりから2000年代の初めにかけてのJVM実装では、オブジェクトの実際の割り当てにパフォーマンスのオーバーヘッドがありました。これは少なくとも2005年以降そうではありません。

チューニングした場合-Xmsアプリケーションを正しく実行するために必要なすべてのメモリをサポートするために、GCを実行して最新のGC実装の​​ほとんどのゴミを一掃する必要がなく、存続期間の短いプログラムがGCを実行することはありません。

とにかく赤いニシンである空き領域を最大化しようとせず、ランタイムのパフォーマンスを最大化します。それがJVMヒープが常にほぼ100%割り当てられていることを意味する場合は、そうです。空きJVMヒープメモリでは、そこに座っているだけでは何も得られません。

GCがメモリを解放してシステムの残りの部分に有効な方法で戻すという誤解があります。これは完全に誤りです!

JVMヒープは拡大および縮小しないため、システムの残りの部分は空きメモリの影響を受けますJVMヒープ内-Xmsは、起動時に指定されたものすべてを割り当てます。そのヒューリスティックは、JVMのインスタンスが完全に終了するまで、メモリを解放して他のOSプロセスと共有することは決してありません。 -Xms=1GB -Xmx=1GBは、任意の時点で実際に作成されるオブジェクトの数に関係なく、1 GBのRAMを割り当てます。ヒープメモリのパーセンテージを解放できるようにするいくつかの設定があります すべての実用的な目的これが発生するために、JVMはこのメモリを十分に解放できません 他のプロセスはこのメモリを再利用できないため、システムの残りの部分はJVMヒープも解放されています。このためのRFEは "accepted" 29-NOV-2006でしたが、これについては何も行われていません。これは、動作が問題とはみなされていません権威の。

生存期間の短い小さなオブジェクトを多数作成するとJVMが長時間停止するという誤解がありますが、これも誤りです

現在のGCアルゴリズムは実際にはoptimizedであり、寿命の短い多くの小さなオブジェクトを作成するためのものです。つまり、基本的にJavaすべてのプログラムのオブジェクトの99%のヒューリスティックです。 Object Pooling は、ほとんどの場合、実際にはJVMのパフォーマンスを低下させます。

今日プールが必要な唯一のオブジェクトは、JVMに対してexternalである有限リソースを参照するオブジェクトです。ソケット、ファイル、データベース接続など、再利用できます。通常のオブジェクトは、メモリの場所に直接アクセスできる言語と同じ意味で pooled にすることはできません。オブジェクト caching は別の概念であり、一部の人々が単純に pooling と呼ぶ場合とそうでない場合があります。この2つの概念は同じものではなく、混同しないでください。

最新のGCアルゴリズムは、スケジュールに従って割り当てを解除しないため、この問題はありません。特定の世代で空きメモリが必要になったときに割り当て解除されます。ヒープが十分に大きい場合、一時停止を引き起こすのに十分な時間、割り当て解除は発生しません。

オブジェクト指向動的言語は、今日でもコンピューティングセンシティブテストでCに勝っています。

484
user7519

結論:オブジェクトを作成するショートカットを取るために、デザインを妥協しないでください。不必要にオブジェクトを作成しないでください。賢明な場合は、(あらゆる種類の)冗長な操作を回避するように設計してください。

ほとんどの回答に反して-はい、オブジェクトの割り当てにはコストが伴います。低コストですが、不要なオブジェクトを作成しないでください。あなたのコードで不必要な何かを避けるべきであるのと同じです。オブジェクトグラフが大きいと、GCが遅くなり、実行時間が長くなることを意味します。つまり、メソッド呼び出しが増え、CPUキャッシュミスが多くなり、プロセスがディスクにスワップされる可能性が低くなりますRAM =ケース。

これがEdgeケースであることを誰もが認める前に-最適化する前に、50行以上のデータを処理するために20 + MBのオブジェクトを作成したアプリケーションのプロファイルを作成しました。これはテストでは問題ありません。1分あたり100リクエストにスケールアップし、突然、1分あたり2GBのデータを作成するまでです。 20リクエスト/秒を実行する場合は、400MBのオブジェクトを作成し、それを破棄します。まともなサーバーの場合、20リクエスト/秒はごくわずかです。

97
jasonk

実際には、Java言語(または他の管理された言語))が可能にするメモリ管理戦略により、オブジェクトの作成は、若い世代と呼ばれるメモリのブロックでポインタをインクリメントするだけです。これは、空きメモリの検索を実行する必要があるCよりもはるかに高速です。

コストの他の部分はオブジェクトの破壊ですが、Cと比較することは困難です。コレクションのコストは長期的に保存されたオブジェクトの量に基づいていますが、コレクションの頻度は作成されたオブジェクトの量に基づいています...最後に、Cスタイルのメモリ管理よりもはるかに高速です。

60
amara

他のポスターは、オブジェクトの作成はJavaでは非常に高速であり、通常の通常のJavaアプリケーションでは心配する必要がないことを指摘しました。

isがオブジェクトの作成を回避するための良いアイデアである非常に特別な状況がいくつかあります。

  • レイテンシの影響を受けやすいアプリケーションを作成していて、GCの一時停止を避けたい場合。作成するオブジェクトが多いほど、GCが発生し、一時停止の可能性が高くなります。これは、ゲーム、一部のメディアアプリケーション、ロボット制御、高頻度取引などに関連する妥当な考慮事項となる場合があります。解決策は、必要なすべてのオブジェクト/配列を事前に割り当て、それらを再利用することです。この種の機能を提供することに特化したライブラリがあります(例: Javolution )。しかし、間違いなく、低レイテンシに本当に関心がある場合は、JavaまたはC#:-)ではなくC/C++/assemblerを使用する必要があります。
  • ボックス化されたプリミティブ(Double、Integerなど)を回避することは、特定の状況で非常に有益なマイクロ最適化になります。ボックス化されていないプリミティブ(double、intなど)は、オブジェクトごとのオーバーヘッドを回避するため、数値処理などのCPU集中型の処理がはるかに高速です。通常、プリミティブ配列はJavaこれらを他の種類のオブジェクトではなく、数値処理に使用します。
  • 制約されたメモリ状況では、各オブジェクトが小さなオーバーヘッド(通常、JVMに応じて8-16バイト)を運ぶため、作成される(アクティブ)オブジェクトの数を最小限に抑えたい実装)。このような状況では、多数の小さなオブジェクトではなく、少数の大きなオブジェクトまたは配列を使用してデータを格納する必要があります。
38
mikera

あなたの同僚が言っていることに真実の核があります。オブジェクトcreationの問題は実際にはガベージcollectionであると私は丁重にお勧めします。 C++では、プログラマーはメモリの割り当て解除方法を正確に制御できます。プログラムは、好きなだけ長く、または短く、クラッドを蓄積できます。さらに、C++プログラムは、それを作成したスレッドとは異なるスレッドを使用してクラッドを破棄できます。したがって、現在動作しているスレッドがクリーンアップのために停止する必要はありません。

対照的に、Java仮想マシン(JVM)は定期的にコードを停止して未使用のメモリを解放します。ほとんどのJava開発者はこの一時停止に気付くことはありません。非常に短いです。蓄積するクラッドが多いほど、またはJVMの制約が大きいほど、これらの一時停止の頻度が高くなります。このプロセスを視覚化するには、 VisualVM などのツールを使用できます。

Javaの最近のバージョンでは、ガベージコレクション(GC)アルゴリズム 調整可能 。原則として、一時停止を短くしたいほど、仮想マシンのオーバーヘッドが高くなります(つまり、CPUとメモリがGCプロセスの調整に費やします)。

これはいつ問題になるのでしょうか?一貫したミリ秒未満の応答率を気にするときはいつでも、GCを気にするでしょう。 Javaで記述された自動取引システムは、休止を最小限に抑えるためにJVMを大幅に調整します。そうでなければJavaと記述する企業は、システムの応答性を高める必要がある状況でC++を使用しますいつも。

記録のために、私は一般にしないオブジェクト回避を容認します!デフォルトはオブジェクト指向プログラミングです。このアプローチを調整するのは、GCが邪魔になる場合のみで、JVMを調整して一時停止する時間を短くした後でのみ調整してください。 Javaパフォーマンスチューニングは Java Performance についての良い本です。CharlieHuntとBinu Johnによるものです。

17
greg

Javaオーバーヘッドが原因で =Androidプラットフォーム

それ以外は、上記の答えが当てはまります。

11
Peter Kelly

gCは多くの短命のオブジェクト用に調整されています

オブジェクトの割り当てを簡単に減らすことができる場合は、

1つの例は、ループ内で文字列を作成することです。素朴な方法は次のようになります。

String str = "";
while(someCondition){
    //...
    str+= appendingString;
}

+=操作ごとに新しいStringオブジェクトを作成します(さらにStringBuilderと新しい基になるchar配列)

これは次のように簡単に書き換えることができます。

StringBuilder strB = new StringBuilder();
while(someCondition){
    //...
    strB.append(appendingString);
}
String str = strB.toString();

このパターン(不変の結果とローカルの変更可能な中間値)は、他のものにも適用できます

それ以外は、幽霊を追いかける代わりに、プロファイラーを引き上げて実際のボトルネックを見つける必要があります。

9
ratchet freak

Joshua Bloch (Javaプラットフォーム作成者の1人)が彼の本に書きました有効なJava 2001:

独自のオブジェクトプールを維持してオブジェクトの作成を回避することは、プール内のオブジェクトが非常に重いものでない限り、お勧めできません。オブジェクトプールを正当化するオブジェクトの典型的な例は、データベース接続です。接続を確立するコストは十分に高いため、これらのオブジェクトを再利用することは理にかなっています。ただし、一般的に言えば、独自のオブジェクトプールを維持すると、コードが乱雑になり、メモリフットプリントが増加し、パフォーマンスが低下します。最新のJVM実装には、軽量オブジェクトでそのようなオブジェクトプールを簡単に上回る高度に最適化されたガベージコレクタがあります。

6
Anthony Ananich

これは実際には特定のアプリケーションに依存するため、一般的に言うのは本当に難しいです。ただし、オブジェクトの作成が実際にアプリケーションのパフォーマンスのボトルネックであるとしたら、私はかなり驚きます。それらが遅い場合でも、コードスタイルの利点はおそらくパフォーマンスを上回ります(実際にユーザーが気付かない限り)。

いずれにせよ、コードをプロファイリングして、推測する代わりに実際のパフォーマンスのボトルネックを特定するまで、これらのことについて心配する必要はありません。それまでは、パフォーマンスではなく、コードを読みやすくするために最善を尽くしてください。

5
Oleksi

あなたの同僚は不必要なオブジェクト作成の観点から言ったに違いないと思います。つまり、同じオブジェクトを頻繁に作成する場合は、そのオブジェクトを共有することをお勧めします。オブジェクトの作成が複雑でより多くのメモリを必要とする場合でも、そのオブジェクトのクローンを作成し、その複雑なオブジェクト作成プロセスを作成しないようにすることができます(ただし、要件によって異なります)。 「オブジェクトの作成にはコストがかかる」という表現は、文脈の中でとるべきだと思います。

JVMメモリー要件に関する限り、Java 8になるまで待ちます。-Xmxを指定する必要すらありません。メタスペース設定がJVMメモリーのニーズを処理し、自動的に増加します。

3
AKS

JavaのGCは、多くのオブジェクトを「バースト」方式で迅速に作成するという点で非常に最適化されています。私が理解できることから、彼らはシーケンシャルアロケーター(可変サイズリクエストの場合は最速かつ最も単純なO(1)アロケーター)を使用して、そのような「バーストサイクル」で「Edenスペース」であり、GCサイクル後にオブジェクトが存続する場合にのみ、オブジェクトは、GCが1つずつ収集できる場所に移動されます。

そうは言っても、パフォーマンスのニーズが十分に重要になると(実際のユーザーエンドの要件で測定されるように)、オブジェクトはオーバーヘッドを伴いますが、作成/割り当てに関してはそれほど考えません。参照の局所性とJava内のすべてのオブジェクトの追加サイズに関連して、リフレクションや動的ディスパッチなどの概念をサポートするために必要に応じて(Floatfloatより大きく、多くの場合4倍ほど大きい)アライメント要件を備えた64ビット上で、Floatの配列は、私が理解しているものから連続して格納されるとは限りません)。

Javaで開発した今まで見た中で最も目を引くものの1つは、自分の分野(VFX)でヘビー級の候補者であると考えるようになりました(インタラクティブなマルチスレッド標準パストレーサー(放射照度キャッシュを使用しない)またはBDPTまたはMLSなど)CPUでリアルタイムプレビューを提供し、ノイズのない画像にすばやく収束しました。C++の専門家と協力して、手の込んだプロファイラーでそのようなことに専念してきましたそれ。

しかし、私はソースコードをじっくりと見回り、わずかなコストで多くのオブジェクトを使用しましたが、パストレーサーの最も重要な部分(BVHと三角形とマテリアル)は、プリミティブ型の大きな配列を優先してオブジェクトを非常に明確かつ意図的に避けました(ほとんどfloat[]およびint[])、これにより、使用するメモリが大幅に減り、配列内のあるfloatから次の配列に到達するための空間局所性が保証されました。私がそこで箱入りの型を使用した場合、Floatが好きだとすると、パフォーマンスにかなりのコストがかかると考えるのは、あまり推論ではないと思います。しかし、私たちはそのエンジンの最も絶対的に重要な部分について話しています。開発者がどれほど巧みにそれを最適化したかを考えると、彼はそれを測定し、非常に慎重に最適化を適用しました。彼の印象的なリアルタイムパストレーサーのわずかなコスト。

ある同僚から、Javaではオブジェクトの作成が実行できる最も負荷の高い操作であると言われました。したがって、できる限り少ないオブジェクトを作成するように結論付けることができます。

私のようにパフォーマンスが重要な分野であっても、重要ではない場所で自分自身をこじつければ、効率的な製品を書くことはできません。私は、最も重要なフィールドが生産性に対してさらに高い需要をもたらす可能性があると主張することさえします。なぜなら、そうでないものにそのような時間を無駄にしないことで本当に重要なホットスポットを調整するために得ることができるすべての追加の時間が必要だからです。 。上記のパストレーサーの例と同様に、筆者は、このような最適化を巧妙かつ慎重に、測定後に本当に重要で、おそらく後から見える場所にのみ適用し、それでも他の場所でオブジェクトを楽しく使用しました。

1
Dragon Energy

クラスの作成には、メモリの割り当て以外にもあります。初期化もあり、その部分がすべての回答でカバーされていない理由がわかりません。通常のクラスはいくつかの変数を含み、何らかの形の初期化を行いますが、それは無料ではありません。クラスが何であるかに応じて、ファイルを読み取るか、他の数の遅い操作を実行します。

だから、それが自由かどうかを判断する前に、クラスコンストラクタが何をするかを考えてください。

1
Alex

私はこれに関して microbenchmark をすばやく行い、githubに full sources を提供しました。私の結論は、オブジェクトの作成にコストがかかるかどうかは問題ではありませんが、GCが処理するという概念でオブジェクトを継続的に作成すると、アプリケーションがGCプロセスをより早くトリガーするようになるということです。 GCは非常にコストのかかるプロセスであり、可能な限り回避し、プッシュしてプッシュしないでください。

0

Java(ただし、追加などのほとんどの単純な操作よりも大きい)の場合、オブジェクトの作成はそれほど大きなコストではありません。

とはいえ、それでもコストはかかるし、できるだけ多くのオブジェクトを廃棄しようとすることがあります。しかし、プロファイリングがこれが問題であることを示した後にのみ。

これはトピックに関するすばらしいプレゼンテーションです: https://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-Java-tutorial.pdf

0
rkj