精簡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等來進行等。會更加可控和靈活。

   


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM