精华内容
下载资源
问答
  • 动态编译的应用场景 Java 6.0之后要引入了动态编译动态编译可以实现:让用户提交一段Java代码,上传到服务器,运行,实现在线评测系统。 Java 6.0之前,可以使用这种方式,达到动态编译的效果: Java 6.0之后,...

    动态编译的应用场景

    Java 6.0之后要引入了动态编译。

    动态编译可以实现:让用户提交一段Java代码,上传到服务器,运行,实现在线评测系统。

    Java 6.0之前,可以使用这种方式,达到动态编译的效果:
    在这里插入图片描述
    Java 6.0之后,使用如下方式,也就是本文要讲的:

    • 使用JavaCompiler动态编译

    在这里插入图片描述

    官方API

    https://docs.oracle.com/en/java/javase/11/docs/api/java.compiler/javax/tools/Tool.html#run(java.io.InputStream,java.io.OutputStream,java.io.OutputStream,java.lang.String...)
    

    在这里插入图片描述

    run

    int run​(InputStream in, OutputStream out, OutputStream err, String... arguments)
    !!这里注意一下String... arguments,可以传进一个String数组,里面好像是所有需要编译的文件路径吧?
    Run the tool with the given I/O channels and arguments. By convention a tool returns 0 for success and nonzero for errors. Any diagnostics generated will be written to either out or err in some unspecified format.
    Parameters:
    in - "standard" input; use System.in if null
    out - "standard" output; use System.out if null
    err - "standard" error; use System.err if null
    arguments - arguments to pass to the tool
    Returns:
    0 for success; nonzero otherwise
    Throws:
    NullPointerException - if the array of arguments contains any null elements.

    动态编译

    动态编译生成一个class文件

    编译前:
    在这里插入图片描述
    编译后:
    在这里插入图片描述代码示例

    下列代码返回值为0,表示编译成功

    package cn.hanquan.dynamic;
    
    import javax.tools.JavaCompiler;
    import javax.tools.ToolProvider;
    
    public class DynamicCompile {
    	public static void main(String[] args) {
    		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    		int result = compiler.run(null, null, null, "c:/picture/Hello.java");
    		System.out.println("result=" + result);
    	}
    }
    
    

    输出

    result=0
    

    提问:如果由于某种原因(如拼接…),需要编译的不是一个文件,而是一个字符串,那么应该如何编译呢?

    • 可以通过IO流操作,将字符串存储成临时文件,然后调用动态编译方法

    动态运行编译好的类

    在这里插入图片描述
    代码示例

    (1)通过Runtime执行

    package cn.hanquan.dynamic;
    
    import java.io.BufferedReader;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    
    import javax.tools.JavaCompiler;
    import javax.tools.ToolProvider;
    
    public class DynamicCompile {
    	public static void main(String[] args) throws Exception {
    		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    		int result = compiler.run(null, null, null, "c:/picture/Hello.java");// 编译
    
    		if (result == 0) {
    			Runtime run = Runtime.getRuntime();// 运行
    			Process process = run.exec("java -cp c:/picture Hello");
    
    			InputStream is = process.getInputStream();// 获取运行后输出结果
    			BufferedReader br = new BufferedReader(new InputStreamReader(is));
    			String info = "";
    			while ((info = br.readLine()) != null) {
    				System.out.println(info);
    			}
    		}
    	}
    }
    

    输出结果

    Hello,World~~
    

    (2)通过反射加载相关类,调用相关方法
    在这里插入图片描述
    注意42行,main方法没有参数,可以不写参数。传String数组的时候,一定要加Object进行转型。
    在这里插入图片描述

    展开全文
  • JIT动态编译技术

    万次阅读 2020-08-25 16:11:47
    JIT动态编译技术 一个Java程序执行的过程,就是执行字节码指令的过程,一般这些指令会按照顺序一条一条指令解释执行,这种就是解释执行,解释执行的效率是非常低下的,因为需要先将字节码翻译成机器码,才能执行。 ...

    JIT动态编译技术

    一个Java程序执行的过程,就是执行字节码指令的过程,一般这些指令会按照顺序一条一条指令解释执行,这种就是解释执行,解释执行的效率是非常低下的,因为需要先将字节码翻译成机器码,才能执行。

    而那些被频繁调用的代码,比如调用次数很高或者for循环次数很多的那些代码,称为热点代码,如果按照解释执行,效率是非常低下的。

    为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器,就称为即时编译器(Just In Time Compiler),简称JIT编译器。这些被编译后的机器码会被缓存起来,以备下次使用,但对于那些执行次数很少的代码来说,这种编译动作就纯属浪费。

    即时编译器类型

    在HotSpot虚拟机中,内置了两个JIT,分别为C1编译器和C2编译器。

    • C1编译器:是一个简单快速的编译器,主要的关注点在于局部性的优化,适用于执行时间较短或对启动性能有要求的程序,例如,GUI应用对界面启动速度就有一定要求,C1也被称为Client Compiler。
    • C2编译器:是为长期运行的服务器端应用程序做性能调优的编译器,适用于执行时间较长或对峰值性能有要求的程序,C2也被称为Server Compiler。

    热点代码

    热点代码有两类:

    1. 被多次调用的方法。
    2. 被多次执行的循环体。

    JIT即时编译后的机器码都会放在CodeCache里,JVM提供了一个参数-XX:ReservedCodeCacheSize用来限制CodeCach的大小。如果这个空间不足,JIT就无法继续编译,编译执行会变成解释执行,性能会降低一个数量级。同时,JIT编译器会一直尝试去优化代码,从而造成了CPU占用上升。

    # java -XX:+PrintFlagsFinal -version | grep ReservedCodeCacheSize
        uintx ReservedCodeCacheSize                     = 251658240                           {pd product}
    java version "1.8.0_151"
    

    -XX:ReservedCodeCacheSize默认大小为240M。

    热点探测

    J9使用过采样的热点探测技术,但是缺点是很难精确的确认一个方法的热度。

    在HotSpot虚拟机中采用基于计数器的热点探测,为每个方法建立一个计数器,用于统计方法的执行次数,如果执行次数超过一定的阈值就认为它是“热点方法”。

    虚拟机为每个方法准备了两类计数器:方法调用计数器和回边计数器(Back Edge Counter)。

    方法调用计数器

    方法调用计数器(Invocation Counter):用于统计方法被调用的次数,默认阈值在客户端模式下是1500次,在服务端模式下是10000次,可通过-XX: CompileThreshold来设定。

    先来看一下运行模式:

    # java -version
    java version "1.8.0_151"
    Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
    Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)
    

    从上面的输出可以看出jdk8中默认的运行模式为Server VM

    再来看一下阈值的默认值:

    # java -XX:+PrintFlagsFinal -version | grep CompileThreshold
         intx CompileThreshold                          = 10000                               {pd product}
    

    回边计数器

    回边计数器(Back Edge Counter):用于统计一个方法中循环体代码执行的次数,在字节码中遇到控制流在循环边界往回跳转的指令称为“回边”(Back Edge)。

    虚拟机运行在服务端模式下,回边计数器的阈值计算公式为方法调用计数器阈值(CompileThreshold)*(OSR 比率(OnStackReplacePercentage)- 解释器监控比率(InterpreterProfilePercentage)/ 100。

    其中OnStackReplacePercentage默认值为140,InterpreterProfilePercentage默认值为33,如果都取默认值,那Server模式虚拟机回边计数器的阈值为10000*(140-33)/100=10700。

    intx CompileThreshold                          = 10000                               {pd product}
    intx InterpreterProfilePercentage              = 33                                  {product}
    intx OnStackReplacePercentage                  = 140                                 {pd product}
    

    建立回边计数器的主要目的是为了触发OSR(On StackReplacement)编译,即栈上编译。在一些循环周期比较长的代码段中,当循环达到回边计数器阈值时,JVM会认为这段是热点代码,JIT编译器就会将这段代码编译成机器码并缓存,在该循环时间段内,会直接将执行代码替换,执行缓存中的机器码。

    分层编译

    在Java8中,默认开启分层编译。可以通过java -version命令查看到当前系统使用的编译模式。

    # java -version
    java version "1.8.0_151"
    Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
    Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)
    

    mixed mode为分层编译。

    使用-Xint参数强制虚拟机运行于只有解释器的编译模式下:

    # java -Xint -version
    java version "1.8.0_151"
    Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
    Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, interpreted mode)
    

    使用-Xcomp参数强制虚拟机运行于只有解释器的编译模式下:

    # java -Xcomp -version
    java version "1.8.0_151"
    Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
    Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, compiled mode)
    

    分层编译根据编译器编译、优化的规模和耗时,划分出5个不同的层次:

    • 第0层:程序纯解释执行,并且解释器不开启性能监控功能(Profiling)。
    • 第1层:使用C1将字节码编译为本地代码,进行简单、可靠的优化,不开启Profiling。
    • 第2层:仍然使用C1编译,仅开启方法调用计数器和回边计数器等Profiling。
    • 第3层:仍然使用C1编译,开启全部Profiling。
    • 第4层:使用C2将字节码编译为本地代码,但是会启用一些编译耗时较长的优化,甚至会根据性能监控信息进行一些不可靠的激进优化。

    在不启用分层编译的情况下,当方法的调用次数和循环回边的次数总和,超过由参数-XX:CompileThreshold指定的阈值时,便会触发即时编译;当启用分层编译时,这个参数将会失效,会采用动态调整的方式进行。

    编译优化技术

    JIT编译运用了一些经典的编译优化技术来实现代码的优化,即通过一些例行检查优化,可以智能地编译出运行时的最优性能代码。

    方法内联

    方法内联的优化行为就是把目标方法的字节码复制到发起调用的方法之中,避免发生真实的方法调用,这样就减少了虚拟机栈中一次栈帧的入栈和出栈。

    C2编译器会在解析字节码的过程中完成方法内联。内联后的代码和调用方法的代码,会组成新的机器码,存放在CodeCache区域里。

    另外,C2支持的内联层次不超过9层,太高的话,CodeCache区域会被挤爆,这个阈值可以通过-XX:MaxInlineLevel进行调整。相似的,编译后的代码超过一定大小也不会再内联,这个参数由-XX:InlineSmallCode进行调整。有非常多的参数,被用来控制对内联方法的选择,整体来说,短小精悍的小方法更容易被优化。

    例如以下方法:

        private int add1(int x1, int x2, int x3, int x4) {
            return add2(x1, x2) + add2(x3, x4);
        }
        private int add2(int x1, int x2) {
            return x1 + x2;
        }
    

    最终会被优化为:

        private int add(int x1, int x2, int x3, int x4) {
            return x1 + x2+ x3 + x4;
        }
    

    下面通过一段代码来演示方法内联的过程:

    public class CompDemo {
        private int add1(int x1, int x2, int x3, int x4) {
            return add2(x1, x2) + add2(x3, x4);
        }
        private int add2(int x1, int x2) {
            return x1 + x2;
        }
    
        public static void main(String[] args) {
            CompDemo compDemo = new CompDemo();
            //方法调用计数器的默认阈值10000次,我们循环遍历超过需要阈值
            for(int i=0; i<1000000; i++) {
                compDemo.add1(1,2,3,4);
            }
    
        }
    }
    

    设置VM参数:-XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining

    • -XX:+PrintCompilation:在控制台打印编译过程信息。
    • -XX:+UnlockDiagnosticVMOptions:解锁对JVM进行诊断的选项参数。默认是关闭的,开启后支持一些特定参数对JVM进行诊断。
    • -XX:+PrintInlining:将内联方法打印出来。

    运行结果如下:

    ...
        159   29 %     4       com.morris.jvm.jit.CompDemo::main @ 10 (32 bytes)
                                  @ 21   com.morris.jvm.jit.CompDemo::add1 (15 bytes)   inline (hot)
                                    @ 3   com.morris.jvm.jit.CompDemo::add2 (4 bytes)   inline (hot)
                                    @ 10   com.morris.jvm.jit.CompDemo::add2 (4 bytes)   inline (hot)
    ...
    

    方法最后面有个hot关键字,说明已经触发了方法内联。

    通过JITWatch工具也能发现触发了方法内联:

    在这里插入图片描述
    在这里插入图片描述
    如果将循环次数减少至1000次,就不会触发方法内联了,修改后的运行结果如下:

    ....
        116   26       3       com.morris.jvm.jit.CompDemo::add2 (4 bytes)
        116   27       3       com.morris.jvm.jit.CompDemo::add1 (15 bytes)
                                  @ 3   com.morris.jvm.jit.CompDemo::add2 (4 bytes)
                                  @ 10   com.morris.jvm.jit.CompDemo::add2 (4 bytes)
    

    热点方法的优化可以有效提高系统性能,一般我们可以通过以下几种方式来提高方法内联:

    • 通过设置JVM参数来减小热点阈值,以便更多的方法可以进行内联,但这种方法意味着需要占用更多地内存。
    • 在编程中,避免在一个方法中写大量代码,习惯使用小方法体。

    锁消除

    在不需要保证线程安全的情况下,尽量不要使用线程安全容器,比如StringBuffer,由于StringBuffer中的append()方法被synchronized关键字修饰,会使用到锁,从而导致性能下降。

    但实际上,在以下代码测试中,StringBuffer和StringBuilder的性能基本没什么区别。这是因为在局部方法中创建的对象只能被当前线程访问,无法被其它线程访问,这个变量的读写肯定不会有竞争,这个时候JIT编译会对这个对象的方法锁进行锁消除。

    public class UnLock {
        public static void main(String[] args) {
            long timeStart1 = System.currentTimeMillis();
            for (int i = 0; i < 10000000; i++) {
                testStringBuffer("aaaaa", "bbbbb");
            }
            long timeEnd1 = System.currentTimeMillis();
            System.out.println("StringBuffer cost: " + (timeEnd1 - timeStart1) + "(s)");
    
            long timeStart2 = System.currentTimeMillis();
            for (int i = 0; i < 10000000; i++) {
                testStringBuilder("aaaaa", "bbbbb");
            }
            long timeEnd2 = System.currentTimeMillis();
            System.out.println("StringBuilder cost: " + (timeEnd2 - timeStart2) + "(s)");
        }
    
        public static void testStringBuffer(String s1, String s2) {
            StringBuffer sb = new StringBuffer();
            sb.append(s1);
            sb.append(s2);
        }
    
        public static void testStringBuilder(String s1, String s2) {
            StringBuilder sd = new StringBuilder();
            sd.append(s1);
            sd.append(s2);
        }
    }
    

    运行结果如下:

    StringBuffer cost: 229(s)
    StringBuilder cost: 190(s)
    

    当我们把锁消除(-XX:-EliminateLocks)关闭后,运行结果如下:

    StringBuffer cost: 570(s)
    StringBuilder cost: 148(s)
    

    锁消除关闭后测试发现性能差别有点大。

    -XX:+EliminateLocks:开启锁消除(jdk1.8 默认开启)。

    -XX:-EliminateLocks:关闭锁消除

    逃逸分析与标量替换

    通过逃逸分析(Escape Analysis),JVM能够分析出一个新对象的使用范围,从而决定是否要将这个对象分配到堆上。

    对象的三种逃逸状态:

    • GlobalEscape(全局逃逸):一个对象的引用逃出了方法或者线程。例如,一个对象的引用是复制给了一个类变量,或者存储在在一个已经逃逸的对象当中,或者这个对象的引用作为方法的返回值返回给了调用方法。
    • ArgEscape(参数逃逸):在方法调用过程中传递对象的引用给调用方法。
    • NoEscape(没有逃逸):该对象只在本方法中使用,未发生逃逸。

    下面用一段代码来说明对象的三种逃逸状态:

    package com.morris.jvm.gc;
    
    /**
     * 演示逃逸分析的三种状态
     * 1. 全局逃逸
     * 2. 参数逃逸
     * 3. 没有逃逸
     */
    public class EscapeStatus {
    
        private B b;
    
        /**
         * 给全局变量赋值,发生逃逸(GlobalEscape)
         */
        public void globalVariablePointerEscape() {
            b = new B();
        }
    
        /**
         * 方法返回值,发生逃逸(GlobalEscape)
         */
        public B methodPointerEscape() {
            return new B();
        }
    
        /**
         * 实例引用传递,发生逃逸(ArgEscape)
         */
        public void instancePassPointerEscape() {
            methodPointerEscape().printClassName(this);
        }
    
        /**
         * 没有发生逃逸(NoEscape)
         */
        public void noEscape() {
            Object o = new Object();
        }
    }
    
    class B {
        public void printClassName(EscapeStatus escapeStatus) {
            System.out.println(escapeStatus.getClass().getName());
        }
    }
    

    可以用JVM参数-XX:+DoEscapeAnalysis来开启逃逸分析,JDK8默认开启。

    逃逸分析的性能测试:

    package com.morris.jvm.gc;
    
    /**
     * 演示逃逸分析的标量替换
     * VM args:-Xmx50m -XX:+PrintGC -XX:-DoEscapeAnalysis --> 682ms+大量的GC日志
     * VM args:-Xmx50m -XX:+PrintGC --> 4ms,无GC日志
     */
    public class EscapeAnalysisDemo {
    
        public static void main(String[] args) {
    
            long start = System.currentTimeMillis();
            for (int i = 0; i < 1_0000_0000; i++) {
                allocate();
            }
            System.out.println((System.currentTimeMillis() - start) + " ms");
        }
    
        private static void allocate() {
            new Person(18, 120.0);
        }
    
        private static class Person {
            int age;
            double weight;
    
            public Person(int age, double weight) {
                this.age = age;
                this.weight = weight;
            }
        }
    
    }
    

    使用jvm参数-Xmx50m -XX:+PrintGC -XX:-DoEscapeAnalysis运行程序耗时682ms,控制台会打印大量的GC日志。

    使用jvm参数-Xmx50m -XX:+PrintGC运行程序耗时4ms,控制台没有打印GC日志,也就是没有发生GC。由此可以发现开启逃逸分析后,对象分配的性能显著提升。

    标量:一个数据无法再分解为更小的数据来表示,Java中的基本数据类型byte、short、int、long、boolean、char、float、double以及reference类型等,都不能再进一步分解了,这些就可以称为标量。

    标量替换:如果一个对象只是由标量属性组成,那么可以用标量属性来替换对象,在栈上分配。

    例如上面的Persion只是由int和double类型的属性构成,可以进行标量替换,替换后变成类似如下的代码:

        private static void allocate() {
            int age = 18;
            double weight = 120.0;
        }
    

    变成上面的代码后,这样基本数据类型就可以在栈上分配了。

    而下面的Person类无法进行标量替换,只能在堆上分配了:

        private static class Person {
            byte[] bytes = new byte[1024]; // 不是标量
            String name;
            int age;
        }
    

    -XX:+EliminateAllocations:开启标量替换(jdk1.8 默认开启)。

    -XX:-EliminateAllocations:关闭标量替换。

    展开全文
  • java动态编译以及动态生成jar文件

    千次阅读 2019-06-07 01:10:37
    java动态编译以及动态生成jar文件 本文主要为将指定java文件编译并生成jar,供其他程序依赖,直接上代码 动态编译java文件 /** * @author zhangchengping * @PackageName:com.demo.wms.tasks * @ClassName:...

    java动态编译以及动态生成jar文件

    本文主要为将指定java文件编译并生成jar,供其他程序依赖,直接上代码

    动态编译java文件

    /**
     * @author zhangchengping
     * @PackageName:com.demo.wms.tasks
     * @ClassName:CompilerUtils
     * @Description: 动态编译java文件
     * @date 2019-06-06 10:37
     * @Version 1.0
     */
    
    import org.apache.commons.lang.StringUtils;
    
    import java.io.*;
    import java.util.Arrays;
    import java.util.List;
    
    import javax.tools.*;
    
    public class CompilerUtils {
        private static JavaCompiler javaCompiler;
        private static String encoding = "UTF-8";
    
        private CompilerUtils() {
        };
    
        private static JavaCompiler getJavaCompiler() {
            if (javaCompiler == null) {
                synchronized (CompilerUtils.class) {
                    if (javaCompiler == null) {
                        //根据JavaCompiler 的获取方式来看,应该是采用了单例模式的,但是这里为了顺便复习一下单例模式,以及确保一下单例吧
                        javaCompiler = ToolProvider.getSystemJavaCompiler();
                    }
                }
            }
    
            return javaCompiler;
        }
    
        /**
         * @Description: 编译java文件
         * @param encoding 编译编码
         * @param jarPath 需要加载的jar的路径
         * @param filePath 文件或者目录(若为目录,自动递归编译)
         * @param sourceDir java源文件存放目录
         * @param targetDir 编译后class类文件存放目录
         * @return boolean
         * @Author zhangchengping
         * @Date 2019-06-06 19:50
         */
        public static void compiler( String filePath, String targetDir, String sourceDir,String encoding, String jarPath)
                throws Exception {
    
            // 得到filePath目录下的所有java源文件
            List<File> sourceFileList = File4ComplierUtils.getSourceFiles(filePath);
    
            if (sourceFileList.size() == 0) {
                // 没有java文件,直接返回
                System.out.println(filePath + "目录下查找不到任何java文件");
                return;
            }
            //获取所有jar
            String jars = File4ComplierUtils.getJarFiles(jarPath);
            if(StringUtils.isBlank(jars)){
                jars="";
            }
            File targetFile = new File(targetDir);
            if(!targetFile.exists())targetFile.mkdirs();
            // 建立DiagnosticCollector对象
            DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
            //该文件管理器实例的作用就是将我们需要动态编译的java源文件转换为getTask需要的编译单元
            StandardJavaFileManager fileManager = getJavaCompiler().getStandardFileManager(diagnostics, null, null);
            // 获取要编译的编译单元
            Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(sourceFileList);
            /**
             * 编译选项,在编译java文件时,编译程序会自动的去寻找java文件引用的其他的java源文件或者class。 -sourcepath选项就是定义java源文件的查找目录, -classpath选项就是定义class文件的查找目录,-d就是编译文件的输出目录。
             */
            //Iterable<String> options =Arrays.asList("-encoding",encoding,"-classpath",jars,"-d", targetDir, "-sourcepath", sourceDir);
            Iterable<String> options =Arrays.asList("-encoding",encoding,"-classpath",jars,"-d", targetDir, "-sourcepath", sourceDir);
            /**
             * 第一个参数为文件输出,这里我们可以不指定,我们采用javac命令的-d参数来指定class文件的生成目录
             * 第二个参数为文件管理器实例  fileManager
             * 第三个参数DiagnosticCollector<JavaFileObject> diagnostics是在编译出错时,存放编译错误信息
             * 第四个参数为编译命令选项,就是javac命令的可选项,这里我们主要使用了-d和-sourcepath这两个选项
             * 第五个参数为类名称
             * 第六个参数为上面提到的编译单元,就是我们需要编译的java源文件
             */
            JavaCompiler.CompilationTask task = getJavaCompiler().getTask(
                    null,
                    fileManager,
                    diagnostics,
                    options,
                    null,
                    compilationUnits);
                // 运行编译任务
            // 编译源程式
            boolean success = task.call();
            for (Diagnostic diagnostic : diagnostics.getDiagnostics())
                System.out.printf(
                        "Code: %s%n" +
                                "Kind: %s%n" +
                                "Position: %s%n" +
                                "Start Position: %s%n" +
                                "End Position: %s%n" +
                                "Source: %s%n" +
                                "Message: %s%n",
                        diagnostic.getCode(), diagnostic.getKind(),
                        diagnostic.getPosition(), diagnostic.getStartPosition(),
                        diagnostic.getEndPosition(), diagnostic.getSource(),
                        diagnostic.getMessage(null));
            fileManager.close();
            System.out.println((success)?"编译成功":"编译失败");
        }
    }
    /**
     * @author zhangchengping
     * @PackageName:com.demo.wms.tasks.web.listener
     * @ClassName:s
     * @Description: 文件处理类
     * @date 2019-06-06 19:07
     * @Version 1.0
     */
    public class File4ComplierUtils {
    
        /**
         * @Description:  获取目录下所有源文件
         * @param sourceFilePath
         * @return java.util.List<java.io.File>
         * @Author zhangchengping
         * @Date 2019-06-06 19:47
         */
        public static List<File> getSourceFiles(String sourceFilePath){
    
            List<File> sourceFileList = new ArrayList<>();
            try {
                getSourceFiles(new File(sourceFilePath),sourceFileList);
            } catch (Exception e) {
                e.printStackTrace();
                sourceFileList = null;
            }
            return sourceFileList;
        }
    
        /**
         * @Description:  获取目录下所有的jar
         * @param sourceFilePath
         * @return java.lang.String
         * @Author zhangchengping
         * @Date 2019-06-06 19:46
         */
        public static String getJarFiles(String sourceFilePath){
    
            String jars = "";
            try {
                getJarFiles(new File(sourceFilePath),jars);
            } catch (Exception e) {
                e.printStackTrace();
                jars = "";
            }
            return jars;
        }
        /**
         * 查找该目录下的所有的java文件
         *
         * @param sourceFile
         * @param sourceFileList
         * @throws Exception
         */
        private static void getSourceFiles(File sourceFile, List<File> sourceFileList) throws Exception {
            if (!sourceFile.exists()) {
                // 文件或者目录必须存在
                throw new IOException(String.format("%s目录不存在",sourceFile.getPath()));
            }
            if (null == sourceFileList) {
                // 若file对象为目录
                throw new NullPointerException("参数异常");
            }
            if (sourceFile.isDirectory()) {// 若file对象为目录
                File[] childrenDirectoryFiles = sourceFile.listFiles(new FileFilter() {
                    @Override
                    public boolean accept(File pathname) {
                        return pathname.isDirectory();
                    }
                });
                for (File file : sourceFile.listFiles()) {
                    if(file.isDirectory()){
                        getSourceFiles(file,sourceFileList);
                    } else {
                        sourceFileList.add(file);
                    }
                }
            }else{
                sourceFileList.add(sourceFile);
            }
        }
    
        /**
         * 查找该目录下的所有的jar文件
         *
         * @param sourceFile
         * @throws Exception
         */
        private static String getJarFiles(File sourceFile,String jars) throws Exception {
            if (!sourceFile.exists()) {
                // 文件或者目录必须存在
                throw new IOException("jar目录不存在");
            }
            if (!sourceFile.isDirectory()) {
                // 若file对象为目录
                throw new IOException("jar路径不为目录");
            }
            if(sourceFile.isDirectory()){
                for (File file : sourceFile.listFiles()) {
                    if(file.isDirectory()){
                        getJarFiles(file,jars);
                    }else {
                        jars = jars + file.getPath() + ";";
                    }
                }
            }else{
                jars = jars + sourceFile.getPath() + ";";
            }
            return jars;
        }
    }

    将编译后的class生成jar文件

    **
     * @author zhangchengping
     * @PackageName:com.demo.wms.tasks.web.listener
     * @ClassName:PackageUtil
     * @Description: 生成jar文件
     * @date 2019-06-06 22:59
     * @Version 1.0
     */
    
    import com.sun.javafx.beans.annotations.NonNull;
    import org.apache.commons.io.FileUtils;
    import org.apache.commons.lang.StringUtils;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.lang.annotation.Target;
    import java.util.jar.JarEntry;
    import java.util.jar.JarOutputStream;
    import java.util.jar.Manifest;
    
    public class CreateJarUtils {
    
        /**
         * @param rootPath    class文件根目录
         * @param targetPath  需要将jar存放的路径
         * @param jarFileName jar文件的名称
         * @Description: 根据class生成jar文件
         * @Author zhangchengping
         * @Date 2019-06-06 23:56
         */
        public static void createTempJar(String rootPath, String targetPath, String jarFileName) throws IOException {
            if (!new File(rootPath).exists()) {
                throw new IOException(String.format("%s路径不存在", rootPath));
            }
            if (StringUtils.isBlank(jarFileName)) {
                throw new NullPointerException("jarFileName为空");
            }
            //生成META-INF文件
            Manifest manifest = new Manifest();
            manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
            //manifest.getMainAttributes().putValue("Main-Class", "Show");//指定Main Class
            //创建临时jar
            File jarFile = File.createTempFile("edwin-", ".jar", new File(System.getProperty("java.io.tmpdir")));
            JarOutputStream out = new JarOutputStream(new FileOutputStream(jarFile), manifest);
            createTempJarInner(out, new File(rootPath), "");
            out.flush();
            out.close();
            //程序结束后,通过以下代码删除生成的jar文件
           /* Runtime.getRuntime().addShutdownHook(new Thread() {
                public void run() {
                    jarFile.delete();
                }
            });*/
            //生成目标路径
            File targetFile = new File(targetPath);
            if (!targetFile.exists()) targetFile.mkdirs();
            File targetJarFile = new File(targetPath + File.separator + jarFileName + ".jar");
            if(targetJarFile.exists() && targetJarFile.isFile())targetJarFile.delete();
            FileUtils.moveFile(jarFile, targetJarFile);
            //jarFile.renameTo(new File(""));
        }
    
        /**
         * @Description: 生成jar文件
         * @param out 文件输出流
         * @param f 文件临时File
         * @param base 文件基础包名
         * @return void
         * @Author zhangchengping
         * @Date 2019-06-07 00:02
         */
        private static void createTempJarInner(JarOutputStream out, File f,
                                               String base) throws IOException {
    
            if (f.isDirectory()) {
                File[] fl = f.listFiles();
                if (base.length() > 0) {
                    base = base + "/";
                }
                for (int i = 0; i < fl.length; i++) {
                    createTempJarInner(out, fl[i], base + fl[i].getName());
                }
            } else {
                out.putNextEntry(new JarEntry(base));
                FileInputStream in = new FileInputStream(f);
                byte[] buffer = new byte[1024];
                int n = in.read(buffer);
                while (n != -1) {
                    out.write(buffer, 0, n);
                    n = in.read(buffer);
                }
                in.close();
            }
        }
    }

    程序调用

    import java.io.File;
    import java.io.FileFilter;
    import java.io.IOException;
    
    import com.demo.wms.tasks.utils.CompilerUtils;
    import com.demo.wms.tasks.utils.CreateJarUtils;
    import org.apache.commons.io.FileUtils;
    
    public class BuildRmi {
        //资源文件根路径
        static String basePath = "E:\\WorkSpace\\demo\\WMS_TASK_NC";
        //生成jar文件路径
        static String jarFilePath = "E:\\WorkSpace\\demo\\rmi-client";
        //需要编译的源文件路径
        static String[] srcFiles = {
                "/src/com/demo/wms/tasks/vo/",
                "/rmi/com/demo/wms/tasks/rmi/",
                "/rmi/com/demo/wms/tasks/rmi/po/",
                "/rmi/com/demo/wms/tasks/rmi/client/"
        };
        static String jarReyOnPath = "E:\\WorkSpace\\demo\\WMS_TASK_NC\\WebRoot\\WEB-INF\\lib";
        static String jarFileName = "rim";
        static String encoding = "utf-8";
    
        public static void main(String[] args) {
            String sourcePath = "";
            String classPath = "";
            try {
                // 将RMI需要使用的JAVA文件拷贝到制定目录中
                System.out.println("分隔符:" + File.separator);
                System.out.println("资源拷贝......");
                sourcePath = jarFilePath + File.separator + "source";
                copySource(sourcePath);//拷贝资源
                System.out.println("资源拷贝结束");
                System.out.println("编译资源......");
                //编译java文件
                classPath = jarFilePath + File.separator + "class";
                try {
                    CompilerUtils.compiler(sourcePath, classPath, basePath, encoding, jarReyOnPath);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("编译资源结束");
                System.out.println("生成jar......");
                //生成jar文件
                CreateJarUtils.createTempJar(classPath, jarFilePath, jarFileName);
                System.out.println("生成jar完成");
                //删除临时文件
                ExeSuccess(sourcePath, classPath);
            } catch (IOException e) {
                e.printStackTrace();
                deleteTempFile(sourcePath, classPath);
    
            } finally {
            }
    
        }
    
        private static void ExeSuccess(String sourcePath, String classPath) {
            final String sourcedir = sourcePath;
            final String classdir = classPath;
            //程序结束后,通过以下代码删除生成的文件
            Runtime.getRuntime().addShutdownHook(new Thread() {
                public void run() {
                    deleteTempFile(sourcedir, classdir);
                    System.out.println("***************执行完毕**********************");
                }
            });
        }
    
        private static void deleteTempFile(String sourcePath, String classPath) {
            //程序结束后,通过以下代码删除生成的class 和java文件
            try {
                File sourceFile = new File(sourcePath);
                if (sourceFile.exists()) {
                    FileUtils.deleteDirectory(sourceFile);
                }
                File classFile = new File(classPath);
                if (classFile.exists()) {
                    FileUtils.deleteDirectory(classFile);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
    
    
        }
    
        private static void copySource(String sourcePath) throws IOException {
            for (String f : srcFiles) {
                String path = f.replace("/", File.separator);
                System.out.println(path);
                File srcFile = new File(basePath + path);
                File targetFile = new File(sourcePath + path);
                FileUtils.copyDirectory(srcFile, targetFile, new FileFilter() {
                    @Override
                    public boolean accept(File pathname) {
                        System.out.println(pathname);
                        return pathname.getName().endsWith(".java");
                    }
                });
            }
        }
    }

     

     

    展开全文
  • 静态编译与动态编译的区别

    千次阅读 2019-04-18 21:23:28
    静态编译与动态编译的区别  动态编译的可执行文件需要附带一个的动态链接库,在执行时,需要调用其对应动态链接库中的命令。所以其优点一方面是缩小了执行文件本身的体积,另一方面是加快了编译速度,节省了系统...

    静态编译与动态编译的区别

      动态编译的可执行文件需要附带一个的动态链接库,在执行时,需要调用其对应动态链接库中的命令。所以其优点一方面是缩小了执行文件本身的体积,另一方面是加快了编译速度,节省了系统资源。缺点一是哪怕是很简单的程序,只用到了链接库中的一两条命令,也需要附带一个相对庞大的链接库;二是如果其他计算机上没有安装对应的运行库,则用动态编译的可执行文件就不能运行。  静态编译就是编译器在编译可执行文件的时候,将可执行文件需要调用的对应动态链接库(.so)中的部分提取出来,链接到可执行文件中去,使可执行文件在运行的时候不依赖于动态链接库。所以其优缺点与动态编译的可执行文件正好互补。

     

     

     

     

    用  静态lib将导出声明和实现都放在lib中。编译后所有代码都嵌入到宿主程序

      动态lib相当于一个h文件,是对实现部分(.dll文件)的导出部分的声明。编译后只是将导出声明部分编译到宿主程序中,运行时候需要相应的dll文件支持

      lib文件是不对外公开的,不能查看一个编译过后的文件

      有几个选择:

      1。如果你查看有同名的dll文件,可以通过vc自带的depends查看dll接口

      2。通过msdn看你使用的该lib包含的函数名,来查找其对应的头文件,头文件里面有整个lib的函数声明(可能不全)

      3。查看vc或者其他工具安装目录下的src目录,查看函数的代码

      lib文件是二进制文件,所以要查看它的内容,只能反汇编。

      用编程语言,打开lib文件的办法有三个:

      1、在object/library modules使用全路径名;

      2、把*.lib放在VC的Lib目录中

      3、修改project setting的Link->Input中的Addtional library path,加入你的目录。

      LIB文件是库文件(与DLL文件相类似),供其它程序调用的,直接打不开。

    编辑本段lib文件操作

      一个lib文件是obj文件的集合。当然,其中还夹杂着其他一些辅助信息,目的是为了让编译器能够准确找到对应的obj文件。我们可以通过tlib.exe(在tc2.0下的根目录)来对lib文件进行操作,你可以把自己生成的obj文件通过tlib命令加入到一个lib文件中,也可以把lib文件内的obj文件进行删除操作,还可以把内部的obj文件给提取出来。明白了lib文件的大致结构以及对它的具体操作,在学习C语言的过程中,就会又多了一个切入点对C语言具体实现进行研究。

      在command下,把当前目录设置为tlib.exe所在目录,然后输入tlib命令回车,此时显示的内容就是对tlib命令的详细解释,语法如下:

      Syntax: TLIB libname [/C] [/E] commands, listfile

      libname library file pathname

      commands sequence of operations to be performed (optional)

      listfile file name for listing file (optional)

      A command is of the form: <symbol>modulename, where <symbol> is:

      + add modulename to the library

      - remove modulename from the library

      * extract modulename without removing it

      -+ or +- replace modulename in library

      -* or *- extract modulename and remove it

      /C case-sensitive library

      /E create extended dictionary

      具体解释:

      tlib libname [/C] [/E] commands, listfile

      /C:大小写敏感标志。该选项不常用,此参数为可选项。

      /E:建立扩展字典。建立扩展字典可以加速大的库文件的连接过程,此参数同样为可选项。

      操作命令(可选项):

      + obj文件名 把指定obj文件添加到lib文件中

      - obj文件名 把指定obj文件从lib文件中删除

      * obj文件名 导出指定的obj文件(导出后对应的obj文件在lib文件内仍然存在)

      -+ obj文件名 替换指定的obj文件(前提是在lib文件中存在与指定obj文件同名的obj)

      -* obj文件名 导出指定的obj文件(导出后把对应的obj文件从lib文件内删除)

      lib文件中obj文件列表(可选项)

      此参数说明了命令运行后,生成的对应lib文件的列表文件名。它记录了当前lib文件内obj文件列表

    编辑本段Lib文件中的节

      Lib格式只有四种类型的节(Section),即First Sec,Second Sec,Longname Sec和Obj Sec;其中Second Sec与Longname Sec是可选节,很多Lib文件中都没有。而开头的Singature只是一个标识,它相当于COFF目标文件中的魔法数字。它是一个长度为8的字符串,值为“!<arch>\n”。

      First Sec 顾名思义,就是第一个节。它包含了库中所有的符号名以及这些符号所在的目标文件在库中的位置(绝对偏移)。

      Second Sec 就是第二节。它的内容和First Sec是相同的。不同的是,Second Sec是一个有序表,通过它来查找库中的符号比通过First Sec来查找要快很多。

      Longname Sec 是长名称节。这一节是一个字符串表。它包含了所有长目标文件名。如果后面的Obj Sec中没有给出相应的目标文件名,我们就要到这一节中来查找。

      Obj Sec 就是目标文件节。这些节中存储着不同的目标文件的原始数据。

      在库文件中,每一节都有两个部分。一个部分是头,另一个部分才是该节的数据;数据紧跟在头的后面。头描述了该节数据的类型、长度等信息。这些头的格式都是相同的。其结构用C语言描述如下:

      typedef struct {

      char Name[16]; // 名称

      char Time[12]; // 时间

      char UserID[6]; // 用户ID

      char GroupID[6]; // 组ID

      char Mode[8]; // 模式

      char Size[10]; // 长度

      char EndOfHeader[2];// 结束符

      } SectionHeader;

      可以看到,头中的数据全都是字符串。用字符串的好处是可以提高格式的兼容性,因为在不同的机器上,数据的排列方式是不同的。有的机器是以Little-Endian方式工作,还有的是以Big-Endian方式工作,它们互不兼容(这两种方式的区别!?请看我的《COFF格式》一文,其中的文件头一节有说明)。用字符串就不会有这种问题(后面我们将会遇到)。但它也有不方便的地方,就是必须把字符串转换成数值,多了一个步骤。

      在这个结构中,最常用的Name、Size以及EndOfHeader三个成员。Name就是节的名称啦!Size也很好理解,就是该节数据的长度。现在要注意的就是这个EndOfHeader成员了!这个成员标志着头的结束,其内容为“`\n”(注意,这里没有打错,是两个字符“`”和“\n”)。怎么样?有点奇怪吧?为什么要有这个结束符?每一节的头长度一定,每节中的数据长度也知道。按顺序向下读不行吗?答案是:不行!因为每一节之间存在间隙!通常是一个字节或零个字节。如果是零个字节倒好,按顺序向下读是OK的。可是如果不为零的话,这样读就要错位了。要知道错位没有,只好用一个结束符来定位了。如果在读头的时候发现结束符不对,那就要一个字节一个字节地向下查找,直到找到结束符,才能算是对齐了。切记!切记!

      当然,通过First Sec或Second Sec中给出的偏移来读数据就不存在这个问题。不会发生错位,放心读吧!

      现在让我们来看看每一节中的数据是什么样子。

    First Sec

      第一节,通常就是Lib中的每一个小节。它的名称是“/”。其数据部分的结构如下:

      typedef struct {

      unsigned long SymbolNum; // 库中符号的数量

      unsigned long SymbolOffset[n]; // 符号所在目标节的偏移

      char StrTable[m]; // 符号名称字符串表

      }FirstSec;

      第一个成员SymbolNum是符号的数量。注意!它是以Big-Endian方式储存的(x86平台上的数据是以Little-Endian方式储存的。这里应该注意转换。后面给出的convert函数可以在Little-Endian格式与Big-Endian格式之间进行相互转换)。

      第二个成员SymbolOffset是一个数组,它的长度n就是符号的数量,也就是SymbolNum。这个数组储存了每一个符号所在的目标节的偏移。我们可以方便地通过它来查找符号所在的目标文件。注意!它也是以Big-Endian格式储存的。

      第三个成员StrTable是一个字符串表,它的长度m就是SectionHeader.Size的值减去(SymbolNum+1)*4。其结构很简单,就是一堆以‘\0’结尾的字符串(和COFF文件中的字符串表结构相同)。在有的系统中,它还可能是以“/\n”这两个字符结尾的字符串的集合。

      很简单的一个结构,不过有两个成员的长度是不定的。怎么才能方便地从Lib中读出这些数据,留给大家自己想吧!下面我只给出一个进行Little-Endian与Big-Endian互转的函数。

      inline void convert(void * p // 要转换的数据的指针

      ,size_tsize = 4 // 数据的长度,long为4,short为2

      ) {

      char * buf=(char*)p;

      char temp;

      for ( size_t i=0;i<size/2;i++ ) {

      temp=buf[i];

      buf[i]=buf[size-i-1];

      buf[size-i-1]=temp;

      }

      }

    Second Sec

      第二节

      这一节与第一节很相似!它通常也就是Lib文件的第二个节。它的名字也是“/”(注意:文件中第一个叫“/”的节是第一节,第二个就是第二节)。不过它的结构与第一节有些不同,如下:

      typedef struct {

      unsigned long ObjNum; // Obj Sec的数量

      unsigned long ObjOffset[x]; // 每一个Obj Sec的偏移

      unsigned long SymbolNum; // 库中符号的数量

      unsigned short SymbolIdx[n]; // 符号在ObjOffset表中的索引

      char StrTable[m]; // 符号名称字符串表

      }SecondSec;

      第一个成员ObjNum是库中Obj Sec的数量。

      第二个成员ObjOffset是一个偏移表,它记录了库中所有Obj Sec的偏移。这个表的记录数x就是ObjNum。

      第三个成员SymbolNum与First Sec中的SymbolNum意义相同。

      第四个成员SymbolIdx变成了一个索引,它记录了相应名称字符串在ObjOffset这个表中的位置,我们要通过两次索引才能找到我们所要符号的Obj Sec位置。它的项目数n为SymbolNum。但请注意,这个索引是unsigned short型,不再是unsigned long型。

      第五个成员StrTable结构与First Sec中的一样。不过,它的长度m为SectionHeader.Size的值减去((ObjNum+1)*4+(SymbolNum+2)*2)。

      值得注意的是,这里的所有数据都是Little-Endian格式的。千万不要弄错了!Longname Sec

      这个小节就是一个字符串表,它的名称为“//”,其结构同FirstSec.StrTable。这里就不多说了。

      Obj Sec

      这一节中的数据就是COFF文件的原始数据,把它读出来存成文件,就是一个COFF文件。它的格式请参考《COFF格式》一文。

      要指出的是它的命名方式有些特殊。如果Obj文件的名称少于16个字符,它就会被保存在SectionHeader的Name成员中,以‘/’字符结尾。如果无法保存在Name成员中,则Name成员的第一个字符就为‘/’,之后再跟上这个名称在Longname Sec中的偏移。

      例如:

      !<arch>\n

      ……

      LongName Sec:

      This_Is_Long_Name0001\0

      This_Is_Long_Name0002\0

      ……

      Obj Sec1:

      Name[16]:“shortname/”

      ……

      Obj Sec2:

      Name[16]:“/0” // 这里使用了第一个长文件名This_Is_Long_Name0001

      ……

      Obj Sec3:

      Name[16]:“/22” // 这里使用了第二个长文件名This_Is_Long_Name0002

    lib文件与dll

      (1)lib是编译时需要的,dll是运行时需要的。

      如果要完成源代码的编译,有lib就够了。

      如果也使动态连接的程序运行起来,有dll就够了。

      在开发和调试阶段,当然最好都有。

      (2)一般的动态库程序有lib文件和dll文件。lib文件是必须在编译期就连接到应用程序中的,而dll文件是运行期才会被调用的。如果有dll文件,那么对应的lib文件一般是一些索引信息,具体的实现在dll文件中。如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。静态编译的lib文件有好处:给用户安装时就不需要再挂动态库了。但也有缺点,就是导致应用程序比较大,而且失去了动态库的灵活性,在版本升级时,同时要发布新的应用程序才行。

      (3)在动态库的情况下,有两个文件,一个是引入库(.LIB)文件,一个是DLL文件,引入库文件包含被DLL导出的函数的名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。从上面的说明可以看出,DLL文件必须随应用程序一起发行,否则应用程序将会产生错误。

    加载LIB文件方法

    直接加入

      在VC中打开File View一页,选中工程名,单击鼠标右键,然后选中"Add Files to Project"菜单,在弹出的文件对话框中选中要加入DLL的LIB文件即可。

    设置工程的 Project Setting

      打开工程的 Project Settings菜单,选中Link,然后在Object/library modules下的文本框中输入DLL的LIB文件。

    通过程序代码

      加入预编译指令#pragma comment (lib,"*.lib"),这种方法优点是可以利用条件预编译指令链接不同版本的LIB文件。因为,在Debug方式下,产生的LIB文件是Debug版本,如Regd.lib;在Release方式下,产生的LIB文件是Release版本,如Regr.lib。

      当应用程序对DLL的LIB文件加载后,还需要把DLL对应的头文件(*.h)包含到其中,在这个头文件中给出了DLL中定义的函数原型,然后声明。

    展开全文
  • 最近一直在研究Java8 的动态编译, 并且也被ZipFileIndex$Entry 内存泄漏所困扰,在无意中,看到一个第三方插件的动态编译。并且编译速度是原来的2-3倍。原本打算直接用这个插件,但是发现插件的编译源码存在我之前...
  • java动态编程-动态编译机制

    千次阅读 2016-05-24 09:49:55
    java可以在运行时,进行动态编译,也就是说,可以在代码中,由程序控制进行编译俩种动态编译方法: 1.执行cmd命令,调用javac进行编译 2.利用java提供的JavaComplier 3.可以利用类加载器还有java反射来执行编译好...
  • Java动态编译

    千次阅读 2015-11-13 16:14:02
    程序产生过程下图展示了从源代码到可运行程序的过程,正常情况下先编译(明文源码到字节码)...这就是我们下面的主题,动态编译。 相关类介绍JavaCompiler: 负责读取源代码,编译诊断,输出class JavaFileObject: 文
  • 静态编译和动态编译的区别

    千次阅读 2018-04-28 11:02:44
    静态编译:在编译时确定类型,绑定对象,即... 动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。 java中的反射就是运用了动态编译创建对象。...
  • Java动态编译和动态加载详解

    千次阅读 2018-10-15 13:58:43
    动态编译 在某些情况下,我们需要动态生成java代码,通过动态编译,然后执行代码。JAVA API提供了相应的工具(JavaCompiler)来实现动态编译。 //获取JavaCompiler JavaCompiler compiler = ToolProvider....
  • Java动态编译执行

    万次阅读 2017-01-05 16:18:02
    在某些情况下,我们可能需要动态生成java代码,通过动态编译,然后执行代码。JAVA API提供了相应的工具(JavaCompiler)来实现动态编译。下面我们通过一个简单的例子介绍,如何通过JavaCompiler实现java代码动态编译...
  • cef库动态编译

    千次阅读 2016-04-14 15:06:31
    CEF动态编译
  • 动态编译、静态编译区别(转)

    千次阅读 2018-08-07 12:06:28
    1.静态编译:编译器在编译可执行文件时,把需要用到的对应动态链接库(.so或.ilb)中的...2.动态编译: 动态编译的可执行文件需要附带一个的动态链接库,在执行时,需要调用其对应动态链接库中的命令。所以其优点一 ...
  • 开心一笑【年底是各种案件的高发期,我们去ATM取钱的时候,一定要注意遮挡,不要被陌生人看到你的余额,要不然啊,就,,,非常容易被人嘲笑。... 】提出问题java中的动态编译和静态编译如何理解???解
  • Java动态编译和运行

    千次阅读 2018-01-05 08:46:49
    Java在进行动态编译的时候需要用到tools.jar资源包,此包在jdk\lib目录中。若tools.jar不存在则会出现进行编译时提示空指针异常: 对于缺少jar包,首先想到的解决办法是找到需要的jar包并且将其加入到build...
  • busybox动态编译静态编译的区别

    千次阅读 2017-01-14 14:56:39
    busy box选择静态编译的话,命令和工具集不需要动态加载lib...为了能够执行动态链接的程序,你把arm-linux-gcc里面的库文件加进去就可以了busybox选择动态编译的话,命令和工具集需要加载lib里面的库,所以在制作根文件
  • vue的动态编译

    千次阅读 2018-12-28 17:11:38
    动态编译的需求: 如在写一个表格组件时,我们可能需要表格的某一列是可定制化的,比如这一列可实现点击响应一个事件,要实现这样一个需求,可通过写配置文件的方式实现 实现方式1 data(){  let me = this  ...
  • 一、动态编译案例 要说动态编译内存泄漏,首先我们先看一个案例(网上搜动态编译的资料是千篇一律,只管实现功能,不管内存泄漏,并且都恬不知耻的标识为原创!!) Java URLClassLoader 动态编译案例:...
  • 1.静态编译:编译器在编译可执行文件时,把需要用到的对应动态链接库(.so或.ilb)中的部分...2.动态编译: 动态编译的可执行文件需要附带一个的动态链接库,在执行时,需要调用其对应动态链接库中的命令。所以其优点一
  • JVM调优——Java动态编译过程中的内存溢出问题

    千次阅读 多人点赞 2018-12-06 17:39:22
    由于测试环境项目每2小时内存就溢出一次, 分析问题,发现...一、找到动态编译那块的代码,具体如下 /** * @MethodName : 编译java代码到Object * @Description * @param fullClassName 类名 * @param javaCod...
  • 1.Dll(Dynamic Link Library)和Lib文件 2.动态编译与静态编译的区别 3.opencv项目的动态编译 4.opencv项目的静态编译 参考链接
  • java实现动态编译的几种方法

    万次阅读 2018-09-19 09:05:00
    所谓动态编译,就是在程序运行时产生java类,并编译成class文件。    在D盘test目录下有两个java文件:AlTest1.java、AlTest2.java,现需要通过java代码实现java文件到class文件的编译操作: import java.io....
  • 使用Roslyn动态编译和执行

    千次阅读 2014-09-19 16:37:40
    使用Roslyn动态编译和执行
  • 动态编译 标准编程语言 对于 C 语言,代码一般要先编译,再执行。 .c -> .exe 解释器语言 shell 脚本 .sh -> interpreter Byte Code 编译 Python, Java 等语言先将代码编译为 byte code(不是机器码),然后...
  • Java动态编译JavaCompiler

    千次阅读 2018-10-27 19:52:16
    Java在进行动态编译的时候需要用到tools.jar资源包,此包在jdk\lib目录中。若tools.jar不存在则会出现进行编译时提示空指针异常: 对于缺少jar包,首先想到的解决办法是找到需要的jar包并且将其加入到buildpath,...
  • apache动态编译和静态编译问题

    千次阅读 2013-04-08 18:17:04
    编译和安装apache分为动态、静态两种...动态编译是指在以后的使用中随时调整配置文件就可以加载模块;静态则相反,在编译时就决定了相应的模块。 举例: 以mod_rewrite模块为例: 错误方式: ./configure

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 818,426
精华内容 327,370
关键字:

动态编译