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


174

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

さて、2つの異なるAppServerと通信する必要があるという問題にぶつかったところ、最初にロードするクラスによっては、うまく機能しない可能性があることがわかりました...実際にJVMを強制終了せずにクラスのアンロードを強制する方法はありますか?

これは理にかなっていると思います


各jarのクラスローダーはありますか?OSGIコンテナーはどのようにアーカイブしてクラスローダーをアンロードしますか?クラスローダークラスにアンロードAPIがないようです。
hetaoblog 2011

回答:


190

クラスをアンロードできる唯一の方法は、使用するクラスローダーがガベージコレクションである場合です。つまり、すべてのクラスへの参照とクラスローダー自体への参照は、ドードーの道を進む必要があります。

問題の1つの可能な解決策は、すべてのjarファイルのクラスローダーと、クラスの実際のロードを特定のJarクラスローダーに委任する各AppServerのクラスローダーを持つことです。そうすることで、すべてのアプリサーバーで異なるバージョンのjarファイルを指定できます。

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

OSGIを使用したくない場合、1つの可能な実装は、JARファイルごとにJarClassloaderクラスの1つのインスタンスを使用することです。

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

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

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


31
また、java.sun.com / docs / books / jls / second_edition / html /…によると、クラスのアンロードは最適化であり、JVM実装によっては、実際に発生する場合と発生しない場合があります。

5
OSGiのより簡単で軽量な代替手段として、JBoss Modules-モジュールごとのクラスローダー(jarのグループ)でモジュール化されたクラスローディングを試してください。
OndraŽižka13年

42

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

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

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

ClassCastExceptionLinkageErrorなどは発生しません。

クラスローダー階層の詳細(はい、これがまさにここで実装しているものです;-)は、Ted Newardによる「サーバーベースのJavaプログラミング」を参照してください。


3
コメントを残さずにこの回答に対して-1を選択した人は理解できません。私にとってはよさそうだ。おそらく、ClassLoaderクラスごとに1つ持つのは少し多すぎますが、ClassLoaderJARごとに1つは理にかなっています。提案されたスキーマでクラスを強制的にアップロードする方法についてより具体的にできますか?たとえば、ClassLoaderAによってロードされたクラスのインスタンスがClassLoaderBによってロードされたインスタンスによって参照されないことをどのように保証できますか?
dma_k '20年

@dma_k正確に、答えは良いですが、あなたが言及した重要な点には触れていません。
2013年

@Georgiは、これを初期化/再利用できる既存の実装がありますか?
そり

1
サンプルのJavaコードを提供できれば非常に役に立ちます。正確には、CustomClassLoaderを使用してクラスをアンロードする方法を探していますが、うまくいきませんでした。
Sriharsha grv

@ Sriharshag.rvこれを試してサンプルを実装しましたか?
niaomingjian 2018年

17

私はカスタムクラスローダーを作成しました。そこから、クラスローダーをGCすることなく、個々のクラスをアンロードできます。JARクラスローダー


魅力のように動作します:)。jarファイルのすべてのクラスファイルをアンロードする方法はありますか?
Ercksen

残念ながら現時点ではできません。しかし、それを調べます。将来のリリースになる可能性があります。
Kamran

ちなみに、少し回避策を見つけました。JarClassLoaderロードしたすべてのjarファイルに1つある場合は、それを呼び出しgetLoadedClasses()、それぞれを反復処理してアンロードできます。
Ercksen 2015年

12

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

この種のことを行う必要があるほとんどの人は、結局OSGiを使用します。OSGiは非常に強力で、驚くほど軽量で使いやすいです。


7

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

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


4

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

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


-1

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


3
注意:System.gc()は、GCを呼び出す必要はありません。jvmに起動を要求するだけですが、強制はしません。IMEがGCを開始しないことがよくあります:-\
Juh_
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.