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. 需要在不同的机器上去测试