さらに多くのオブジェクト割り当て要求が入る前にGCを実行する機会があった場合、JVMは再起動なしでOutOfMemoryErrorから回復できますか?
さまざまなJVM実装はこの点で異なりますか?
私の質問は、JVMの回復についてであり、ユーザープログラムがエラーをキャッチして回復を試みているのではありません。つまり、アプリケーションサーバー(jboss/websphere/..)でOOMEがスローされた場合、haveで再起動しますか?または、それ以上のリクエストが問題なく機能しているように思われる場合は、実行させることができますか?.
それはうまくいくかもしれませんが、一般的には悪い考えです。アプリケーションが回復する成功、または成功しなかったかどうかを知る保証はありません。例えば:
予約済みメモリのブロックを解放するなどのリカバリ手順を実行した後でも、要求されたタスクを実行するのに十分なメモリが not ある可能性があります。この状況では、アプリケーションがループに陥り、繰り返し回復しているように見えて、再びメモリ不足になる可能性があります。
OOMEはどのスレッドでもスローされます。アプリケーションスレッドまたはライブラリがそれに対応するように設計されていない場合、これにより、長期間有効なデータ構造が不完全または不整合な状態になる可能性があります。
OOMEの結果としてスレッドが停止した場合、アプリケーションはOOMEリカバリの一部としてスレッドを再起動する必要がある場合があります。少なくとも、これはアプリケーションをより複雑にします。
スレッドが、通知/待機または何らかの高レベルのメカニズムを使用して他のスレッドと同期するとします。そのスレッドがOOMEで終了した場合、他のスレッドは、来たことのない通知(など)を待機したままになることがあります。これを設計すると、アプリケーションが大幅に複雑になる可能性があります。
要約すると、特にアプリケーション(またはアプリケーションが実行されるフレームワーク、またはアプリケーションが使用するライブラリ)がマルチスレッドである場合、OOMEから回復するためのアプリケーションの設計、実装、およびテストは難しい場合があります。 OOMEを致命的なエラーとして扱うことをお勧めします。
関連する質問の my answer も参照してください:
[〜#〜] edit [〜#〜]-このフォローアップの質問への回答:
言い換えると、OOMEがアプリケーションサーバー(jboss/websphere/..)でスローされた場合、have to再起動しますか?
いいえする必要があります再起動します。しかし、それはおそらくwise toです。特に、サービスが正しく実行されていることを確認する適切な/自動化された方法がない場合は特にそうです。
JVMは正常に回復します。ただし、アプリケーションサーバーとアプリケーション自体は、この状況にどのように対処するように設計されているかによって、回復する場合と回復しない場合があります。 (私の経験では、一部のアプリサーバーはこれに対応するように設計されていない/ であり、OOMEから回復するための複雑なアプリケーションの設計と実装は困難であり、適切にテストすることはさらに困難です。 )
編集2
このコメントへの応答:
「他のスレッドは、来たことのない通知(など)を待機したままになる可能性があります」本当に?強制終了されたスレッドはスタックを巻き戻し、保持されているロックを含め、リソースをそのまま解放しませんか?
はい、そうです!このことを考慮:
スレッド#1はこれを実行します。
_ synchronized(lock) {
while (!someCondition) {
lock.wait();
}
}
// ...
_
スレッド#2はこれを実行します。
_ synchronized(lock) {
// do stuff
lock.notify();
}
_
スレッド#1が通知を待機していて、スレッド#2が_// do something
_セクションでOOMEを取得した場合、スレッド#2はnotify()
呼び出しを行わず、スレッド#1は発生しない通知を待って永久にスタックしました。確かに、スレッド#2はlock
オブジェクトのミューテックスを解放することが保証されていますが、それだけでは不十分です。
そうでない場合、スレッドによって実行されたコードは例外セーフではありません。これはより一般的な問題です。
「例外安全」は私が聞いたことのある用語ではありません(私はあなたが何を意味するのか知っていますが)。 Javaプログラムは通常、予期しない例外に耐えられるように設計されていません。実際、上記のようなシナリオでは、アプリケーションの例外を安全にすることは困難と不可能の中間にある可能性があります。
スレッド#1の障害(OOMEによる)がスレッド#2へのスレッド間通信障害通知に変わるメカニズムが必要です。 Erlangはこれを行いますが、Javaは行いません。 Erlangでこれを実行できるのは、ErlangプロセスがCSPのような厳密なプリミティブを使用して通信するためです。つまり、データ構造の共有はありません!
(上記の問題は、ほぼすべての unexpected 例外... Error
例外だけでなく発生します。特定の種類のJava unexpected 例外から回復しようとすると、コードが異常終了する可能性があります。)
JVMは、OutOfMemoryError
のエッジにあるときにGCを実行します。 GCがまったく役に立たなかった場合、JVMはOOMEをスローします。
あなたはできますがただしcatch
で、必要に応じて代替パスを使用します。 try
ブロック内の割り当てはすべてGCされます。
OOMEはError
であり、catch
である可能性があるため、異なるJVM実装が同じように動作することを期待します。少なくとも経験から、上記がSun JVMに当てはまることを確認できます。
以下も参照してください:
OutOfMemoryErrorの原因に部分的に依存すると思います。 JVMのメモリが実際に不足している場合は、JVMを再起動し、可能な場合はより多くのメモリ(またはより効率的なアプリ)を使用することをお勧めします。ただし、2 GBの配列などを割り当てることによって引き起こされるかなりの量のOOMEを見てきました。その場合、それがJ2EE Webアプリのようなものであれば、エラーの影響はその特定のアプリに限定されるべきであり、JVM全体の再起動は何の役にも立ちません。
できる回復する?たぶん。適切に作成されたJVMは、ユーザーが指示したことを実行するのに十分なメモリを再利用するためにできる限りのことを試みた後にのみ、OOMEをスローします。これがあなたが回復できないことを意味する非常に良い可能性があります。だが...
それは多くのものに依存します。たとえば、ガベージコレクターがコピーコレクターではない場合、「メモリ不足」状態は、実際には「割り当てるのに十分な大きさのチャンクがない」可能性があります。スタックを巻き戻すという行為自体が、後のGCラウンドでオブジェクトをクリーンアップして、目的に十分な大きさの開いたチャンクを残す場合があります。その状況では、再起動できる場合があります。結果として、少なくとも1回は再試行する価値があります。だが...
あなたはおそらくこれに依存したくないでしょう。定期的にOOMEを取得している場合は、サーバーを調べて、何が起こっているのか、そしてその理由を調べてください。コードをクリーンアップする必要があるかもしれません(リークしたり、一時オブジェクトを作成しすぎたりする可能性があります)。たぶん、JVMを呼び出すときにメモリの上限を上げる必要があります。 OOMEは、たとえ回復可能であっても、コードのどこかで何か悪いことがファンに影響を与えたという兆候として扱い、それに応じて行動します。たぶん、サーバーがNOWNOWNOWNOWNOWダウンする必要はないかもしれませんが、より深いトラブルに巻き込まれる前に何かを修正する必要があります。
試すことはお勧めしませんが、このシナリオから回復する確率を上げることができます。あなたがすることは、回復作業を行うために専用の起動時に固定量のメモリを事前に割り当てることであり、OOMをキャッチすると、事前に割り当てられた参照を無効にすると、more可能性があります回復シーケンスで使用するメモリを確保します。
さまざまなJVM実装については知りません。
正常なJVMは、ガベージコレクターが実行できる処理がない場合にのみOutOfMemoryErrorをスローします。ただし、スタックフレームでOutOfMemoryErrorを早期にキャッチした場合、原因自体が到達不能になり、ガベージコレクションが行われた可能性があります(現在のスレッドに問題がない場合を除く)。
一般に、アプリケーションサーバーのような他のコードを実行するフレームワークは、OMEに直面して続行しようとすることは理にかなっています(サードパーティのコードを合理的に解放できる限り)。何も起こらなかったかのように続行しようとするのではなく、理由をベイルしてユーザーに伝えます。
新しく更新された質問に回答するには:すべてが正常に機能している場合、サーバーをシャットダウンする必要があると考える理由はありません。私のJBossでの経験では、OMEがデプロイメントに影響を与えない限り、問題はありません。多くのホットデプロイメントを行うと、JBossがpermgenスペースを使い果たすことがあります。その場合、実際には状況は絶望的であり、即時の再起動(強制終了する必要があります)は避けられません。
もちろん、各アプリサーバー(および展開シナリオ)は異なりますが、実際にはそれぞれの経験から学んだことです。
OutOfMemoryErrorが発生したJVMを完全には使用できません。少なくともOracle JVMでは、-XX:OnOutOfMemoryError="cmd args;cmd args"
そして、JVMを強制終了したり、イベントをどこかに送信したりするなどの回復アクションを実行します。
リファレンス: https://www.Oracle.com/technetwork/Java/javase/tech/vmoptions-jsp-140102.html