web-dev-qa-db-ja.com

Javaでクラスをアンロードしますか?

デスクトップアプリケーションが、対話する必要があるAppServerからクラスのロードを動的に開始できるように、カスタムクラスローダーがあります。これを行うために必要なジャーの量はばかげているので(これを出荷したい場合)、これを行いました。また、実行時にAppServerライブラリからクラスを動的にロードしないと、バージョンの問題が発生します。

今、私はちょうど2つの異なるAppServerと話をする必要があるという問題にぶつかり、最初にロードするクラスによってはひどく壊れる可能性があることがわかりました...実際にJVMを殺すことなくクラスのアンロードを強制する方法はありますか?

これが理にかなっていることを願って

167
el_eduardo

クラスをアンロードできる唯一の方法は、使用されているクラスローダーがガベージコレクションされている場合です。これは、すべての単一クラスとクラスローダー自体への参照がドードーの道を行く必要があることを意味します。

問題の解決策の1つは、すべてのjarファイルにClassloaderを使用し、特定のJarクラスローダーにクラスの実際のロードを委任する各AppServerにClassloaderを使用することです。そのようにして、すべてのAppサーバーのjarファイルの異なるバージョンをポイントできます。

ただし、これは簡単なことではありません。 OSGiプラットフォームは、各バンドルに異なるクラスローダーがあり、プラットフォームによって依存関係が解決されるため、まさにこれを実行しようとします。たぶん良い解決策はそれを見てみることでしょう。

OSGIを使用したくない場合は、JARファイルごとに JarClassloader classの1つのインスタンスを使用することが考えられます。

そして、Classloaderを拡張する新しいMultiClassloaderクラスを作成します。このクラスは内部的にJarClassloaderの配列(またはリスト)を持ち、defineClass()メソッドでは、定義が見つかるか、NoClassDefFoundExceptionがスローされるまで、すべての内部クラスローダーを繰り返します。クラスに新しいJarClassloaderを追加するために、いくつかのアクセサメソッドを提供できます。 MultiClassLoaderのネット上にはいくつかの実装が考えられるため、独自に作成する必要すらありません。

サーバーへの接続ごとにMultiClassloaderをインスタンス化する場合、原則として、すべてのサーバーが同じクラスの異なるバージョンを使用する可能性があります。

プロジェクトでMultiClassloaderのアイデアを使用しました。プロジェクトでは、ユーザー定義スクリプトを含むクラスをメモリからロードおよびアンロードする必要があり、非常にうまく機能しました。

183
Mario Ortegón

はい、クラスをロードし、後で「アンロード」する方法があります。トリックは、高レベルクラスローダー(システムクラスローダー)とアプリサーバーのクラスローダーの間に存在する独自のクラスローダーを実装し、アプリサーバーのクラスローダーがクラスローダーを上位ローダーに委任することを期待することです。 。

クラスは、そのパッケージ、名前、および最初にロードしたクラスローダーによって定義されます。 JVMの起動時に最初にロードされる「プロキシ」クラスローダーをプログラムします。ワークフロー:

  • プログラムが起動し、実際の「メイン」クラスがこのプロキシクラスローダーによってロードされます。
  • 通常ロードされるすべてのクラス(つまり、階層を壊す可能性のある別のクラスローダーの実装を介さない)は、このクラスローダーに委任されます。
  • プロキシクラスローダーは、Java.xおよびSun.xをシステムクラスローダーに委任します(これらはシステム以外のクラスローダーを介してロードする必要はありませんクラスローダー)。
  • 置き換え可能なすべてのクラスについて、クラスローダー(実際にクラスをロードし、親クラスローダーに委任しない)をインスタンス化し、これを介してロードします。
  • クラスのパッケージ/名前をキーとして、クラスローダーを値としてデータ構造(つまり、ハッシュマップ)に保存します。
  • プロキシクラスローダーは、以前にロードされたクラスの要求を取得するたびに、以前に保存されたクラスローダーからクラスを返します。
  • クラスローダーによってクラスのバイト配列を見つけ(またはデータ構造からキー/値のペアを「削除」)、クラスを変更したい場合はクラスをリロードすれば十分です。

ClassCastException または LinkageError などが発生しないはずです。

クラスローダー階層の詳細については(はい、それはまさにここで実装しているものです;-)をご覧ください "Ted Newardによる「Server-Based Java Programming" -この本は私を助けてくれましたあなたが望むものに非常に類似した何かを実装します。

39
Georgi

クラスローダーをGCせずに個々のクラスをアンロードできるカスタムクラスローダーを作成しました。 Jarクラスローダー

15
Kamran

クラスローダーは厄介な問題になる可能性があります。複数のクラスローダーを使用していて、それらの相互作用が明確かつ厳密に定義されていない場合、特に問題が発生する可能性があります。実際にクラスをアンロードできるようにするには、アンロードしようとしているクラス(およびそのインスタンス)へのすべての参照を削除する必要があります。

このタイプのことを行う必要があるほとんどの人は OSGi を使用することになります。 OSGiは本当に強力で、驚くほど軽量で使いやすい、

12
Steve g

ClassLoaderはアンロードできますが、特定のクラスをアンロードすることはできません。より具体的には、管理下にないClassLoaderで作成されたクラスをアンロードすることはできません。

可能であれば、アンロードできるように独自のClassLoaderを使用することをお勧めします。

6
Jason Cohen

クラスには、ClassLoaderインスタンスへの暗黙の強い参照があり、その逆もあります。これらは、Javaオブジェクトと同様にガベージコレクションされます。ツールのインターフェイスなどを使用しないと、個々のクラスを削除できません。

これまでどおり、メモリリークが発生する可能性があります。クラスまたはクラスローダーのいずれかへの強い参照は、すべてをリークします。これは、SunのThreadLocal、Java.sql.DriverManager、およびJava.beans実装などで発生します。

アンロードクラスが JConsole などで動作するかどうかをライブで監視している場合は、クラスのアンロードロジックの最後にJava.lang.System.gc()も追加してみてください。ガベージコレクターを明示的にトリガーします。

0