Jre瘦身

来源:互联网 发布:linux压缩命令 编辑:程序博客网 时间:2024/05/15 23:53

Jre瘦身

1. bin瘦身

       启动运行的程序,这里我以自己开发的源码统计工具作为例子进行讲解:


这是主界面

       第二步:就是找到加载这个进程的DLL文件,打开360安全卫士à功能大全à进程管理器找到刚才的进程,并且单击显示加载此进程的dll文件。

如下:




将其中的dll文件拷贝到新建的bin目录下,其中有一个client文件夹直接拷贝,另外有一个java.exe是必须的,具体目录如下图:


 

2. 对lib文件进行瘦身

       通过在命令行执行如下语句:

java -jar -verbose:class CodeTongJi.jar>>Class.txt

然后在软件中操作所有的功能,让他尽可能的加载有关的类。产生的Class.txt文件内容如下:

[Loaded java.lang.Object from shared objects file]

[Loaded java.io.Serializable from shared objects file]

[Loaded java.lang.Comparable from shared objects file]

[Loaded java.lang.CharSequence from shared objects file]

[Loaded java.lang.String from shared objects file]

[Loaded java.lang.reflect.GenericDeclaration from shared objects file]

[Loaded java.lang.reflect.Type from shared objects file]

[Loaded java.lang.reflect.AnnotatedElement from shared objects file]

[Loaded java.lang.Class from shared objects file]

……

对此进行处理【可以使用editplus进行处理】,得到如下的文件

java/lang/Object

java/io/Serializable

java/lang/Comparable

java/lang/CharSequence

java/lang/String

java/lang/reflect/GenericDeclaration

java/lang/reflect/Type

java/lang/reflect/AnnotatedElement

….

接下来写个程序,然后根据这样的表单去查找相关的.class文件,然后拷贝到制定的文件夹中,以下是从网上找来的,并且修改了一下,以便符合实际应用的需要,详细请见在test包下的CopyClass类:

package com.cgx;

 

import java.io.BufferedReader;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStreamReader;

import java.io.PrintWriter;

 

/**

 * 由于class.txt每行都是形同: [Loaded java.lang.System from shared objects

 * file]的一串字符,修改文本以方便获取类完整名java.lang.System,从而获得类似类路径java/lang/System的一串字符,方便后继编写类拷贝程序.

 * 修改方法:

 * 1.查找并替换[Loaded 为空,达到删除[Loaded 的目的.

 * 2.使用任意一个具有正则表达式查找替换功能的文本编辑器,查找并替换from.*为空,达到删除 from及其后面的字符串的目的.

 * 3.查找并替换.为/

 * 4.删除以[Opened 开头的行.

 * 5.删除程序中System.out.println的输出行.

 * 首先是输出这样的class.txt文件

 * 通过命令行执行

 * java -jar -verbose:class D:\BillSysGDB\toolsLBMS\toolsLBMS_Browser_tool.jar >>class.txt

 * 即可得到class.txt文件中的内容

 * 提取之后的文件为class_success.txt

 * @author chenguixin

 * 2012-7-27 下午02:17:51

 * CopyClass.java

 */

public class CopyClass {

 

    private String source = "D:\\lib\\"; // 类源目录

    private String dest = "D:\\lib\\"; // 类拷贝目的目录

    String[] jarArr = new String[] { "rt", "charsets" };

 

    /***

     *

     * @param source

     *            类源目录

     * @param dest

     *            类拷贝目的目录

     * @param jarArr

     *            需要的提取的jar文件

     */

    public CopyClass(String source, String dest, String[] jarArr) {

       this.source = source;

       this.dest = dest;

       this.jarArr = jarArr;

    }

 

