JRE精简攻略

前言

转:https://www.iteye.com/blog/stream-town-1990748
前段时间用Java完成了一个本地工具的开发,但在推广时发现让用户自己去安装JDK是件很麻烦的事情,而且JDK版本的不统一也给工具的正常运行带来很多影响,于是自己就准备将JRE精简后打包到程序中。

  JRE精简的网上教程有很多,像GreenJVM一类的工具也有很多,但这些教程和工具在自己的一番试用之下没一个管用的,所以只好自己动手,丰衣足食。



  工具准备:process explorer(附件中有下载)



  步骤一 选择要精简的JRE版本

  选择好目标才好动手,首先要根据自己程序的需要选择好对应的JRE版本,1.6,1.7等等,更重要的是选好32位还是64位。此处建议选择32位的JRE,因为32位的JRE是可以运行在64位操作系统上的,而64位的是不能运行在32位操作系统上的。



  步骤二 精简DLL文件
  启动自己所写的JAVA应用,打开process explorer,在进程列表中选中java(或javaw),按下ctrl+d,就可以在下方面板中查看到java.exe运行时所调用dll文件及其所在路径,将jre\bin\路径下除这些文件之外的dll全部删除,即可完成dll文件的精简了。



 

 步骤三 精简jar和ttf
  AVA应用的运行,打开process explorer,在进程列表中选中java(或javaw),按下ctrl+h,就可以在下方面板中查看到程序运行时所使用的jar和ttf文件,在jre\lib\下移除掉其他无用的jar和ttf即可。





  步骤四 精简其他资源
  现在lib文件夹下应该只剩下一些配置文件和图标等文件,建议保留\lib\下面.properties,xlist,.data等文件,保留zi目录下面用到的国家配置文件,其他的按使用情况删除即可。

   bin目录下面的exe除了java或javaw其他的也都可以删掉,只要在process explorer中没有看到即可。

   最后,如果不确定的文件就先删掉以后运行一下程序试试。

  

  步骤五 精简rt.jar

  终于来到最重要也是最麻烦的一步,rt.jar的精简。一个rt.jar的体积有50多M,占到JRE总体积的三分之一还多,而其中我们用到的只有一点点,所以精简rt.jar是整个jre精简工作的核心步骤。
  首先,用如下命令运行我们的程序,具体参数请根据实际情况修改,重点在于利用-verbos:class打印出程序运行时所用到的类:

Java代码 收藏代码
java -jar -classpath lib/*.jar; -verbose:class myAPP.jar >> classes.txt
启动后将程序的所用功能都运行一遍,一定把每个功能都过一遍才可以,尤其是tooltip等容易被忽略的要特别注意。全部运行完后,会生成一份如下的classes.txt:

Java代码 收藏代码
[Loaded java.lang.Math from shared objects file]
[Loaded java.nio.charset.Charset$3 from C:\Java\jdk1.6.0_16\jre\lib\rt.jar]
[Opened C:\Java\jdk1.6.0_16\jre\lib\charsets.jar]
接着,将jre下的rt.jar解压到一个rt文件夹中,利用如下的RTCutter程序,将rt文件夹所在的目录和class.txt的路径作为输入,即可得到一份精简好的rt1文件夹了:

Java代码 收藏代码
package com.tc.test;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class RTCutter {
private String source = null; // 类源目录
private String dest = null; // 类拷贝目的目录
String[] jarArr = new String[] {“rt” };

/*** 
 *  
 * @param source 
 *            类源目录 
 * @param dest 
 *            类拷贝目的目录 
 * @param jarArr 
 *            需要的提取的jar文件 
 */  
public RTCutter(String source, String dest, String[] jarArr) {  
    this.source = source;  
    this.dest = dest;  
    this.jarArr = jarArr;  
}  

public static void main(String[] args) {  
    String[] jarArr = new String[] {"rt" };  
    RTCutter obj = new RTCutter(  
            "C:/JREMaker/tmp1/",  
            "C:/JREMaker/tmp1/", jarArr);  
    obj.cutRT("D:/classes.txt");  
}  

/*** 
 * 根据classes.txt文件精简rt目录 
 * @param logName 
 *            提取class明细 
 */  
