有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

Java VM是否有缺陷,或者为什么反复使用CompliationTask和反射会导致软件缺陷?

问题已解决:以下代码在Java 1.8.232中仍然存在缺陷,但在Java 13.0.1中具有正确的行为。也许OpenJDK的人可以研究这个问题,因为Java 8仍然受支持?还不知道是什么版本解决了Java8和Java13之间的这个问题

Java虚拟机有缺陷吗?或者为什么重复使用Java CompilationTask、ToolProvider。getSystemJavaCompiler和反射导致软件缺陷

以下代码已从原始代码基线(https://github.com/peytonc)中缩减,并使用了运行时代码生成、运行时编译和反射API

在下面的软件中,重复调用Java编译器(在运行时编译动态代码)和反射(调用动态代码)会出现缺陷。起初,所有调用都会执行正确的计算,但经过一段较长的时间后,计算会变得不正确。当软件按预期运行时,JVM只保留每个唯一包/类的一个实例(在我看来这是正确的)。但是,当缺陷发生时,JVM有两个或多个相同包/类的副本(在我看来这是不正确的)

在我的计算机上[Intel NUC,Fedora 31,OpenJDK运行时环境(build 1.8.0_232-b09)],下面的代码生成以下结果。迭代0-913都显示正确的行为,而迭代914显示第一个有缺陷的行为

INFO: Iteration #0: (c0) (i0) (c1) (i1) (c2) (i2) (c4) (i4) (c3) (i3) (c5) (i5) (c6) (i6) (c7) (i7) (c8) (i8) (c9) (i9)

INFO: Iteration #1: (c0) (i0) (c1) (i1) (c4) (i4) (c5) (c6) (i5) (i6) (c7) (i7) (c8) (i8) (c9) (i9) (c3) (i3) (c2) (i2)

INFO: Iteration #2: (c3) (i3) (c1) (i1) (c2) (i2) (c0) (c4) (i4) (i0) (c5) (i5) (c7) (c8) (i7) (c9) (i9) (c6) (i6) (i8)

...

INFO: Iteration #913: (c0) (i0) (c2) (i2) (c3) (i3) (c4) (c5) (i4) (i5) (c1) (c6) (i1) (i6) (c7) (i7) (c8) (i8) (c9) (i9)

INFO: Iteration #914: (c0) (c1) (c3) (i3) (i0) (c2) (i2) (i1)

ERROR: On iteration #914 id #4, actualResultsFromProgram != expectedResultFromProgram, 4!=5

ERROR: On iteration #914 id #5, actualResultsFromProgram != expectedResultFromProgram, 5!=6

ERROR: On iteration #914 id #6, actualResultsFromProgram != expectedResultFromProgram, 6!=7

ERROR: On iteration #914 id #7, actualResultsFromProgram != expectedResultFromProgram, 7!=8

ERROR: On iteration #914 id #8, actualResultsFromProgram != expectedResultFromProgram, 8!=9

ERROR: On iteration #914 id #9, actualResultsFromProgram != expectedResultFromProgram, 9!=10

package minijava;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;

public class Programs {
    public int species = 1;
    private static final JavaCompiler JAVA_COMPILER = ToolProvider.getSystemJavaCompiler();
    private static final int MAX_POPULATION = 10;
    private List<Program> listProgram = new ArrayList<Program>(MAX_POPULATION);
    private static final int AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();
    private long iteration = 0;

    // The compute function will increment the first array value by one
    private static final String sourceCodeTemplate = 
            "package species0.id0; \n" +
            "import java.util.ArrayList; \n" +
            "public class GeneticProgram { \n" +
            "   public static void compute(ArrayList<Long> values00) { \n" +
            "       long value = values00.get(0); \n" +
            "       System.out.print(\" (c\" +  value + \") \"); \n" +
            "       values00.set(0, value + 1); \n" +
            "   } \n" +
            "} \n";

    public Programs() {
        System.out.println(sourceCodeTemplate);

        int errorCode = 0;
        while(errorCode == 0) {
            System.out.print("\nINFO: Iteration #" + iteration + ":");

            errorCode = createPrograms();
            if(errorCode == 0) {
                compilePrograms();
            }
            if(errorCode == 0) {
                executePrograms();
            }
            iteration++;
        }
    }

    public int createPrograms() {
        listProgram.clear();
        for(int index=0; index<MAX_POPULATION; index++) {
            String simulatedRandomSourceCode = replacePackage(sourceCodeTemplate, species, index);
            Program program = new Program(simulatedRandomSourceCode, species, index);
            program.vectors.add(new Long(index));   // this number will be incremented by 1 upon execution of program
            listProgram.add(program);
        }
        return 0;
    }

    public int compilePrograms() { 
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
        for(Program program : listProgram) {
            try (StandardJavaFileManager standardJavaFileManager = JAVA_COMPILER.getStandardFileManager(diagnostics, Locale.ENGLISH, null)) {
                Iterable<Program> javaFileObject = Arrays.asList(program);
                ProgramClassSimpleJavaFileObject programClassSimpleJavaFileObject = null;
                try {
                    programClassSimpleJavaFileObject = new ProgramClassSimpleJavaFileObject(Program.PACKAGE_SPECIES + species + "." + Program.PACKAGE_ID + program.ID + "." + Program.PROGRAM_CLASS);
                } catch (Exception e) {
                    e.printStackTrace();
                    return -1;
                }
                ProgramForwardingJavaFileManager programForwardingJavaFileManager = new ProgramForwardingJavaFileManager(standardJavaFileManager, programClassSimpleJavaFileObject, program.programClassLoader);
                CompilationTask compilerTask = JAVA_COMPILER.getTask(null, programForwardingJavaFileManager, diagnostics, null, null, javaFileObject);
                if (!compilerTask.call()) {
                    System.out.println("\nERROR: compilePrograms compilerTask.call()");
                    return -1;
                }
            } catch (IOException e) {
                e.printStackTrace();
                return -1;
            }
        }
        return 0;
    }

    public int executePrograms() {
        List<CallableMiniJava> listCallable = new ArrayList<CallableMiniJava>(MAX_POPULATION);
        ExecutorService executorService = Executors.newFixedThreadPool(AVAILABLE_PROCESSORS);

        try {
            for(Program program : listProgram) {
                listCallable.add(new CallableMiniJava(program));
            }
            for(CallableMiniJava callableMiniJava : listCallable) {
                executorService.execute(callableMiniJava);
            }
            executorService.shutdown();
            if(!executorService.awaitTermination(1000, TimeUnit.MILLISECONDS)) {
                executorService.shutdownNow();
                int milliseconds = 1000;
                while(!executorService.awaitTermination(1000, TimeUnit.MILLISECONDS)) {
                    milliseconds += 1000;
                    System.out.println("\nINFO: Runaway program for " + milliseconds + " milliseconds");
                }
            }

            for (Program program : listProgram) {
                long actualResultsFromProgram = program.vectors.get(0);
                long expectedResultFromProgram = program.ID + 1;
                if(actualResultsFromProgram != expectedResultFromProgram) {
                    System.out.println("\nERROR: On iteration #" + iteration + " id #" + program.ID + ", actualResultsFromProgram != expectedResultFromProgram, " + actualResultsFromProgram + "!=" + expectedResultFromProgram);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            return -1;
        }
        return 0;
    }

    public static String replacePackage(String source, int species, int packageNumber) {
        return source.replaceFirst("package species[0-9][^;]*;", "package " + Program.PACKAGE_SPECIES + species + "." + Program.PACKAGE_ID + packageNumber + ";");
    }

    public static void main(String[] args) {
        Programs programs = new Programs();
    }
}
package minijava;

import java.net.URI;
import java.util.ArrayList;

import javax.tools.SimpleJavaFileObject;

public class Program extends SimpleJavaFileObject {
    public static final String PROGRAM_CLASS = new String("GeneticProgram");
    public static final String PACKAGE_SPECIES = new String("species");
    public static final String PACKAGE_ID = new String("id");
    public String source;
    public ArrayList<Long> vectors;
    public int species;
    public int ID;
    public ProgramClassLoader programClassLoader = null;

    Program(String source, int species, int ID) {
        super(URI.create("string:///" + PACKAGE_SPECIES + species + '/' + PACKAGE_ID + ID + '/' + PROGRAM_CLASS + Kind.SOURCE.extension), Kind.SOURCE);
        this.source = new String(source);
        this.species = species;
        this.ID = ID;
        vectors = new ArrayList<Long>(1);
        programClassLoader = new ProgramClassLoader(ClassLoader.getSystemClassLoader());
    }

    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
        return source;
    }
}
package minijava;

import java.lang.reflect.Method;
import java.util.ArrayList;

public class CallableMiniJava implements Runnable {
    private Program program = null;
    private Class<?> cls = null;
    private Method method = null;

    public CallableMiniJava(Program program) {
        if(program.vectors != null) {
            this.program = program;
            try {
                cls = program.programClassLoader.loadClass(Program.PACKAGE_SPECIES + program.species + "." + Program.PACKAGE_ID + program.ID + "." + Program.PROGRAM_CLASS);
            } catch (ClassNotFoundException e1) {
                e1.printStackTrace();
            }
            try {
                method = cls.getMethod("compute", ArrayList.class);
            } catch (NoSuchMethodException e1) {
                e1.printStackTrace();
            } catch (SecurityException e1) {
                e1.printStackTrace();
            }
        }
    }

    @Override
    public void run() {
        try {
            method.invoke(null, program.vectors);
            System.out.print(" (i" +  program.ID + ") ");
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}
package minijava;

import java.util.HashMap;
import java.util.Map;

public class ProgramClassLoader extends ClassLoader {

    public Map<String, ProgramClassSimpleJavaFileObject> mapProgramClass = new HashMap<>();

    public ProgramClassLoader(ClassLoader classLoader) {
        super(classLoader);
    }

    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        ProgramClassSimpleJavaFileObject programClassSimpleJavaFileObject = mapProgramClass.get(className);
        if(programClassSimpleJavaFileObject != null) {
            byte[] byteCode = programClassSimpleJavaFileObject.byteArrayOutputStream.toByteArray();
            return defineClass(className, byteCode, 0, byteCode.length);
        } else {
            return super.findClass(className);
        }
    }
}
package minijava;

import javax.tools.SimpleJavaFileObject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;

public class ProgramClassSimpleJavaFileObject extends SimpleJavaFileObject {
    public ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

    public ProgramClassSimpleJavaFileObject(String className) throws Exception {
        super(new URI(className), Kind.CLASS);
    }

    @Override
    public OutputStream openOutputStream() throws IOException {
        return byteArrayOutputStream;
    }
}
package minijava;

import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;

import java.io.IOException;

public class ProgramForwardingJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
    private ProgramClassLoader programClassLoader;
    private ProgramClassSimpleJavaFileObject programClassSimpleJavaFileObject;

    protected ProgramForwardingJavaFileManager(JavaFileManager javaFileManager, ProgramClassSimpleJavaFileObject programClassSimpleJavaFileObject, ProgramClassLoader programClassLoader) {
        super(javaFileManager);
        this.programClassSimpleJavaFileObject = programClassSimpleJavaFileObject;
        this.programClassLoader = programClassLoader;
        this.programClassLoader.mapProgramClass.put(programClassSimpleJavaFileObject.getName(), programClassSimpleJavaFileObject);
    }

    @Override
    public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, Kind kind, FileObject fileObject) throws IOException {
        return programClassSimpleJavaFileObject;
    }

    @Override
    public ClassLoader getClassLoader(Location location) {
        return programClassLoader;
    }
}

共 (0) 个答案