46

職場では、「PermGen out of memory」例外の問題が発生しており、チームリーダーは、これがJVMのバグであると判断しました。これはコードのホットデプロイメントに関連するものです。多くの詳細を説明することなく、彼は、ホットデプロイメントは「難しい問題」であり、.NETでさえまだそれを行っていないほど難しいと指摘しました。

バーズアイビューからホットデプロイメントを説明する記事をたくさん見つけましたが、常に技術的な詳細が不足しています。誰かが私に技術的な説明を指摘し、ホットデプロイメントが「難しい問題」である理由を説明できますか?

4

4 に答える 4

60

クラスがロードされると、クラスに関するさまざまな静的データがPermGenに保存されます。このクラスインスタンスへのライブ参照が存在する限り、クラスインスタンスをガベージコレクションすることはできません。

問題の一部は、GCがpermgenから古いクラスインスタンスを削除する必要があるかどうかに関係していると思います。通常、ホットデプロイするたびに、新しいクラスインスタンスがPermGenメモリプールに追加され、現在は使用されていない古いクラスインスタンスは通常削除されません。デフォルトでは、Sun JVMはPermGenでガベージコレクションを実行しませんが、これはオプションの「java」コマンド引数で有効にできます。

したがって、十分な回数のホットデプロイを行うと、最終的にPermGenスペースを使い果たしてしまいます。

デプロイ解除時にWebアプリが完全にシャットダウンしない場合(たとえば、スレッドが実行されたままの場合)、そのWebアプリで使用されるすべてのClassインスタンスがPermGenスペースに固定されます。再デプロイすると、これらすべてのクラスインスタンスの別の全体のコピーがPermGenにロードされます。アンデプロイすると、スレッドは続行し、PermGenにクラスインスタンスの別のセットを固定します。コピーのネットセット全体を再デプロイしてロードすると、最終的にPermGenがいっぱいになります。

これは、次の方法で修正できる場合があります。

  • PermGenおよびクラスのGCを有効にするために、最近のSunJVMにコマンド引数を提供します。あれは: -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
  • 固定サイズのPermGenを採用していない、またはロードされたクラスでGCを実行する別のJVMを使用する

ただし、これは、Webアプリが完全かつクリーンにシャットダウンし、そのWebアプリのクラスローダーによってロードされたクラスのクラスインスタンスへのライブ参照が残っていない場合にのみ役立ちます。

クラスローダーのリークが原因で、これでも必ずしも問題が解決するわけではありません。(場合によっては、インターンされた文字列が多すぎます。)

詳細については、次のリンクを確認してください(2つの太字のリンクには、問題の一部を説明するための優れた図があります)。

于 2009-03-18T23:16:52.517 に答える
6

一般的な問題は、すでにロードされたクラスが再度ロードされるのを実際に防止しようとする Java のセキュリティ モデルです。

もちろん Java は最初から動的なクラス ローディングをサポートしてきましたが、難しいのはクラスの再ローディングです。

実行中の Java アプリケーションに悪意のあるコードを含む新しいクラスが挿入されることは、(正当な理由で) 有害であると考えられていました。たとえば、java.lang.String のひびの入ったインターネットからの実装では、文字列を作成する代わりに、メソッド length() を呼び出してランダムなファイル ファイルを削除します。

したがって、Java が考案された方法 (JVM で非常に「触発された」ため、結果として .NET CLR を推測します) は、既に読み込まれているクラスが同じ VM を再度読み込むのを防ぐことでした。

彼らは、この「機能」を無効にするメカニズムを提供しました。クラスローダーですが、クラスローダーのルールは、新しいクラスをロードする前に「親」クラスローダーに許可を求める必要がありました。親が既にクラスをロードしている場合、新しいクラスは無視されます。

たとえば、LDAP または RDBMS からクラスをロードするクラスローダーを使用しました。

Java の世界では、アプリケーション サーバーが Java EE の主流になったときに、ホット デプロイが必要になります (また、この種の負担を回避するために、Spring のようなマイクロ コンテナーが必要になります)。

コンパイルのたびにアプリサーバー全体を再起動すると、誰もが気が狂います。

そのため、アプリ サーバー プロバイダーは、この「カスタム」クラス ローダーを提供してホット デプロイを支援し、構成ファイルを使用して、その動作を運用環境で設定するときに無効にする必要があります。ただし、トレードオフは、開発中に大量のメモリを使用する必要があることです。したがって、これを行う良い方法は、3 ~ 4 回の展開ごとに再起動することです。

これは、最初からクラスをロードするように設計された他の言語では発生しません。

たとえば Ruby では、実行中のクラスにメソッドを追加したり、実行時にメソッドをオーバーライドしたり、固有の特定のオブジェクトに単一のメソッドを追加したりすることさえできます。

この種の環境でのトレードオフは、もちろんメモリと速度です。

これが役立つことを願っています。

編集

少し前に、リロードをできるだけ簡単にすることを約束するこの製品を見つけました。この回答を最初に書いたときのリンクを覚えていませんでした。

ZeroTurnaround の JavaRebelです

于 2009-03-18T23:37:43.090 に答える
3

Sun JVM では PermGen スペースが固定されており、最終的にはすべて消費されます (そうです、明らかにクラスローダー関連のコードのバグによるものです) => OOM。

別のベンダーの JVM (Weblogic など) を使用できる場合は、PermGen スペースを動的に拡張するため、permgen 関連の OOM を取得することはありません。

于 2009-03-18T23:47:34.300 に答える