对于一份不大的程序却要使用庞大的jre,这是一个很烦恼的事情,所以,在看了网络上千千万万的方法之后,终于测验并总结出可用的精简方法。
我jre精简前是这样的:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200713173457564.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0lfdHJ5,size_16,color_FFFFFF,t_70) 重点看bin文件夹以及lib文件夹。这两个是精简的重点,其他的皆可以舍弃。
桌面新建文件夹名: xin
复制你jre 以及 jar,并另外建文件夹tt。
将你的 rt.jar 解压到 xin 文件下。
一:精简bin:
对于bin文件夹的精简较为容易,将你程序的jar包放在bin目录下,cmd进入命令,输入`java.exe -jar xxx.jar`,运行后直接全选bin文件下所有,删除掉所有可以删除的对象,注意保留java.exe 以及javaw.exe,后者保障你的jre可以被你程序检索到,若是一直报no jvm found的错误,注意检查一下是否是这个文件错删了。
测试重运行一下,可以将`java.exe -jar xxx.jar`以及后续的一些命令行操作改为批处理文件,省的每次都要重新处理。
一:精简lib:
lib文件下最重要的是精简 rt.jar。至于其他的例如 charsets.jar,个人建议不要去动,避免出现资源加载错误。
将你的rt.jar 复制到一个单独的文件夹,解压缩后,在该目录新建一个文件夹命名tt,当然,此处是为了代码方便,这代码我也是网上摘抄的,但有些小错误,我已经修复了。
1.此处利用 -version 读出程序加载的类。
test.jar 注意改为你自己的jar
java -jar -classpath lib/*.jar; -verbose:class test.jar >> class.txt
2.根据classes.txt文件提炼出具体的类包。
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
/**
* @author soil
*
*/
public class Demo_Class2 {
private static String sourse = "C:/Users/MY/Desktop/xin/class.txt";
private static String outAddress = "C:/Users/MY/Desktop/xin/class_demo2.txt";
public static void main(String[] args) {
try {
int count_L = 0;
int loopCount = 0;
// 源文件位置,打开它
FileInputStream fin = new FileInputStream(sourse);
InputStreamReader isr = new InputStreamReader(fin);
BufferedReader br = new BufferedReader(isr);
// 输出文件位置
FileOutputStream fout = new FileOutputStream(outAddress);
OutputStreamWriter osw = new OutputStreamWriter(fout);
BufferedWriter bw = new BufferedWriter(osw);
// 读一行
String sp = br.readLine();
// 只要没有读到文件尾就一直执行
while (sp != null ) {
if (sp.length() < 1) {
System.out.println("发现一次!");
}
// 只读取以"[L"为开头的行
else if (sp.substring(0, 2).equals("[L")) {
// 以空格来分隔这个行,返回的字符串数组中的第二个就是我们需要的信息
String s = sp.split(" ")[1];
StringBuilder bs = new StringBuilder(s);
// 只是个测试输出,可以不加
System.out.println(bs);
// 循环遍历这个字符串,修改它,使它变成我们需要的格式
for (int i = 0; i < bs.length(); i++) {
char ch = bs.charAt(i);
// 简化循环,因为我们得到的信息很有规律。只要出现大写的字母,就说明已经到了不需要执行的时候了。
if (ch >= 65 && ch < 91)
break;
// 把'.'替换成'/',当然,代码中是因为方法的参数要求。
if (ch == '.') {
bs.replace(i, i + 1, "/");
}
// 这个是循环的执行此时。
loopCount++;
}
// 这里在输出你的文件信息。加工后用于后续操作。
bw.write(bs.toString() + '\n');
// 程序需要的类文件数目。
count_L++;
}
// 读行
sp = br.readLine();
}
// 两个测试输出
System.out.println(count_L);
// System.out.println(loopCount);
// 千万要把两个文件关闭!!!
br.close();
bw.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.复制并分离这些class。
/**
* Created by MY on 2020/7/6.
*/
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* @author soil
*
*/
public class CopyClass {
private String source = "C:\\Users\\MY\\Desktop\\xin\\rt\\"; // 类源目录
private String dest = "C:\\Users\\MY\\Desktop\\xin\\tt\\"; // 类拷贝目的目录
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 static void main(String[] args) {
String[] jarArr = new String[] { "rt", "charsets" };
CopyClass obj = new CopyClass("C:\\Users\\MY\\Desktop\\xin\\",
"C:\\Users\\MY\\Desktop\\xin\\tt\\", jarArr);
obj.readAndCopy("C:\\Users\\MY\\Desktop\\xin\\class_demo2.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();
while (string != null) {
if (copyClass(string) == true)
count++;
else
System.out.println("ERROR " + count + ": " + string);
string = br.readLine();
}
br.close();
} 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";
FileOutputStream fout;
FileInputStream fin;
boolean result = false;
for (String jar : jarArr) {
File srcFile = new File(source + "/" + jar + "/" + classDir + "/" + className);
if (!srcFile.exists()) {
continue;
}
byte buf[] = new byte[256];
fin = new FileInputStream(srcFile);
/* 目标目录不存在,创建 */
File destDir = new File(dest + "/" + jar + "1/" + classDir);
if (!destDir.exists())
destDir.mkdirs();
File destFile = new File(destDir + "/" + className);
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;
}
}
4.打包替换运行
在 tt 文件夹处进入cmd,运行以下代码
jar cvf rt1.jar -C rt\ .
修改名字为 rt ;放回 jre 并替换原有的jar包运行即可。
5.可能出现的错误
:1:对应确实什么class,到相应目录重新打包就好。
:2:显示什么 a JNA …类似的,或者是找不到main类似的,就是你的jre lib内的java少东西了,少什么我没有测出来,有个方法就是利用 GreenJVM,以下是我的网盘资源链接:
下载:
提取码:zu6a
按照操作将得出来的jar解压到你利用程序得出的jar综合以下,一般就可以用了。