.javaファイルを動的にロードおよびアンロードするJava、ガベージコレクション?

ヨルダン:

シャットダウンせずに更新された機能を必要とする長期間実行されるJavaアプリケーションを作成しています。私はこの更新された機能を、メモリでコンパイルされインスタンス化される.javaファイル(データベースからバイト配列としてプルされる)の形式でロードすることで提供することにしました。あなたがより良い方法を持っているなら、私はすべて耳です。

私が遭遇した問題は、人工環境でテストを行うと、これらの「スクリプト」をロードする各サイクルでメモリフットプリントがわずかに増加することです。

注:これは実際に、Javaでこのようなことを行うのが初めてです。以前、C#で.csファイルをロードおよびアンロードしてこのようなことを達成し、そこにメモリフットプリントの問題もありました...それらを別のアプリドメインにロードしたことを解決し、ファイルを再コンパイルすると、そのアプリドメインをアンロードして作成しました新しいもの。

エントリーポイント


これは、長期間使用した後(多くの再コンパイルサイクル)にメモリフットプリントをシミュレートするために使用している入力方法です。私はこれを短期間実行すると、すぐに500MB以上を消費します。

これは、一時ディレクトリに2つのダミースクリプトがある場合のみです。

public static void main( String[ ] args ) throws Exception {
    for ( int i = 0; i < 1000; i++ ) {
        Container[ ] containers = getScriptContainers( );
        Script[ ] scripts = compileScripts( containers );

        for ( Script s : scripts ) s.Begin( );
        Thread.sleep( 1000 );
    }
}

スクリプトのリストの収集(一時的)


これは、スクリプトファイルのリストを収集するために使用している一時的な方法です。本番環境では、これらは実際には、データベースからのクラス名などの他の情報を含むバイト配列としてロードされます。

@Deprecated
private static Container[ ] getScriptContainers( ) throws IOException {
    File root = new File( "C:\\Scripts\\" );
    File[ ] files = root.listFiles( );

    List< Container > containers = new ArrayList<>( );
    for ( File f : files ) {
        String[ ] tokens = f.getName( ).split( "\\.(?=[^\\.]+$)" );
        if ( f.isFile( ) && tokens[ 1 ].equals( "java" ) ) {
            byte[ ] fileBytes = Files.readAllBytes( Paths.get( f.getAbsolutePath( ) ) );
            containers.add( new Container( tokens[ 0 ], fileBytes ) );
        }
    }

    return containers.toArray( new Container[ 0 ] );
 }

コンテナクラス


これは単純なコンテナクラスです。

public class Container {
    private String className;
    private byte[ ] classFile;

    public Container( String name, byte[ ] file ) {
        className = name;
        classFile = file;
    }

    public String getClassName( ) {
        return className;
    }

    public byte[ ] getClassFile( ) {
        return classFile;
    }
}

スクリプトをコンパイルする


これは、.javaファイルをコンパイルし、それらをScriptオブジェクトにインスタンス化する実際のメソッドです。

private static Script[ ] compileScripts( Container[ ] containers ) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    List< ClassFile > sourceScripts = new ArrayList<>( );
    for ( Container c : containers )
        sourceScripts.add( new ClassFile( c.getClassName( ), c.getClassFile( ) ) );

    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler( );
    JavaFileManager manager = new MemoryFileManager( compiler.getStandardFileManager( null, null, null ) );

    compiler.getTask( null, manager, null, null, null, sourceScripts ).call( );

    List< Script > compiledScripts = new ArrayList<>( );
    for ( Container c : containers )
        compiledScripts.add( ( Script )manager.getClassLoader( null ).loadClass( c.getClassName( ) ).newInstance( ) );

    return ( Script[ ] )compiledScripts.toArray( new Script[ 0 ] );
}

MemoryFileManagerクラス


これは、コンパイラー用に作成したカスタムJavaFileManager実装であり、出力を物理.classファイルではなくメモリーに格納できます。

public class MemoryFileManager extends ForwardingJavaFileManager< JavaFileManager > {
    private HashMap< String, ClassFile > classes = new HashMap<>( );

    public MemoryFileManager( StandardJavaFileManager standardManager ) {
        super( standardManager );
    }

    @Override
    public ClassLoader getClassLoader( Location location ) {
        return new SecureClassLoader( ) {
            @Override
            protected Class< ? > findClass( String className ) throws ClassNotFoundException {
                if ( classes.containsKey( className ) ) {
                    byte[ ] classFile = classes.get( className ).getClassBytes( );
                    return super.defineClass( className, classFile, 0, classFile.length );
                } else throw new ClassNotFoundException( );
            }
        };
    }

    @Override
    public ClassFile getJavaFileForOutput( Location location, String className, Kind kind, FileObject sibling ) {
        if ( classes.containsKey( className ) ) return classes.get( className );
        else {
            ClassFile classObject = new ClassFile( className, kind );
            classes.put( className, classObject );
            return classObject;
        }
    }
}

