オブジェクトプーリングは非推奨のテクニックですか?


62

私はオブジェクトプーリングの概念に非常に精通しており、常に可能な限りそれを使用しようとしています。

さらに、Java自体と他のフレームワークが可能な限りプーリングを使用することを確認したため、オブジェクトプーリングは標準的な標準であると常に考えていました。

最近、私にとってはまったく新しい(そして直感に反する?)ものを読みました。

このプーリングにより、特に同時実行アプリケーションでは特にプログラムのパフォーマンスが低下します。new新しいJVMではオブジェクトのインスタンス化が非常に高速であるため、代わりにオブジェクトをインスタンス化することをお勧めします。

私は本でこれを読みました: Java Concurrency in Practice

本の最初の部分では、新しいインスタンスを作成する代わりにExecutorsその再利用を使用するようにアドバイスされているので、ここで何かを誤解しているのではないかと考え始めていThreadます。

それでは、オブジェクトプーリングは非推奨になりましたか?

回答:


72

-ご存知のように-短命オブジェクト自体の作成と破棄(つまり、メモリ割り当てとGC)は現代のJVMでは非常に安価であるため、一般的な手法としては推奨されません。そのため、通常のオブジェクトに手書きのオブジェクトプールを使用することは、通常よりも遅く、複雑で、エラーが発生しやすい可能性がありnewます。*

ただし、DB /ネットワーク接続、スレッドなど、作成に比較的コストがかかる特別なオブジェクトには、まだ用途があります。

* クロールJavaアプリのパフォーマンスを改善する必要があった。調査により、オブジェクトプールを使用して何百万ものオブジェクトを割り当てる試みが明らかになりました...そして、それを書いた賢い人は、単一のグローバルロックを使用してスレッドセーフにしました。プールをプレーンに置き換えるとnew、アプリが30倍速くなりました。


1
それでは、オブジェクトのインスタンス化が高すぎるかどうかをどのように判断できますか?
user10326

3
オブジェクトがオペレーティングシステムリソース(スレッド、I / O、共有メモリなど)を消費する場合
ケビンクライン

13
@ user10326、測定による:-)オブジェクトの作成に時間がかかる場合、および/またはオブジェクトが特別な、潜在的に制限されている、メモリ以外のリソースに関連付けられている場合、プーリングを検討できます。
ペテルトレック

8
@ user10326、IMOの95%以上のケースでは、上記の基準により、オブジェクトプールが必要かどうかを事前に簡単に決定できます。(さらに、プールを必要とするほとんどすべての場合、既存のライブラリ/フレームワークを使用する可能性が高く、おそらく既にオブジェクトプールが実装されています。)残りについては、たとえばファクトリー。後で適切な方法で再実装できます。
ペテルトレック

2
@Peter Torokによる非常に重要なポイント:多くのフレームワークとライブラリはプーリングを実装しています。独自の実装を行う前に、常にプールされたライブラリを使用していないことを確認してください。
hromanko

36

具体的な質問への答え:「オブジェクトプーリングは非推奨のテクニックですか?」は:

いいえ。オブジェクトプーリングは、スレッドプーリング、データベース接続プーリングなど、特定の場所で広く使用されています。

一般的なオブジェクトの作成は決して遅いプロセスではありません。プール自体は、リソースとメモリと処理能力を消費します。最適化はトレードオフです。

ルールは次のとおりです。

時期尚早な最適化は悪!!!

しかし、与えられた最適化は時期尚早ですか?

早期最適化とは徹底的なプロファイリングによってボトルネック発見する前に行われた最適化です。


2
確かに。OPは「私は常に可能な限りそれを使用しようとしています」と述べました-これは問題です、IMO。
nerdytenor

@ Boris、2番目の文によると、プロファイリングによってボトルネックとしてそれらを発見するまで、オブジェクトプールのdb接続とスレッドをオブジェクト化するべきではありませんか?
Pacerier

1
@Pac一部のプロファイリング結果は、一定の再測定を必要としません:
デビッドブロック

9

ガベージコレクションを完全に避けたい状況では、オブジェクトプーリングが唯一の実行可能な代替手段だと思います。だから、それは絶対に非推奨の手法ではありません。


1
そして、オブジェクトの寿命が長くなり、古い世代に移動した場合は、GCを避けることをお勧めします。
ザンリンクス

8

測定する

それは、ユースケース、オブジェクトのサイズ、JVM、JVMオプション、有効にしたGC、およびその他の多くの要因に完全に依存します。

要するに、前に測定し、後に測定します。(Apacheのような)オブジェクトプーリングフレームワークを使用していると仮定すると、実装間で交換するのに苦痛はないはずです。

追加のパフォーマンステストのヒント-最初にJVMを少しウォームアップし、実行中のJVMで何度もテストを実行すると、動作が異なる場合があります。


3
「JVMを少し先にウォームアップさせてください」-「ウォームアップ」する必要があるのはモニターだけだったときのことを覚えています。おい、新しいものはすべて古い。
キルベン

ウォームアップする必要があるのはコーヒーだけです!
幻滅

@Marijn、どうやって「ウォームアップ」させますか?
14

詳細については、JMHフレームワーク(openjdk.java.net/projects/code-tools/jmh)を参照してください。しかし、基本的には、JVMにコードをJITする機会を与え、ベンチマークの前にGCを実行する必要があります。
マーティンヴェルブルク

8

このプーリングにより、特に同時実行アプリケーションでは特にプログラムのパフォーマンスが低下します。新しいJVMでは、オブジェクトのインスタンス化が非常に高速であるため、代わりに新しいオブジェクトをインスタンス化することをお勧めします。

コンテキストに依存します。


1
優れた答えであり、説得力があります。「おそらく、アスタリスクとして?」という「24バイト」の主張は、4バイトのfloat(16バイト)の4つのインスタンス、オブジェクト参照用の4バイト、ロック参照用の4バイトを指すと付け加えます。これは、設計によって排除される正確なオーバーヘッドです。
strickli

5

ここにトレンドの変化があるかどうかはわかりませんが、それは確かにそれが依存する場合です。JavaクラスがRMI接続やリソースファイルのロードなどの外部リソースを管理している場合、オブジェクトのインスタンス化のコストは確かに高くなります(ただし、これらのリソースは既にプールされている可能性があります!)。一般的な慣行として、私は本に同意します。


この場合でも、(これを読む前に)プーリングを明確に使用することを記述しているため、オーバーヘッドもあります。1)プーリングを処理するための新しい構成2)オブジェクトの取得/解放プール3から)プールなどを維持するため、サーバーに接続するたびに新しいソケットを開くのではなく、ソケットをキャッシュするなど、有用なユースケースはおそらくないと考えています。このケースはネットワークのためですインスタンス化のオーバーヘッドではなくレイテンシー
-user10326

@ user10326はい、正確に。インスタンス化のオーバーヘッドの一部としてソケットを開くと考えています。そのクラスの仕事がそれを行い、コンストラクタで初期化する必要がある場合、レイテンシとIOへの影響が懸念されます。
ジェレミー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.