• 精简JRE的思路初探


    引言: JRE是Java程序赖以执行的基础环境,眼下JRE已经很的庞大;即使为了执行一个简单的Hello World的程序。可能依旧须要依赖整个JRE,将近百兆大小的依赖性。

    能否够对特定Java程序依赖的JRE进行精简呢? 当然是能够。依据当前代码的须要,动态精简JRE,仅仅依赖须要的class。而非所有。

    1.  总体的思路

      a.  首先找到当前Java程序依赖的全部class,包含自身类库/第三方类库,以及JRE中的类库。

      b.   将JRE中不须要的类库文件移除掉,仅仅保留须要的类库。

      C.   将保留下的类库又一次打包。替换已有的JRE文件

    2.  寻找所需的JRE中的类库文件

      在执行Java应用的过程中,能够针对JVM加入參数[-XX:+TraceClassLoading],则应用在启动过程中,会将全部的所需的class打印到控制台。  

     在上述的样例中,就列出全部依赖的类库。

    2. 怎样提取须要的类库或者移除不须要的类库

     这里我们採用前者,仅仅提取所需的类库。

    jar xvf xxx.jar classname1 classname2 ....
      这个命令就会把须要的class从jar中提取出来,拷贝到本地当前文件夹。

    3.  将这些类库进行打包,替换掉JRE中相应的类库

    jar cvf target.jar sourcefolder1 classfolder2 ...
       打包命令,将classfolder中的类库,打包为target.jar.

    4. 那代码上怎样。利用上述的仅仅是完毕自己主动化打包 JRE相应的类库呢?

    方案例如以下:

    4.1. 基于执行过程中的Java參数-XX:+TraceClassLoading,打印出所用在JRE中用到的java类

    4.2. 捕获从控制台输出的class列表

    4.3. 利用jar自带的功能。从rt.jar中提取对应的所须要的class

    4.4. 将rt.jar中提取的class进行打包,就可以得到所需的jre核心jar包。

    代码假定的前提:

        1. Jre所在的路径

        2. 目标java类已经编译成class.这里未考虑动态编译的情况

       3.  将jre中的rt.jar打包在当前路径。

          演示样例代码例如以下:

    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.LineNumberReader;
    import java.util.ArrayList;
    import java.util.List;
    
    public class RunClass {
    
    	public static void main(String[] args) throws IOException {
    		List<String> classes = new ArrayList<String>();
    
    		String[] cmdB = { "java", "-XX:+TraceClassLoading", "MainTest" };
    		Runtime runtime = Runtime.getRuntime();
    
    		Process process = Runtime.getRuntime().exec(cmdB);
    		// /process = Runtime.getRuntime().exec(cmdB);
    		LineNumberReader br = new LineNumberReader(new InputStreamReader(
    				process.getInputStream()));
    		StringBuffer sb = new StringBuffer();
    		String line;
    		while ((line = br.readLine()) != null) {
    			String className = RunClass.parseClass(line);
    			if (className.length() > 0) {
    
    				sb.append(className.replace(".", "/")).append(" ");
    				classes.add(className.replace(".", "/"));
    			}
    		}
    
    		System.out.println("classes to be packed in size:" + classes.size());
    
    		classes.add(0, "/opt/jdk7/jre/lib/rt.jar");
    		classes.add(0, "xvf");
    		classes.add(0, "jar");
    
    		Process jarClass = runtime.exec((String[]) classes
    				.toArray(new String[classes.size()]));
    		LineNumberReader br1 = new LineNumberReader(new InputStreamReader(
    				jarClass.getInputStream()));
    		StringBuffer sb1 = new StringBuffer();
    		String line1;
    		while ((line1 = br.readLine()) != null) {
    			System.out.println("extracting:" + line1);
    		}
    		System.out.println(classes.size()
    				+ " classes have been extracted successfully");
    
    		String[] cmdJarPackage = { "jar", "cvf", "rt.jar", "com", "java",
    				"javax", "META-INF", "org", "sun", "sunw" };
    		Process jarProcess = runtime.exec(cmdJarPackage);
    
    		System.out
    				.println("Jar the classes into a package rt.jar successfully.");
    
    	}
    
    	public static String parseClass(String lineStr) {
    		String keyStr = "";
    
    		if (lineStr.startsWith("[Loaded")) {
    			String[] keys = lineStr.split(" ");
    
    			if (keys.length == 4) {
    				keyStr = keys[1];
    			}
    		}
    
    		return keyStr;
    	}
    
    }
    

    5. 总结

     JRE在jDK8中已经对其进行了模块化设计,从而使按需载入和定制JRE成为可能。这里的演示样例代码仅仅是简单示意了流程,离真正的工具化还有较大差距;基本的原因是大量使用了Runtime.exec方法来直接调用命令,这样不是非常灵活和可控;比方打包和解压能够利用JarOuputStream, JarInputStream等来进行等。会更加可控和灵活。

       

  • 相关阅读:
    leetcode:655. 输出二叉树
    leetcode:763. 划分字母区间
    leetcode:3. 无重复字符的最长子串
    leetcode:2. 两数相加
    leetcode每日一题:409. 最长回文串
    leetcode:1381. 设计一个支持增量操作的栈
    leetcode:1380. 矩阵中的幸运数
    [数据结构] 迷宫问题(栈和队列,深搜和广搜)
    [数据结构] N皇后问题
    [2011山东ACM省赛] Sequence (动态规划)
  • 原文地址:https://www.cnblogs.com/yxwkf/p/5204975.html
Copyright © 2020-2023  润新知