1. 程式人生 > >Java動態編譯優化——提升編譯速度(N倍)

Java動態編譯優化——提升編譯速度(N倍)

一、前言

最近一直在研究Java8 的動態編譯, 並且也被ZipFileIndex$Entry 記憶體洩漏所困擾,在無意中,看到一個第三方外掛的動態編譯。並且編譯速度是原來的2-3倍。原本打算直接用這個外掛,但是發現外掛的編譯原始碼存在我之前已經解決過的記憶體洩漏問題。所以拿其原始碼,進行改善。

 

二、第三方外掛

1、maven配置

我找到的這個第三方編譯外掛有兩個,第一個是:Talismane Utilities ,在maven倉庫中可搜到相關pom的配置:

http://mvnrepository.com/search?q=Talismane+Utilities

這個外掛也能編譯,但是編譯速度和記憶體洩漏問題依然存在(廢棄)

 

第二個外掛是Java Runtime Compiler , 可在Maven倉庫中找到 :   http://mvnrepository.com/artifact/net.openhft/compiler

我使用的版本是最新的2.3.1 ,  進行反編譯後:

 

2、外掛原始碼更改

拿到Java Runtime Compiler外掛的原始碼後,能找到有個CachedCompiler類,我對其compilerFromJava方法進行了更改,加上了編譯options引數。具體程式碼如下:

Map<String, byte[]> compileFromJava(@NotNull String className, @NotNull String javaCode, @NotNull final PrintWriter writer, MyJavaFileManager fileManager) {
        Object compilationUnits;
        if(this.sourceDir != null) {
            String filename = className.replaceAll("\\.", '\\' + File.separator) + ".java";
            File file = new File(this.sourceDir, filename);
            CompilerUtils.writeText(file, javaCode);
            compilationUnits = CompilerUtils.s_standardJavaFileManager.getJavaFileObjects(new File[]{file});
        } else {
            this.javaFileObjects.put(className, new JavaSourceFromString(className, javaCode));
            compilationUnits = this.javaFileObjects.values();
        }
        System.setProperty("useJavaUtilZip", "true");
        List<String> options = new ArrayList<>();
        options.add("-encoding");
        options.add("UTF-8");
        options.add("-classpath");
        //獲取系統構建路徑
        options.add(buildClassPath());
        //不使用SharedNameTable (jdk1.7自帶的軟引用,會影響GC的回收,jdk1.9已經解決)
        options.add("-XDuseUnsharedTable");
        options.add("-XDuseJavaUtilZip");

        boolean ok = CompilerUtils.s_compiler.getTask(writer, fileManager, new DiagnosticListener<JavaFileObject>() {
            public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
                if(diagnostic.getKind() == Kind.ERROR) {
                    writer.println(diagnostic);
                }

            }
        }, options, (Iterable)null, (Iterable)compilationUnits).call().booleanValue();
        Map<String, byte[]> result = fileManager.getAllBuffers();
        if(!ok) {
            if(this.sourceDir == null) {
                this.javaFileObjects.remove(className);
            }

            return Collections.emptyMap();
        } else {
            return result;
        }
    }

3、具體編譯時測試類

利用原來的測試類,以10萬個編譯測試為例,進行測試,編譯速度提升N倍,同時記憶體溢位問題也僅存在ZipFIleIndex

package com.yunerp.web.util.run.compile;

import com.yunerp.web.util.run.WebInterface;
import com.yunerp.web.util.run.dynamic.compiler.CompilerUtils;

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

public class DynaCompTest{

    public static Map<String,Object> map = new HashMap<>();


    public static void main(String[] args) throws Exception {

        String  code = "import java.util.HashMap;\n" +
                "import com.yunerp.web.vaadin.message.alert;\n" +
                "import java.util.List;\n" +
                "import java.util.ArrayList;\n" +
                "import com.yunerp.web.vaadin.util.modularfuntion.base.BaseUtil;\n" +
                "import com.yunerp.web.vaadin.util.function.TableFuntionUtil;\n" +
                "import com.yunerp.web.vaadin.util.modularfuntion.stoUtil.StoUtil;\n" +
                "import java.util.Map;import com.yunerp.web.vaadin.util.modularfuntion.user.mini.HomePageUtil;\n" +
                "import com.yunerp.web.util.run.WebInterface;\n" +
                "\n" +
                "public class web2905763164651825363 implements WebInterface {\n" +
                " public  Object execute(Map<String,Object> param) {\n" +
                " System.out.println(param.get(\"key\")+ \"次測試編譯\");" +
                "  return null;\n" +
                " }\n" +
                "}";
        String name = "web2905763164651825363";

        for(int i=0;i<100000;i++){
            long time1 = System.currentTimeMillis();
            DynamicEngine de = new DynamicEngine();
            try {
//                Class cl = de.javaCodeToObject(name,code);
                Class cl = CompilerUtils.CACHED_COMPILER.loadFromJava(name, code);
                if (cl==null){

                    System.out.println("編譯失敗/類載入失敗");
                    continue;
                }
                WebInterface webInterface = (WebInterface)cl.newInstance();
                Map<String,Object> param = new HashMap<>();
                param.put("key",i);
                webInterface.execute(param);
            }catch (Exception e) {
                e.printStackTrace();
            }
            long time2 = System.currentTimeMillis();
            System.out.println("次數:"+i+"            time:"+(time2-time1));
        }

    }

}

4、編譯速度對比

之前的編譯程式碼編譯速度:

 

使用更改後的第三方編譯程式碼編譯速度如下:

 

注: 因為之前的就存在ZipFileIndex問題,更改後的編譯原始碼也只是提升編譯速度,ZipFileIndex記憶體洩漏的問題仍然存在,目前唯一的解決方案是升級Java8 到 Java10