ClassFileクラス


これは、ソースの.javaファイルとコンパイルされた.classファイルをメモリに格納するために使用する多目的SimpleJavaFileObject実装です。

public class ClassFile extends SimpleJavaFileObject {
    private byte[ ] source;
    protected final ByteArrayOutputStream compiled = new ByteArrayOutputStream( );

    public ClassFile( String className, byte[ ] contentBytes ) {
        super( URI.create( "string:///" + className.replace( '.', '/' ) + Kind.SOURCE.extension ), Kind.SOURCE );
        source = contentBytes;
    }

    public ClassFile( String className, CharSequence contentCharSequence ) throws UnsupportedEncodingException {
        super( URI.create( "string:///" + className.replace( '.', '/' ) + Kind.SOURCE.extension ), Kind.SOURCE );
        source = ( ( String )contentCharSequence ).getBytes( "UTF-8" );
    }

    public ClassFile( String className, Kind kind ) {
        super( URI.create( "string:///" + className.replace( '.', '/' ) + kind.extension ), kind );
    }

    public byte[ ] getClassBytes( ) {
        return compiled.toByteArray( );
    }

    public byte[ ] getSourceBytes( ) {
        return source;
    }

    @Override
    public CharSequence getCharContent( boolean ignoreEncodingErrors ) throws UnsupportedEncodingException {
        return new String( source, "UTF-8" );
    }

    @Override
    public OutputStream openOutputStream( ) {
        return compiled;
    }
}

スクリプトインターフェース


そして最後に、シンプルなスクリプトインターフェイス。

public interface Script {
    public void Begin( ) throws Exception;
}

プログラミングに関してはまだ新しいのですが、私が遭遇した小さな問題のいくつかの解決策を見つけるためにしばらくスタックを使用しました。これは質問をするのが初めてなので、情報が多すぎると謝罪しますまたは、これが長すぎる場合。私は自分が徹底していることを確認したかっただけです。

Michael Borgwardt:

アプリケーションのデフォルトのクラスローダーを使用して、コンパイルされたクラスをロードしているようです。これにより、クラスのガベージコレクションが不可能になります。

そのため、新しくコンパイルしたクラス用に別のクラスローダー作成する必要があります。これがアプリサーバーが行う方法です。

ただし、コンパイルされたクラスに個別のクラスローダーを使用する場合でも、クラスローダーとそれがロードしたすべてのクラスは、単一の限り、ガベージコレクションの対象にならないため、ガベージコレクションでそれらのクラスを取得するのは難しい場合があります。これらのクラスのインスタンスは、他の場所(つまり、アプリケーションの残りの部分)で参照されます。

これは、クラスローダーリーク呼ばれ、アプリサーバーの一般的な問題です。これにより、再デプロイでさらに多くのメモリが使用され、最終的に失敗します。クラスローダーのリークの診断と修正は非常に難しい場合があります。記事にはすべての詳細があります。

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

「アンロードされた」Javaクラスファイル内のクラス、フィールド、およびメソッドレベルのアノテーションを検査する方法はありますか?

分類Dev

スレッドセーフを使用したローカル変数とグローバル変数に対するJavaガベージコレクション

分類Dev

PostmanアプリにPostmanコレクションファイルを自動的にロードする方法

分類Dev

Javaコードを使用してファイルをhdfsにアップロードおよびダウンロードする方法

分類Dev

アプリケーションレベルおよびOSレベルのディレクトリにある.SQLITEデータベースファイルを保護するためのガイド

分類Dev

コルドバAndroidプロダクションリリースにロードするCSSおよびJSファイルを取得できません

分類Dev

ローカル モードでガベージ コレクション情報を有効にするには?

分類Dev

Java Web アプリケーションの起動時に csv ファイルをロードする

分類Dev

psr0およびpsr4自動ロードを使用しないファイルを使用するコンポーザー(symfonyプロジェクト)

分類Dev

Javaで動的にロードおよびアンロード可能なアプリケーションモジュール-どのように?

分類Dev

ズームインおよびズームアウトするためにWebページにボタンを作成する必要があります(アクセシビリティコントロール)(無効化されたフレンドリー)

分類Dev

Javaでメソッドをファイナライズ[ガベージコレクション]

分類Dev

logrotateによるガベージコレクターログ(loggc)ファイルのローテーションが正しく機能しない

分類Dev

Java:ファイルのアップロードおよびダウンロード中にファイル名に特殊文字を含める方法は?

分類Dev

Netsuite Suitelet:ガバナンスの制限に達することなく、トランザクションラインアイテムのリストを反復処理してレコードをロードおよび送信します

分類Dev

コントローラーのアクションごとにcssファイルを動的にロードする方法は?

分類Dev

Javaメジャーおよびマイナーガベージコレクション

分類Dev

どのプログラムまたはアプリケーションがJavaサーバーコード、Androidクライアントコード、および非同期タスクコードをコンパイルできますか?

