关于精简JRE文件之精简rt.jar包

  在网络上搜索下便能看到有不少讨论这个问题的文章。个人认为,JRE最好是能向下兼容,于是不同的程序可以在自己的程序中配置所需要的JRE版本。这样就不再会因为用户机器上的JRE版本问题而烦恼。同时,Java开发者所能做的就是尽量精简JRE以满足自己产品运行为最低要求。  
     
本文將介绍我成功简化rt.jar包的过程。
正文:
操作流程简介:
首先,在Windows环境下,准备好需要精简的rt.jar所属的JRE文件夹。
接着,需要在Windows环境下,通过命令在准备好的的JRE环境中运行我们所需要的程序,程序的每个功能最好都能执行,以满足每个需要被调用的包能被自动记录在一个文本文件中。
然后,编写一个程序按照记录將解压缩後的rt.jar包中所需要的类及其所属文件目录结构完整的复制到一个新的rt文件夹中(默认命名为ort(Objecct rt的意思))。
最后,打包ort文件为rt.jar并替换之前准备好的JRE/lib/ 下的rt.jar文件。(原来的rt.jar文件有40多MB,简化後最小可能只有几MB)。

操作流程详述:

(其中用到了来自网络的程序代码,当然经过我大量修改後才成功执行了任务。由于事隔几日,没有记下原代码作者信息,如果原代码作者看到,可以联系我,我将会加上引用注释)
重新搜索了下,那
篇文章被到处引用,如:
http://www.kaiyuan8.org/Article/syaKuiwQVGqhnFgjCwcZ.aspx
http://www.cnitblog.com/Walter/articles/59164.html(仅仅作为举例,别无它意)
已经难寻出处了。
下面来讲讲我根据这篇文章精简的经历吧。

  首先,在Windows下准备好for Windows版的完整JRE文件。(我的在C:/Program Files/Java/jdk1.6.0_20/jre,其中jre/bin/中的Java程序是.exe格式的)。所要精简的rt.jar在/jre/lib/目录下。先不拷贝这个jre文件。
  我们需要在jre文件见的父文件夹(即,上级文件夹)下创建.txt文本文件,并写入:

start jre/bin/java -jar -verbose:class 【改写成你打包成.jar的产品程序包路径,并去掉这句话两端的中括号】>>usedClasses.txt
pause

  再將其.txt修改成.cmd。并执行。

“这样程序使用的就是当前目录下的jre,程序运行后,最好把所有的功能使用一遍,这样输出了一个文件usedClasses.txt,里面有所有需要的class,其格式如下:
[Opened D:\data\dict\jre\lib\rt.jar]
[Loaded java.lang.Object from D:\data\dict\jre\lib\rt.jar]
[Loaded java.io.Serializable from D:\data\dict\jre\lib\rt.jar]
[Loaded java.lang.Comparable from D:\data\dict\jre\lib\rt.jar]
[Loaded java.lang.CharSequence from D:\data\dict\jre\lib\rt.jar]
[Loaded org.apache.lucene.index.CompoundFileReader$FileEntry from file:/D:/data/dict/dict.jar]
 我们依照这个文件来裁剪rt.jar:“

