关于精简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是否成功吧。
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值