分類Dev

アップロードコレクションでファイルをクリック可能にするにはどうすればよいですか?

分類Dev

Java、Hibernate、CascadeTypesおよび「ガベージコレクション」孤児

分類Dev

GoおよびReactでPDFファイルジェネレーターからクライアントローカルにファイルをダウンロードする方法

分類Dev

認証されたユーザーの名前、IPアドレス、およびHTTPフィルターから呼び出されているコントローラーアクションを取得するにはどうすればよいですか?

分類Dev

それは、Java 9で動的にJDKおよびカスタムモジュールをロードし、アンロードすることは可能ですか?

分類Dev

ガベージコレクションのロード画面

分類Dev

Cloud FirestoreデータベースコレクションをJSONまたはCSVファイルにダウンロードする方法は?

分類Dev

IOwinRequestからアップロードされたファイルのコレクションを取得するにはどうすればよいですか?

分類Dev

フローティングアクションボタン、スワイプリフレッシュレイアウトとコーディネーターレイアウト、およびツールバーレイアウト動作を一緒に使用する

分類Dev

Java:暗号化されたデータベース(およびその他の)パスワードをプロパティファイルに保存するための最良のオプションは何ですか?

分類Dev

ASP.NETMVCでIoC / DI、UoW、およびリポジトリパターンを使用せずに、ロジックをコントローラーアクションから「サービスレイヤー」に移動する

Related 関連記事

  1. 1

    「アンロードされた」Javaクラスファイル内のクラス、フィールド、およびメソッドレベルのアノテーションを検査する方法はありますか?

  2. 2

    スレッドセーフを使用したローカル変数とグローバル変数に対するJavaガベージコレクション

  3. 3

    PostmanアプリにPostmanコレクションファイルを自動的にロードする方法

  4. 4

    Javaコードを使用してファイルをhdfsにアップロードおよびダウンロードする方法

  5. 5

    アプリケーションレベルおよびOSレベルのディレクトリにある.SQLITEデータベースファイルを保護するためのガイド

  6. 6

    コルドバAndroidプロダクションリリースにロードするCSSおよびJSファイルを取得できません

  7. 7

    ローカル モードでガベージ コレクション情報を有効にするには?

  8. 8

    Java Web アプリケーションの起動時に csv ファイルをロードする

  9. 9

    psr0およびpsr4自動ロードを使用しないファイルを使用するコンポーザー(symfonyプロジェクト)

  10. 10

    Javaで動的にロードおよびアンロード可能なアプリケーションモジュール-どのように?

  11. 11

    ズームインおよびズームアウトするためにWebページにボタンを作成する必要があります(アクセシビリティコントロール)(無効化されたフレンドリー)

  12. 12

    Javaでメソッドをファイナライズ[ガベージコレクション]

  13. 13

    logrotateによるガベージコレクターログ(loggc)ファイルのローテーションが正しく機能しない

  14. 14

    Java:ファイルのアップロードおよびダウンロード中にファイル名に特殊文字を含める方法は?

  15. 15

    Netsuite Suitelet:ガバナンスの制限に達することなく、トランザクションラインアイテムのリストを反復処理してレコードをロードおよび送信します

  16. 16

    コントローラーのアクションごとにcssファイルを動的にロードする方法は?

  17. 17

    Javaメジャーおよびマイナーガベージコレクション

  18. 18

    どのプログラムまたはアプリケーションがJavaサーバーコード、Androidクライアントコード、および非同期タスクコードをコンパイルできますか?

  19. 19

    アップロードコレクションでファイルをクリック可能にするにはどうすればよいですか?

  20. 20

    Java、Hibernate、CascadeTypesおよび「ガベージコレクション」孤児

  21. 21

    GoおよびReactでPDFファイルジェネレーターからクライアントローカルにファイルをダウンロードする方法

  22. 22

    認証されたユーザーの名前、IPアドレス、およびHTTPフィルターから呼び出されているコントローラーアクションを取得するにはどうすればよいですか?

  23. 23

    それは、Java 9で動的にJDKおよびカスタムモジュールをロードし、アンロードすることは可能ですか?

  24. 24

    ガベージコレクションのロード画面

  25. 25

    Cloud FirestoreデータベースコレクションをJSONまたはCSVファイルにダウンロードする方法は?

  26. 26

    IOwinRequestからアップロードされたファイルのコレクションを取得するにはどうすればよいですか?

  27. 27

    フローティングアクションボタン、スワイプリフレッシュレイアウトとコーディネーターレイアウト、およびツールバーレイアウト動作を一緒に使用する

  28. 28

    Java:暗号化されたデータベース(およびその他の)パスワードをプロパティファイルに保存するための最良のオプションは何ですか?

  29. 29

    ASP.NETMVCでIoC / DI、UoW、およびリポジトリパターンを使用せずに、ロジックをコントローラーアクションから「サービスレイヤー」に移動する

ホットタグ

アーカイブ