Jor*_*dan 12 java memory class loading dynamic
我正在创建一个将长时间运行的Java应用程序,这需要更新的功能而无需关闭.我决定通过以.java文件(从数据库中拉出一个字节数组)的形式加载它来提供这个更新的功能,这些文件在内存中编译并实例化.如果你有更好的方式,我全都耳朵.
我遇到的问题是,当我在人工环境中进行一些测试时,每次加载这些"脚本"的周期内存占用量会略有增加.
注意:这实际上是我第一次使用java做这样或类似的事情.我之前在C#中已经完成了这样的事情,加载和卸载.cs文件,并且还有内存占用问题...解决我将它们加载到一个单独的appdomain中,当我重新编译文件时,我只是卸载了那个appdomain并创建了一个新的一个.
这是我在长时间使用(许多重新编译循环)后用来模拟内存占用的入口方法.我运行这个很短的时间,很快就会吃掉500MB +.
这只是临时目录中的两个虚拟脚本.
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 );
}
}
Run Code Online (Sandbox Code Playgroud)
这是我用来收集脚本文件列表的临时方法.在生产过程中,这些实际上将作为字节数组加载,并带有一些其他信息,例如数据库中的类名.
@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 ] );
}
Run Code Online (Sandbox Code Playgroud)
这是简单的容器类.
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;
}
}
Run Code Online (Sandbox Code Playgroud)
这是编译.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 ] );
}
Run Code Online (Sandbox Code Playgroud)
这是我为编译器创建的自定义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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的多用途SimpleJavaFileObject实现,用于将源.java文件和已编译的.class文件存储在内存中.
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;
}
}
Run Code Online (Sandbox Code Playgroud)
最后是简单的Script界面.
public interface Script {
public void Begin( ) throws Exception;
}
Run Code Online (Sandbox Code Playgroud)
在编程方面,我仍然有点新意,我已经使用堆栈一段时间找到解决我遇到的小问题的方法,这是我第一次提出问题,所以如果我包含太多信息我会道歉或者如果这太长了; 我只是想确保我彻底.
| 归档时间: |
|
| 查看次数: |
2734 次 |
| 最近记录: |