如何在java中将外部源代码传递给线程?

如何在java中将外部源代码传递给线程?,java,jvm,classloader,Java,Jvm,Classloader,我的意思是,我需要将源代码作为输入传递给线程,让线程在源代码已经运行时编译并运行源代码 如何将源代码传递给mapperWordCount类中的map函数,当整个项目的代码运行时,我的程序需要从scanner中获取mapper和reducer代码,并且代码必须放在map函数和reducer函数中,您可以查看代码 所谓线程,我指的不是java程序的主线程 class mapper extends Thread{ // some code @Override public

我的意思是,我需要将源代码作为输入传递给线程,让线程在源代码已经运行时编译并运行源代码

如何将源代码传递给mapperWordCount类中的map函数,当整个项目的代码运行时,我的程序需要从scanner中获取mapper和reducer代码,并且代码必须放在map函数和reducer函数中,您可以查看代码

所谓线程,我指的不是java程序的主线程

class mapper extends Thread{

    // some code 

    @Override
    public void run() {
        mapper.map(bucket,reader);
    }
}
首先是一个大警告:您必须确定要编译的代码来自何处,因为这为代码注入创造了巨大的潜力

另一个警告:如果你编译类,你不能一次又一次地重用同一个类名,这样你的类加载器最终会在OOME之前耗尽你所有的RAM!光是这个原因就应该让你找到另一种方法!编辑:自Java8以来,存储类元数据的PermGen空间不再存在,而是元空间。区别之一是元空间是垃圾收集,而PermGen不是;因此,它应该可以减少OOME的可能性

如果您只是在寻找一种向程序中添加更动态配置的方法,我强烈建议您看看,如果您至少使用Oracle的HotSpot JVM,它支持的ECMAScript与JavaScript非常接近。它将为您节省编写自己的类加载器和编译代码的开销

这就是说,如果您仍然想继续朝这个方向发展,我只是猜测在这里给一个headstart,但是您可能必须通过一个对象编译一个文件,并将内容注入到一个新的应用程序中

如果我有一台计算机要测试,我会尝试以下方法:

Files[] files = ... ; // input for first compilation task
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(files));
compiler.getTask(null, fileManager, null, null, null, compilationUnits).call().get(); // Compile the class and wait for it to finish
Class<?> cls = null;
for (JavaFileObject compiledClass : compilationUnits) {
    if (compiledClass.getKind() != Kind.CLASS)
        continue;
    int n;
    byte[] classData;
    try (InputStream is = compiledClass.openInputStream()) {
        classData = new byte[1024]; // DO A PROPER FULL READ HERE!
        n = is.read(classData);
    } // catch Exceptions here
    cls = myClassLoader.defineClass(className, classData, 0, n);
    break;
}
if (cls != null) {
    // Now, cls.newInstance() etc.
}
除了在黑暗中完全尝试之外,您还必须定义自己的类加载器,因为该方法受到保护


或者,您也可以直接从生成的.class中使用并读取字节码字节来生成对javac的调用,或者甚至可以直接将其生成到类路径中。

您看过API吗?那么您是说希望在运行时编译代码,希望成功,然后以某种方式强制类加载器加载它?头脑正常的人会想这么做吗?@jbx是的,它是一个类加载器;现在我想知道:如果你能在一个类路径目录中生成一个.class文件呢?我猜这可能取决于类加载器……您知道大多数Java安装只有JRE而没有编译器吗?人们在20世纪60年代停止编写自修改应用程序。你将面临的一个大问题是不能重用类名,因此无法从每次编译中释放内存,最终导致OOME。如果你创建一个新的类加载器,然后在完成后将其丢弃,OOME问题不应该发生。@Slaw你是说当它被垃圾收集时?我不确定它是否会释放加载的类元数据。在Java8之前,它不会permgen而不是GC'd。来自Java8,也许…很有趣。看起来permgen在Java8中被删除了,至少在HotSpot中被metaspace替换了。据我所知,metaspace似乎可以在需要时通过配置内存限制对加载程序/类进行GC;如果配置正确,即使是permgen也应该如此。当然,无论是使用permgen还是metaspace,您都必须确保删除所有引用并避免类加载器泄漏。@Slaw是的,如果OP胆敢用他的用例来启发我们,这听起来像是有很多值得怀疑的工作。。。
class reducer extends Thread{

    // some code 

    @Override
    public void run() {
        Reducer.reduce(bucket,reader);
    }
}
class reducerWordCount implement Reducer {
    public void reduce ( Bucket bucket , MapOfKeysAndLists keyListOfKeysAndValues){
        // code for reducer , this code must be entered by scanner as input , and compiled and run while the project is running 
    }
}
Files[] files = ... ; // input for first compilation task
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(files));
compiler.getTask(null, fileManager, null, null, null, compilationUnits).call().get(); // Compile the class and wait for it to finish
Class<?> cls = null;
for (JavaFileObject compiledClass : compilationUnits) {
    if (compiledClass.getKind() != Kind.CLASS)
        continue;
    int n;
    byte[] classData;
    try (InputStream is = compiledClass.openInputStream()) {
        classData = new byte[1024]; // DO A PROPER FULL READ HERE!
        n = is.read(classData);
    } // catch Exceptions here
    cls = myClassLoader.defineClass(className, classData, 0, n);
    break;
}
if (cls != null) {
    // Now, cls.newInstance() etc.
}