    public void obtainClassPath(String sourceName, String destName) {

 

       try {

           File sourceFile = new File(sourceName);

           FileInputStream fi = new FileInputStream(sourceFile);

           InputStreamReader ir = new InputStreamReader(fi);

           BufferedReader br = new BufferedReader(ir);

 

           File destFile = new File(destName);

 

           PrintWriter pw = new PrintWriter(destFile);

 

           String content = br.readLine();

 

           StringBuffer writer = new StringBuffer();

 

           while (content != null) {

              // 去掉两边空格

              content = content.trim();

              if (content.indexOf("[Loaded") != -1

                     && content.indexOf("file:/D:/BillSysGDB/toolsLBMS/") == -1) {

 

                  int begin = 7;

 

                  int end = content.indexOf(" from ");

 

                  if (end == -1) {

                     content = br.readLine();

                     continue;

                  }

 

                  System.err.println();

                  System.err.println(content);

                  System.err.println("content->begin:" + begin + ";end:"

                         + end);

                  System.err.println();

 

                  content = content.substring(begin, end).trim();

 

                  content = content.replaceAll("\\.", "\\/");

 

//                if (content.indexOf("$") == -1) {

                     writer.append(content + "\r\n");

//                }

                  // pw.println(content) ;//输入文件中

                  content = br.readLine();

              } else {

                  content = br.readLine();

              }

           }

 

           pw.println(writer.toString());

 

       } catch (Exception e) {

           e.printStackTrace();

       }

 

    }

 

    public static void main(String[] args) {

      

      

       String[] jarArr = new String[]{"rt","charsets"}; 

             CopyClass obj = new CopyClass("D:\\lib\\","D:\\lib\\",jarArr); 

              obj.readAndCopy("D:\\BillSysGDB\\toolsLBMS\\class_success.txt");

//            obj.obtainClassPath("D:\\BillSysGDB\\toolsLBMS\\logg.txt",

//            "D:\\BillSysGDB\\toolsLBMS\\class_success.txt");

 

    }

 

    /***

     * @param logName

     *            提取class明细

     */

    public void readAndCopy(String logName) {

       int count = 0; // 用于记录成功拷贝的类数

       try {

           FileInputStream fi = new FileInputStream(logName);

           InputStreamReader ir = new InputStreamReader(fi);

           BufferedReader br = new BufferedReader(ir);

 

           String string = br.readLine();

           string = string.trim() ;

           while (string != null) {

              if(!"".equals(string)){

              if (copyClass(string) == true)

                  count++;

              else

                  System.out.println("ERROR " + count + ": " + string);

              }

              string = br.readLine();

              if(string != null)

              string = string.trim() ;

           }

       } 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 {

       System.err.println(string) ;

       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;

           break;

       }

       return result;

    }

}

执行后得到这样的文件:charsets1和rt1文件


对这两个文件进行压缩成jar文件

打包后替换掉在lib下的charsets和rt两个包


至此瘦身完成

注意:在瘦身过程中,一定要注意不能缺少类,要尽可能的然其加载所有的类,否则很容报错,对此可以使用eclipse中添加自己的环境,然后进行测试。^__^

 

补充:如果执行exe程序无法启动时可以从以下三个方面进行查找原因

1.      使用eclipse添加自己瘦身后的jre环境,查找缺少的class文件

2.      在eclipse上可以运行,不代表在单机版下可以运行,因为瘦身后,可能存在一些类是不存在的,会导致jar可执行包和exe可执行文件无法执行,接下来一步是使用的自己瘦身后的jre环境来执行jar包,看看是否有异常报出,例子如下:

java -jar D:\temp\CodeTongJi\CodeTongJi.jar

3.      完成第二步之后并不是代表就可以执行exe文件,虽然可以在自己瘦身后的环境中可以执行jar,但是exe可能还会有异常报出,如何处理,此时关键的问题是,执行exe时能够报出异常,并打印出来,下面是我想到的一个方面,通过Runtime类执行一个命令,返回来一个进程对象,将该进程对象的ErrorInputStream打印出来,例子如下,详细请见在test包下的TestExe类:

package com.cgx;

 

import java.io.FileInputStream;

import java.io.IOException;

 

public class TestExe {

 

    /**

     *

     * @author chenguixin

     * @Created On 2012-8-2 下午02:59:46

     * TestExe.java

     * @param args

     */

    public static void main(String[] args) {

       // TODO Auto-generated method stub

       Process process = null ;

      

       try {

           process = Runtime.getRuntime().exec("D:\\temp\\CodeTongJi\\CodeTongJi.exe") ;

          

           FileInputStream fis =(FileInputStream) process.getErrorStream() ;

          

           int word = 0 ;

           while((word = fis.read())!=-1){

              System.err.print((char)word) ;

           }

          

           System.err.println(process.getErrorStream().read()) ;

          

           System.err.println(process.getOutputStream()) ;

          

       } catch (IOException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       }

    }

 

}

4. 需要在不同的机器上去测试

 

 

原创粉丝点击