public void cutRT(String logName) {  
    List<String> cList = getClassesListFromFile(logName);  
    int count = 0; // 用于记录成功拷贝的类数  
    try {  
        for(String klass : cList) {  
            if (copyClass(klass)) {  
                count++;  
            } else {  
                System.out.println("ERROR " + count + ": " + klass);  
            }  
        }  
    } catch (IOException e) {  
        System.out.println("ERROR: " + e);  
    }  
    System.out.println("count: " + count);  
}  

/*** 
 * 从原jar路径提取相应的类到目标路径,如将java/lang/CharSequence类从rt目录提取到rt1目录 
 *  
 * @param string 
 *            提取类的全路径 
 * @return 
 * @throws IOException 
 */  
public boolean copyClass(String string) throws IOException {  
    if (string.lastIndexOf("/") == -1) {  
        return false;  
    }  
    String classDir = string.substring(0, string.lastIndexOf("/"));  
    String className = string.substring(string.lastIndexOf("/") + 1,  
            string.length())  
            + ".class";  

    boolean result = false;  

    for (String jar : jarArr) {  
        File srcFile = new File(source + "/" + jar + "/" + classDir + "/"  
                + className);  
        if (!srcFile.exists()) {  
            continue;  
        }  

        byte buf[] = new byte[256];  
        FileInputStream fin = new FileInputStream(srcFile);  

        /* 目标目录不存在,创建 */  
        File destDir = new File(dest + "/" + jar + "1/" + classDir);  
        if (!destDir.exists()) {  
            destDir.mkdirs();  
        }  
        File destFile = new File(destDir + "/" + className);  
        FileOutputStream fout = new FileOutputStream(destFile);  
        int len = 0;  
        while ((len = fin.read(buf)) != -1) {  
            fout.write(buf, 0, len);  
        }  
        fout.flush();  
        result = true;  
        fin.close();  
        fout.close();  
        break;  
    }  
    return result;  
}  
  
/** 
 * 将原始的classes.txt转换为类名列表 
 * @param source 由-verbose:classes选项生成的classes.txt的路径 
 */  
private List<String> getClassesListFromFile(String source) {  
    BufferedReader reader = null;  
    ArrayList<String> classList = new ArrayList<>();  
    try {  
        reader = new BufferedReader(new FileReader(source));  
        String line = null;  
        while((line = reader.readLine()) != null) {  
            if (line.startsWith("[")) {  
                if (line.startsWith("[Opened")) {  
                    continue;  
                }  
                line = line.replace("[Loaded ", "");  
                int index = line.indexOf("from");  
                if (index >= 0) {  
                    line = line.substring(0, index);  
                }  
                line = line.trim().replace(".", "/");  
                classList.add(line);  
            }  
        }  
    } catch (Exception e) {  
        e.printStackTrace();  
    } finally {  
        try {  
            reader.close();  
        } catch (Exception e) {  
              
        }  
    }  
    return classList;  
}  

}

 RTCutter程序处理后,会在rt所在目录下面生成一个rt1目录,里面就是精简后的之后的rt了,利用jar命令将rt1目录里的类打包成新的rt.jar:

Java代码 收藏代码
jar cf rt.jar com java javax org sun

这样rt.jar的精简就完成了,当然还需要用新的rt.jar运行一下程序,如果出现了classNotFound的异常,就需要把缺少的类放到rt1目录中,再重新打包。



若程序之后的版本做了改动,那么仍需要利用-verbose:classes参数来运行程序,将改动的功能单独运行一次,利用RTCutter得到一个rt1目录,并和之前的rt1目录合并一下,再打包生成一个新的rt.jar。当然,如果更牛一点,这些步骤都可以在ant脚本中实现。



结语

整个JRE的精简就是上面的5步了,整体过程还是比较简单的,就是在精简中肯定会需要大量的时间去验证,而且在后续的版本升级和维护方面也会有很多工作,另外利用精简化的jre也有可能出现一些莫名其妙的问题,同时也使得程序失去了跨平台的兼容性,这些因素都是需要在确立精简方案之前需要考虑的,用户的易用性和程序的可维护性中间终究要找到一个平衡。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值