同僚から、Javaオブジェクトの作成は実行できる最も高価な操作であると言われました。したがって、できる限り少ないオブジェクトを作成することしかできません。
これは、オブジェクト指向プログラミングの目的をやや損なうようです。オブジェクトを作成していない場合、最適化のために1つの長いクラスCスタイルを記述しているだけですか?
同僚から、Javaオブジェクトの作成は実行できる最も高価な操作であると言われました。したがって、できる限り少ないオブジェクトを作成することしかできません。
これは、オブジェクト指向プログラミングの目的をやや損なうようです。オブジェクトを作成していない場合、最適化のために1つの長いクラスCスタイルを記述しているだけですか?
回答:
あなたの最も高価な操作はそれらを聞いているでしょう。彼らは、10年以上前の情報(この回答が投稿された当初の時点)に誤った方向を向ける時間を無駄にし、ここに投稿して真実を調べるために時間を費やす必要がありました。
10年以上前に聞いたり読んだりしたものを無知に吐き出し、それ以上何も知らないことを願っています。私は彼らが疑わしいと言う他のことも考えますが、これはいずれにせよ最新の情報を保持している人なら誰でもよく知っている間違いです。
primitives
)プリミティブ(int, long, double
など)以外はすべてJavaのオブジェクトです。Javaでのオブジェクトの作成を回避する方法はありません。
Javaでのメモリ割り当て戦略によるオブジェクト作成は、ほとんどの場合C ++よりも高速で、JVMの他のすべてと比較して実用的な目的のために「フリー」と見なすことができます。
1990年代後半から2000年代初頭にかけてのJVM実装では、オブジェクトの実際の割り当てでパフォーマンスのオーバーヘッドが発生していました。これは、少なくとも2005年以来そうではありません。
-Xms
アプリケーションを正しく実行するために必要なすべてのメモリをサポートするように調整すると、GCを実行する必要がなくなり、最新のGC実装でほとんどのガベージをスイープする必要がなくなります。
とにかく赤いニシンである空き領域を最大化しようとはしませんが、ランタイムのパフォーマンスを最大化します。それは、JVMヒープが常にほぼ100%割り当てられていることを意味する場合は、そうする必要があります。とにかく、無料のJVMヒープメモリはそこに座っているだけではありません。
GCは便利な方法でメモリを解放してシステムの残りの部分に戻すという誤解がありますが、これは完全に間違っています!
JVMヒープは拡大も縮小もしないため、システムの残りの部分はJVMヒープの空きメモリによってプラスの影響を受けます。-Xms
起動時に指定されたすべてを割り当てます。そのヒューリスティックは、JVMのインスタンスが完全に終了するまで、メモリをOSに実際に解放して他のOSプロセスと共有しないことです。-Xms=1GB -Xmx=1GB
常に実際に作成されるオブジェクトの数に関係なく、1GBのRAMを割り当てます。ヒープメモリのパーセンテージを解放できるようにする設定がいくつかありますが、すべての実用的な目的のために、JVMはこれが発生するのに十分なメモリを解放することはできませんそのため、他のプロセスはこのメモリを再利用できません。そのため、システムの残りの部分も、JVMヒープが解放されても恩恵を受けません。このためのRFEは2006年11月29日「承認」されましたが、これまで何も行われていません。これは行動であり、権威者による懸念ではありません。
多くの小さな短命のオブジェクトを作成すると、JVMが長時間停止するという誤解がありますが、これも同様にfalseです
現在のGCアルゴリズムは、短命である多くの小さなオブジェクトを作成するために実際に最適化されています。これは、基本的にすべてのプログラムのJavaオブジェクトの99%のヒューリスティックです。オブジェクトプーリングを試みると、ほとんどの場合、実際にはJVMのパフォーマンスが低下します。
現在プーリングが必要なオブジェクトは、JVMの外部にある有限のリソースを参照するオブジェクトのみです。ソケット、ファイル、データベース接続など、再利用できます。通常のオブジェクトは、メモリの場所に直接アクセスできる言語と同じ意味ではプールできません。オブジェクトキャッシングは異なる概念であり、一部の人々が単純にプーリングと呼ぶ場合とそうでない場合があります。2つの概念は同じものではなく、混同すべきではありません。
最新のGCアルゴリズムは、スケジュールに従って割り当てを解除しないため、特定の世代で空きメモリが必要になると割り当てを解除するため、この問題はありません。ヒープが十分に大きい場合、一時停止を引き起こすほど長く割り当て解除は発生しません。
_alloca
、償却よりも高速に実行されるメモリアリーナクラスを作成できます。
new
ホットスポットでキーワードを使用する前に、コンストラクターで何が起こるかを常に確認する必要があります。Swingオブジェクトnew ImageIcon(Image)
のpaint()
メソッドで人々が使用するのを見てきましたが、これはかなり高価で、UI全体が非常に遅くなりました。だから、それはあなたが使用する前に考えて、黒と白の答えではありませんnew
どこか。
結論:オブジェクトを作成するショートカットを作成するために、デザインを妥協しないでください。不必要にオブジェクトを作成しないでください。賢明な場合は、(あらゆる種類の)冗長な操作を避けるように設計してください。
ほとんどの答えに反して-はい、オブジェクトの割り当てにはコストがかかります。低コストですが、不要なオブジェクトを作成しないでください。コード内の不要なものを避ける必要があるのと同じです。大きなオブジェクトグラフを使用すると、GCが遅くなり、実行時間が長くなる可能性があるため、メソッド呼び出しが増え、CPUキャッシュミスが発生し、RAMが少ない場合にプロセスがディスクにスワップされる可能性が高くなります。
これがエッジケースであると暴言を言う前に-最適化する前に、最大50行のデータを処理するために20MB以上のオブジェクトを作成したアプリケーションのプロファイルを作成しました。これは、1分間に100件のリクエストにスケールアップし、突然1分間に2GBのデータを作成するまで、テストでは問題ありません。20 reqs / secを実行する場合は、400MBのオブジェクトを作成してから破棄します。まともなサーバーの場合、20 reqs / secはごくわずかです。
while(something) { byte[] buffer = new byte[10240]; ... readIntoBuffer(buffer); ...
かもしれません例に比べて無駄が多いbyte[] buffer = new byte[10240]; while(something) { ... readIntoBuffer(buffer); ...
。
実際、Java言語(または他のマネージ言語)が可能にするメモリ管理戦略により、オブジェクトの作成は、若い世代と呼ばれるメモリブロック内のポインタをインクリメントする以上のことはありません。空きメモリの検索を行う必要があるCよりもはるかに高速です。
コストの他の部分はオブジェクトの破壊ですが、Cと比較するのは困難です。コレクションのコストは長期保存されたオブジェクトの量に基づきますが、コレクションの頻度は作成されたオブジェクトの量に基づきます...最後に、Cスタイルのメモリ管理よりもはるかに高速です。
Point
オブジェクトが2つの汎用レジスタ内に収まることができます)。
他のポスターは、Javaではオブジェクトの作成が非常に高速であり、通常のJavaアプリケーションでは通常、オブジェクトの作成について心配するべきではないことを正しく指摘しています。
非常に特殊な状況のカップルがあります。あるオブジェクトの作成を回避するために、良いアイデア。
あなたの同僚が言っていることには真実の核があります。オブジェクトの作成に関する問題は、実際にはガベージコレクションであることをお勧めします。C ++では、プログラマはメモリの割り当て解除方法を正確に制御できます。このプログラムは、好きなだけ長い間、または短い間、ごみを蓄積することができます。さらに、C ++プログラムは、それを作成したスレッドとは異なるスレッドを使用して、クラッドを破棄できます。したがって、現在動作しているスレッドがクリーンアップするために停止する必要はありません。
対照的に、Java仮想マシン(JVM)は定期的にコードを停止して、未使用のメモリを再利用します。ほとんどのJava開発者はこの一時停止に気付くことはありません。これは通常、頻繁ではなく非常に短いためです。JVMの蓄積が多いほど、またはJVMの制約が大きいほど、これらの一時停止が頻繁に発生します。VisualVMなどのツールを使用して、このプロセスを視覚化できます。
Javaの最近のバージョンでは、ガベージコレクション(GC)アルゴリズムを調整できます。一般的な規則として、一時停止を短くしたいほど、仮想マシンのオーバーヘッドが高くなります(つまり、CPUとメモリがGCプロセスの調整に費やした)。
これはいつ問題になるでしょうか?一貫したサブミリ秒の応答率を気にするときはいつでも、GCを気にします。Javaで記述された自動取引システムは、一時停止を最小限に抑えるためにJVMを大幅に調整します。そうでなければJavaを作成する企業は、システムの応答性が常に高い場合にC ++を使用します。
記録のために、私は一般的にオブジェクトの回避を容認しません!デフォルトはオブジェクト指向プログラミングです。このアプローチは、GCが邪魔になった場合にのみ調整し、JVMを調整して一時停止時間が短くなった後にのみ調整してください。Javaパフォーマンスチューニングに関する優れた書籍は、Charlie HuntとBinu JohnによるJavaパフォーマンスです。
オーバーヘッドのためにJavaでオブジェクトを作成しすぎることを思いとどまる場合が1つあります -Androidプラットフォームでのパフォーマンスのための設計
それ以外は、上記の答えが当てはまります。
GCは多くの短命オブジェクトに合わせて調整されます
それは、オブジェクトの割り当てを簡単に減らすことができるなら、
1つの例は、ループで文字列を構築することです、素朴な方法は
String str = "";
while(someCondition){
//...
str+= appendingString;
}
これにより、String
各+=
操作で新しいオブジェクトが作成されます(さらにStringBuilder
、新しい基になるchar配列)
これを次のように簡単に書き換えることができます。
StringBuilder strB = new StringBuilder();
while(someCondition){
//...
strB.append(appendingString);
}
String str = strB.toString();
このパターン(不変の結果とローカルで変更可能な中間値)は、他のものにも適用できます
それ以外は、ゴーストを追いかけるのではなく、プロファイラーを引き上げて本当のボトルネックを見つける必要があります
StringBuilder
アプローチがあり、プリサイズStringBuilder
ので、それをしなければならないことを再配分となる配列StringBuilder(int)
コンストラクタを。これにより、割り当てではなく単一の割り当てになり1+N
ます。
Joshua Bloch(Javaプラットフォームクリエーターの1人)は2001年に彼の著書Effective Javaに次のように書いています。
独自のオブジェクトプールを維持してオブジェクトの作成を回避することは、プール内のオブジェクトが極端に重い場合を除いて、悪い考えです。オブジェクトプールを正当化するオブジェクトの典型的な例は、データベース接続です。接続を確立するコストは十分に高いため、これらのオブジェクトを再利用するのは理にかなっています。ただし、一般的に、独自のオブジェクトプールを維持すると、コードが乱雑になり、メモリフットプリントが増加し、パフォーマンスが低下します。最新のJVM実装には、軽量オブジェクトでこのようなオブジェクトプールを簡単に上回るガベージコレクターが高度に最適化されています。
これは本当に特定のアプリケーションに依存するため、一般的に言うのは本当に難しいです。ただし、オブジェクトの作成が実際にアプリケーションのパフォーマンスのボトルネックになっている場合、私は非常に驚くでしょう。それらが遅い場合でも、コードスタイルの利点はおそらくパフォーマンスよりも重要です(実際にユーザーが気付かない限り)。
いずれの場合でも、推測する代わりに実際のパフォーマンスのボトルネックを決定するためにコードをプロファイルするまで、これらのことについて心配することを始めるべきではありません。それまでは、パフォーマンスではなく、コードを読みやすくするために最善のことを行う必要があります。
あなたの同僚は、不必要なオブジェクト作成の観点から言ったに違いないと思います。同じオブジェクトを頻繁に作成する場合は、そのオブジェクトを共有することをお勧めします。オブジェクトの作成が複雑で、より多くのメモリを必要とする場合でも、そのオブジェクトのクローンを作成し、その複雑なオブジェクト作成プロセスの作成を避けることができます(ただし、要件によって異なります)。「オブジェクトの作成には費用がかかります」という文は文脈に沿って解釈する必要があると思います。
JVMのメモリ要件に関する限り、Java 8を待ちます。-Xmxを指定する必要さえありません。メタスペースの設定がJVMのメモリの必要性を処理し、自動的に成長します。
クラスの作成には、メモリの割り当て以上のものがあります。初期化もあります。なぜその部分がすべての答えでカバーされないのか分かりません。通常のクラスにはいくつかの変数が含まれており、何らかの形式の初期化を行いますが、それは無料ではありません。クラスが何であるかに応じて、ファイルを読み取るか、他の多くの低速操作を実行します。
したがって、クラスコンストラクターが無料かどうかを判断する前に、クラスコンストラクターが何を行うかを検討してください。
JavaのGCは、実際には大量のオブジェクトを「バースト」方式で迅速に作成するという点で非常に最適化されています。私が理解できることから、彼らは「エデン空間」と呼ばれるメモリ空間にその種の「バーストサイクル」のためにシーケンシャルアロケーター(可変サイズのリクエストのための最速かつ最も簡単なO(1)アロケーター)を使用し、オブジェクトが持続する場合のみGCサイクルの後、GCが1つずつ収集できる場所に移動します。
とはいえ、パフォーマンスニーズが十分に重要になった場合(実際のユーザーエンドの要件で測定した場合)、オブジェクトにはオーバーヘッドがかかりますが、作成/割り当てに関してはあまり考えません。参照の局所性と、リフレクションやダイナミックディスパッチなどの概念をサポートするために必要なJavaのすべてのオブジェクトの追加サイズとの関係があります(多くのFloat
場合float
、64ビットではアライメント要件が4倍以上であり、 array of Float
は、私が理解しているものから連続して格納されるとは限りません)。
Javaで開発した最も目を引くものの1つは、私の分野(VFX)でヘビー級の競争相手であると考えさせたものです(インタラクティブなマルチスレッド標準パストレーサー(放射キャッシュ、BDPT、MLSなどを使用しません) CPUはリアルタイムのプレビューを提供し、ノイズのない画像にかなり迅速に収束します。私はC ++の専門家と仕事をして、そのようなことに専念してきました。
しかし、私はソースコードをじっと見つめましたが、それは些細なコストで多くのオブジェクトを使用していましたが、パストレーサーの最も重要な部分(BVHおよび三角形とマテリアル)は、プリミティブタイプの大きな配列を優先してオブジェクトを非常に明確かつ意図的に回避しました(ほとんどfloat[]
とint[]
)、それははるかに少ないメモリを使用float
し、配列内のある場所から次の場所に到達するための空間的局所性を保証しました。著者が好きなボックス化されたタイプを使用した場合、Float
そこでは、パフォーマンスにかなりのコストがかかります。しかし、私たちはそのエンジンの最も絶対に重要な部分について話しているので、開発者がどれだけ巧みに最適化したかを考えて、彼はそれを測定し、その最適化を非常に慎重に適用しました。彼の印象的なリアルタイムパストレーサーの些細なコスト。
同僚から、Javaオブジェクトの作成は実行できる最も高価な操作であると言われました。したがって、できる限り少ないオブジェクトを作成することしかできません。
私のようにパフォーマンスが重視される分野であっても、重要ではない場所で自分自身を縛り付けると、効率的な製品を書くことはできません。最もパフォーマンスが重要な分野では生産性に対する要求がさらに高くなる可能性があると主張することもあります。なぜなら、そうでない時間に無駄をかけないことで、本当に重要なホットスポットを調整するために余分な時間が必要になるからです。上記のパストレーサーの例と同様に、著者は巧妙かつ慎重にこのような最適化を、本当に重要で、おそらく測定後に後知恵のある場所にのみ適用し、それでも他のどこでも幸福にオブジェクトを使用しました。
float[]
vsのようなものFloat[]
です。
人々が言ったように、オブジェクトの作成はJavaで大きなコストではありません(ただし、追加などの最も単純な操作よりも大きいと思います)。
それはまだ費用がかかり、可能な限り多くのオブジェクトを廃棄しようとしていることに気付くことがあります。しかし、プロファイリングがこれが問題であることを示して初めてです。
このトピックに関する優れたプレゼンテーションは次のとおりです。https://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf
これに関して簡単なマイクロベンチマークを行い、githubで完全なソースを提供しました。私の結論は、オブジェクトの作成に費用がかかるかどうかは問題ではありませんが、GCが処理を行うという概念でオブジェクトを作成し続けると、アプリケーションがGCプロセスをより早くトリガーするようになります。GCは非常に高価なプロセスであり、可能な限り避けることをお勧めします。