由于在Ubuntu下我使用GUN Emacs 23来修改usedClasses.txt文件的内容,以满足之后程序读取的需要(这算是我第一次正式使用Emacs吧)。第一步,我用Gun Emacs 23打开usedClasses.txt文件。
第二步,选择Edit->Replace->Replace String…,键入将被替换的[Loaded 并确认,再键入用于替换它的回车(即,等同于删去[Loaded 这个关键字段)。
第三步,选择Edit->Replace->Replace Regexp…,通过正则表达式from.*来查找from到]的语句。并替换
第四步,删掉最开头的 [Opened 。删掉其他多余的不包含.class类的语句。
第五步,保存文件。这样usedClasses.txt便准备好了。
注释:在Emacs中 `\n’ here doesn’t match a newline; to do that, type C-q C-j instead(C代表Ctrl键)按下Ctrl不放,再接着依次按qj键。(根多关于Emacs可以参考:http://hi.baidu.com/limp_t/blog/item/3bc60f54d868b0143a2935bf.html)

下面修改之前提到的文章中的Java代码。因为我发现他们还不能够成功拷贝二进制文件。
首先,我分享将要用到的,由我封装的IO读写类的部分用到的方法的代码,其他的代码可以省去,足够作为一个完整的类。
然后,我在分享我修改後的拷贝.class文件及其目录结构的代码。


InputOutput.java:

package com.wordpress.iwillaccess.classes.global;

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.FileReader;

import java.io.FileWriter;

import java.io.IOException;

public 
class InputOutput {

    
private 
static String _fileLocation;

    
private 
static String _fileName;

    
private 
static String _fileDirectory;

    
private 
static String _fileContent;

    
private 
static 
byte[] _byte;

    
/**

     

@return
 the _fileDirection

     */

    
public String get_fileLocation() {

        
return _fileLocation;

    }

    
/**

     

@return
 the _fileName

     */

    
public String get_fileName() {

        
return _fileName;

    }

    
/**

     

@return
 the _fileDirectory

     */

    
public String get_fileDirectory() {

        
return _fileDirectory;

    }

    
/**

     

@return
 the _fileContents

     */

    
public String get_fileContent() {

        
return _fileContent;

    }

    
/**

     

@return
 the _byte

     */

    
public 
byte[] get_byte() {

        
return _byte;

    }

    
/**

     

@param
 fileDirection

     *            the _fileDirection to set

     */

    
private 
static 
synchronized 
void set_fileLocation(String fileLocation) {

        _fileLocation = fileLocation;

    }

    
/**

     *

     

@param
 fileName

     *            the _fileName to set

     */

    
private 
static 
synchronized 
void set_fileName(String fileName) {

        _fileName = fileName;

    }

    
/**

     *

     

@param
 fileLocation

     *            the _fileLocation to set

     * 
@param
 fileName

     *            the _fileName to set 
<
br
>

     * 
<
br
>

     *            fileLocation and fileName is the _fileDirectory to set.

     */

    
public 
synchronized 
void set_fileDirectory(String fileLocation,

            String fileName) {

        set_fileLocation(fileLocation);

        set_fileName(fileName);

        _fileDirectory = fileLocation + fileName;

    }

    
/**

     *

     

@param
 fileDirectory

     *            the _fileDirectory to set 
<
br
>

     * 
<
br
>

     *            _fileDirectory = _fileLocation + _fileName; 
<
br
>

     *            _fileLocation and _fileName will be set automatically in this

     *            method. 
<
br
>

     */

    
public 
synchronized 
void set_fileDirectory(String fileDirectory) {

        set_fileLocation(fileDirectory.substring(
0, fileDirectory

                .lastIndexOf(
“/”) + 
1));

        set_fileName(fileDirectory

                .substring(fileDirectory.lastIndexOf(
“/”) + 
1));

        _fileDirectory = fileDirectory;

    }

    
/**

     

@param
 fileContents

     *            the _fileContents to set

     */

    
public 
synchronized 
void set_fileContent(String fileContent) {

        _fileContent = fileContent;

    }

    
/**

     

@param
 _byte

     *            the _byte to set

     */

    
private 
synchronized 
void set_byte(
byte[] _byte) {

        InputOutput._byte = _byte;

    }

    
public InputOutput() {

        
// 
TODO
 Auto-generated constructor stub

    }

    
/**

     *
 check if the file is exist or not

     *

     

@param
 fileDirectory

     *            the _fileDirectory to set, and the directory of the file will

     *            be checked.

     * 
@return
 true: file is exist; 
<
br
>

     *         false: file is not exist.

     */

    
public 
boolean checkIfFileExist(String fileDirectory) {

        set_fileDirectory(fileDirectory);

        File file = 
new File(get_fileDirectory());

        
// System.out.println(“file.exists():” + file.exists());

        
return file.exists();

    }

//…………

//……省去的无关方法的代码………

//…………

    
/**

     *
 input get_byte() to get_fileDirectory.
 If object file is exist, it will

     * be replaced without warning.
<
br
>

     * 
<
br
>

     * NOTE: 
<
br
>

     * 
<
blockquote
>
 use the set_fileDirectory(), set_byte() and

     * checkIfFileExist(String fileDirectory) methods first 
<
br
>

     * to set a righdis.readFully(b)t file directory and file content
<
br
>

     * before using this method; 
<
br
>

     * 
<
blockquote
>

     */

    
public 
synchronized 
void DataInputFully() {

        
if (get_fileLocation() != 
null && get_fileName() != 
null

                && get_fileContent() != 
null && get_byte() != 
null) {

            
try {

                FileOutputStream fos = 
new FileOutputStream(get_fileDirectory());

                DataOutputStream dos = 
new DataOutputStream(fos);

                dos.write(get_byte());

                dos.close();

            } 
catch (Exception e) {

                e.printStackTrace();

            }

        } 
else {

            System.out.println(
“fileLocation:” + get_fileLocation()

                    + 
“; or fileName:” + get_fileName() + 
“; or  byte[]:”

                    + get_byte() + 
” is null.”);

        }

    }

    
/**

     *
 input get_byte() to get_fileDirectory.
 If object file is exist, it will

     * be replaced without warning.
<
br
>

     * 
<
br
>

     * NOTE: 
<
br
>

     * 
<
blockquote
>
 please use set_fileDirectory() to set the file directory,

     * and use checkIfFileExist(String fileDirectory) first 
<
br
>

     * before using this method. 
<
br
>

     * 
</
blockquote
>

     *

     * 
@param
 b

     *            the _byte to set

     */

    
public 
void DataInputFully(
byte[] b) {

        set_byte(b);

        DataInputFully();

    }

    
/**

     *
 input get_byte() to get_fileDirectory.
 If object file is exist, it will

     * be replaced without warning.
<
br
>

     * 
<
br
>

     * NOTE: 
<
br
>

     * 
<
blockquote
>
 create a file, whose directory as 
<
code
>
fileDirectory
</
code
>

     * and file content as 
<
code
>
get_byte()
</
code
>

<
br
>

     * Use checkIfFileExist(String fileDirectory) first 
<
br
>

     * 
</
blockquote
>

     *

     * 
@param
 fileDirectory

     *            the _fileDirectory to set
<
br
>

     * 
@param
 b

     *            the _byte to set, contains the content of file.

     */

    
public 
void DataInputFully(String fileDirectory, 
byte[] b) {

        set_fileDirectory(fileDirectory);

        set_byte(b);

        DataInputFully();

    }

    
/**

     *
 input get_byte() to get_fileDirectory.
 If object file is exist, it will

     * be replaced without warning.
<
br
>

     * 
<
br
>

     * NOTE: 
<
br
>

     * 
<
blockquote
>
 create file named 
<
code
>
fileName
</
code
>
 in

     * 
<
code
>
fileLocation
</
code
>
 input 
<
code
>
get_byte()
</
code
>
 to file named as

     * 
<
code
>
fileName
</
code
>
 in 
<
code
>
fileLocation
</
code
>

<
br
>

     * Use checkIfFileExist(String fileDirectory) first 
<
br
>

     * 
</
blockquote
>

     *

     * 
@param
 fileLocation

     *            the _fileLocation to set and will be used to set the

     *            _fileDirectory

     * 
@param
 fileName

     *            the _fileName to set and will be used to set the

     *            _fileDirectory

     * 
@param
 b

     *            the _byte to set and will be write to the file with

     *            _fileDirectory directory. _byte Contains the content of file.

     */

    
public 
void DataInputFully(String fileLocation, String fileName, 
byte[] b) {

        set_fileDirectory(fileLocation, fileName);

        set_byte(b);

        DataInputFully();

    }

    
/**

     *
 Out put a file by using DataOutputStream class.
 
<
br
>

     * 
<
br
>

     * NOTE:
<
br
>

     * 
<
blockquote
>
use the set_fileDirectory() method first
<
br
>

     * to set a right file directory
<
br
>

     * before using this method;
<
br
>

     * 
</
blockquote
>

     *

     * 
@return
 null:if fileDirectory is null; 
<
br
>

     *         file content byte[];

     */

    
public 
synchronized 
byte[] DataOutputFully() {

        
if (get_fileDirectory() != 
null) {

            File file = 
new File(get_fileDirectory());

            
if (file.exists()) {

                
try {

                    FileInputStream fis;

                    fis = 
new FileInputStream(get_fileDirectory());

                    DataInputStream dis = 
new DataInputStream(fis);

                    
byte[] b = 
new 
byte[dis.available()];

                    
while (dis.available() > 
0) {

                        dis.readFully(b);

                        
// System.out.println(”   :” + b.length);

                    }

                    set_byte(b);

                    String tmpStr = 
“”;

                    
for (
int i = 
0; i < b.length; i++) {

                        tmpStr += String.valueOf(b[i]);

                    }

                    set_fileContent(tmpStr);

                    
// System.out.println(“\n” + get_fileContent() + “\n”

                    
// + get_fileLocation() + “\n” + get_fileName());

                } 
catch (FileNotFoundException e) {

                    
// 
TODO
 Auto-generated catch block

                    e.printStackTrace();

                } 
catch (IOException e) {

                    
// 
TODO
 Auto-generated catch block

                    e.printStackTrace();

                } 
catch (Exception e) {

                    
// 
TODO
 Auto-generated catch block

                    e.printStackTrace();

                }

                
return get_byte();

            } 
else {

                System.out.println(
“fileDirectory:” + get_fileDirectory()

                        + 
“; and the file is not exist.”);

                
return 
null;

            }

        } 
else {

            System.out.println(
“fileDirectory:” + get_fileDirectory()

                    + 
“; is null”);

            
return 
null;

        }

    }

    
/**

     *
 Out put a file, which directory as 
<
code
>
fileDirectory
</
code
>

<
br
>

     *
 by using DataOutputStream class.
 
<
br
>

     *

     * 
@param
 fileDirectory

     *            the _fileDirectory to set and will be used to find the file,

     *            which is supposed to be opened. 
<
br
>

     * 
@return
 byte[] _byte() the content of the chose file.

     */

    
public 
byte[] DataOutputFully(String fileDirectory) {

        set_fileDirectory(fileDirectory);

        
return DataOutputFully();

    }

    
/**

     *
 Out put a file, which directory as 
<
code
>
fileLocation
</
code
>
 plus

     *
 
<
code
>
fileName
</
code
>

<
br
>

     *
 by using DataOutputStream class.
 
<
br
>

     *

     * 
@param
 fileLocation

     *            the _fileLocation to set and will be used to set the

     *            _fileDirectory

     * 
@param
 fileName

     *            the _fileName to set and will be used to set the

     *            _fileDirectory

     * 
@return
 byte[[] get_byte() the content of the chose file.Data

     */

    
public 
byte[] DataOutputFully(String fileLocation, String fileName) {

        set_fileDirectory(fileLocation, fileName);

        
return DataOutputFully();

    }

    
public 
static 
void main(String[] args) {

        InputOutput io = 
new InputOutput();

        io.set_fileDirectory(
“/home/knowyourself1010/Tracker.class”);

        io.set_fileContent(
“1234567890 abcdefg ABCDEFG 好的繁体字简体字复杂么?”);

        
// io.DataInput();

        System.out.println(io.DataOutput());

        io.set_fileDirectory(
“/home/knowyourself1010/file.class”);

        io.DataInput();

        
// io.set_fileContent(“rfgadssssssssssssssdsaaaadwefewfefeferegrqedwdq”);

        
// System.out.println(io.get_fileContent());

    }

}

CopyUsefulClasses.java:

/**

 *

 
*/

package com.wordpress.iwillaccess.tools;

import java.io.BufferedReader;

import java.io.File;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.InputStreamReader;

import java.io.LineNumberReader;

import com.wordpress.iwillaccess.classes.global.InputOutput;

/**

 

@author
 knowyourself1010

 *

 */

public 
class CopyUsefulClasses {

    
// 文件拷贝

    
private 
static 
boolean copy(String sourceFileLocation,

            String objectFileLocation, String fileName) {

        
try 
// must try and catch,otherwise will compile error

        {

            
if (sourceFileLocation.substring(sourceFileLocation.length() – 
1) != 
“/”) {

                sourceFileLocation += 
“/”;

            }

            
if ((objectFileLocation.substring(objectFileLocation.length() – 
1)) != 
“/”) {

                objectFileLocation += 
“/”;

            }

            InputOutput inputOutput = 
new InputOutput();

            
byte[] b = inputOutput

                    .DataOutputFully(sourceFileLocation, fileName);

            inputOutput.DataInputFully(objectFileLocation, fileName, b);

            
return 
true
// if success then return true

        } 
catch (Exception e) {

            System.out.println(
“Error!”);

            
return 
false
// if fail then return false

        }

    }

    
// 读取路径,copy

    
private 
static 
int dealClass(String needfile, String sdir, String odir)

            
throws IOException {

        
int sn = 
0
// 成功个数

        
if (odir.length() > 
0 && sdir.length() > 
0) {

            
if ((sdir.substring(sdir.length() – 
1)) != 
“/”) {

                sdir += 
“/”;

            }

            
if (odir.substring(odir.length() – 
1) != 
“/”) {

                odir += 
“/”;

            }

            File usedclass = 
new File(needfile);

            
if (usedclass.canRead()) {

                String line = 
null;

                LineNumberReader reader = 
new LineNumberReader(

                        
new InputStreamReader(
new FileInputStream(usedclass),

                                
“UTF-8”));

                
while ((line = reader.readLine()) != 
null) {

                    line = line.trim();

                    
if (line.contains(
“.”) || line.contains(
“/”)) {

                        
// format the direction from package name to path

                        String dir = line.replace(
“.”
“/”);

                        
// filter the file name.

                        String tmpdir = dir.substring(
0, dir.lastIndexOf(
“/”));

                        String sourceFileLocation = sdir + tmpdir;

                        String objectFileLocation = odir + tmpdir;

                        String fileName = dir.substring(

                                dir.lastIndexOf(
“/”) + 
1, dir.length())

                                + 
“.class”;

                        File fdir = 
new File(objectFileLocation);

                        
if (!fdir.exists())

                            fdir.mkdirs();

                        
boolean copy_ok = copy(sourceFileLocation,

                                objectFileLocation, fileName);

                        
if (copy_ok)

                            sn++;

                        
else {

                            System.out.println(line);

                        }

                    } 
else {

                        sn = –
1;

                    }

                }

            }

        }

        
return sn;

    }

    
/**

     

@param
 args

     */

    
public 
static 
void main(String[] args) {

        
// 
TODO
 Auto-generated method stub

        
try {

            BufferedReader lineOfText = 
null;

            
// get need classes log file direction

            System.out

                    .println(
“needfile (file logged the full name of useful classes ):”);

            lineOfText = 
new BufferedReader(
new InputStreamReader(System.in));

            String needfile = lineOfText.readLine();

            
// get source folder direction

            System.out

                    .println(needfile

                            + 

\n
Source Folder Direction (the direction of the folder filled the classes need to be filtered.):”);

            lineOfText = 
new BufferedReader(
new InputStreamReader(System.in));

            String sdir = lineOfText.readLine();

            
// get object folder direction

            System.out

                    .println(sdir

                            + 

\n
Object Folder Direction (the direction of the folder used to fill the filtered classes.):”);

            lineOfText = 
new BufferedReader(
new InputStreamReader(System.in));

            String odir = lineOfText.readLine();

            System.out.println(odir + 

\n
);

            
int sn = dealClass(needfile, sdir, odir);

            System.out.print(sn);

        } 
catch (IOException e) {

            
// 
TODO
 自动生成 catch 块

            e.printStackTrace();

        }

    }

}

   (在此感谢irc: ##java上朋友提示读写Binary文件的注意事项:不要用Reader方法!Char也不太支持哦。)

运行CopyUsefulClasses.java。首先输入usedClasses.txt的绝对路径,回车,在输入jre/lib/rt.jar解压後的rt文件夹所在的路径,回车,再输入ort(所要存放拷贝过来的有用的.class文件的文件夹)。然后等上几分中,期间会提示,你的产品程序所用到的jre不包含的类不存在,不用管,因为我们呢只拷贝rt文件中的.class文件。
最后將ort文件夹下面的所有文件加压缩为rt.zip文件,并改为rt.jar。
注意:1.是选中ort文件夹下所有子文件夹,而非选中ort文件夹,并压缩。
          2.在Ubuntu10.10下如果选择压缩为jar格式文件,可能会少压缩进java.lang.Object.class等文件(估计是个bug),所以需要压缩为.zip再该类型为.jar。

最后,將要简化的jre中的rt.jar替换为这里压缩得到的rt.jar文件。我的只有2MB。
用 java -jar [您的jar程序路径]
来测试简化後的jre是否成功吧。

    原文作者:Housing_person
    原文地址: https://blog.csdn.net/kkkwewewaqsdfas/article/details/11829349
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