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