前言
- 框架选型结束还算顺利,接下来才是重头戏:jre运行环境的精简
-
一个完整的jre环境动辄上百M,对于微型设备来说还是太大了
plan A ejdk
oracle官方有一个针对嵌入式设备的ejdk,可以通过自带的工具 jrecreate.sh 生成3个级别的jre,size 从10几M到30几M之间
燃鹅!!!哪怕是最大的30几M的包,运行我们的demo的时候也提示缺少包.......
而且由于arm板子上工具缺失,也不方便分析,所以 plan A 暂时搁置。
plan B 基于jdk8 进行精简
- 通过大量的百度调研,发现jdk8的jre精简几乎都是一个思路:
1、rt.jar的精简
通过运行时添加 -verbose:class 来记录所有加载的class,然后分析其中来自rt.java的class,重新组装成一个rt.jar,用于替换jre里面的rt.jar
2、lib下的dll/so的精简
参考:https://blog.csdn.net/xiaoping8411/article/details/6973887
基于这个思路,自行编写了一个脚本文件:thin.sh
#!--bash
rm -rf tmp
mkdir tmp
mkdir tmp/jar
mkdir tmp/rt
mkdir tmp/third
mkdir tmp/ort
let thirdnum=0
let rtnum=0
let num=0
hasrt="n"
unzip -d tmp/jar -q $1
cat class.txt |egrep 'Loaded.*from' >tmp/loadedclass.txt
cat tmp/loadedclass.txt |sed 's/\[//g' |sed 's/!\/\]//g' |sed 's/\]//g' |egrep '\.jar$' >>tmp/loaded.txt
total=`cat tmp/loaded.txt |wc -l`
i=0
while read line
do
#echo "$i:$line"
l=($line)
#echo "${l[1]} ${l[3]}"
file="${l[1]}"
jar="${l[3]}"
file="${file//\.//}.class"
len="${#jar}"
#echo $len
let len=len-7
#echo $len
#echo $file
end="${jar:len}"
let num++
#echo $end
if [ "$end" = "/rt.jar" ] ;then
rtfile=$jar
echo "$num/$total*************get $file from rt.jar****************" >>tmp/run.log
if [ "$hasrt" = "n" ] ;then
hasrt="y";
echo "init origin rt $rtfile"
fi
unzip -qo $rtfile $file -d tmp/rt
let rtnum++
else
#jarfile="tmp/jar/${jar#jar:*jar!/}"
#unzip -q $jarfile $file -d tmp/jar/BOOT-INF/classes/
let thirdnum++
#echo "$num/$total============get $file from third==============" >> tmp/run.log
#echo "$num/$total============get $file from $jarfile=============="
fi
i=$((1+$i))
#if [ $i -eq 10000 ];
#then
# break;
#fi
done < tmp/loaded.txt
echo "done rt class:$rtnum third class :$thirdnum"
echo "done rt class:$rtnum third class :$thirdnum" >>tmp/run.log
echo "zip............"
# cd tmp/third
#zip -r -q ../third.jar ./*
#cd tmp
#rm -rf jar/BOOT-INF/lib/*
#cp third.jar jar/BOOT-INF/lib/
#cd jar
#zip -r -q ../../slim-$1 ./*
#cd ..
#rm -rf /root/jre/jdk2/lib/rt.jar
cd tmp/rt
zip -r -q ../../rt.jar ./*
cd ../..
echo "===========done============="
- 执行 /root/jre/jdk1/bin/java -jar -verbose:class solondemo.jar >>class.txt
- 然后访问所有接口(后续考虑通过测试案例,代码覆盖率100%来保证load所有class)
生成的rt.jar 替换掉jre的rt.jar 然后用替换后的jar来执行
生成的rt.jar 5M左右。
在windows环境可以正常运行。 -
但是,在arm64环境下 使用OpenJDK8U-jre_aarch64_linux_hotspot_8u302b08做同样的操作,最终却得到一个异常
而且连具体的异常是什么的偶不知道,突然就僵住了。
plan C 使用jdk11的模块化特性进行jre剪裁
-
在solon群里分享了这一情况,群里有热心网友提到 性jdk9可以通过模块化对jre进行剪裁。
- 简直是柳暗花明又一村,果断进行百度调研~~~~
由于jdk9并不是一个lts版本,17又有一些序列化的兼容性问题,所以最终决定用open jdk11进行尝试
https://jdk.java.net/archive/
- 一开始打算把项目使用jdk11来编译,于是修改项目jdk版本,并添加module.java
但是编译始终不通过。最后发现是在 .idea的目录下compiler.xml还有一个 bytecodeTargetLevel的配置需要修改
当然,群主也给了一个方案,就是删掉所有idea相关的信息,重新maven导入。
- 编译通过,但是运行报错,大意是类没有export,于是在moudle描述中添加exports 但是又报出更多的权限问题
Unable to make field org.noear.weed.DbContext com.elitect.controller.DemoController.db1 accessible: module solondemo does not "opens com.elitect.controller" to module solon
突然意识到,框架以及框架引用的其他第三方包 都是不支持模块化的,这样强行用是不行的。
java9+版本不再是publlic就可以被任何人的调用的时代了。(这个模块化的方案一定借鉴了karaf,又想起了被karaf支配的恐惧)
于是改变思路,既然代码模块化暂不可行,那我还是用jdk8来对代码进行编译打包,只是用jdk11运行,应该可行吧,向下兼容应该还是有的吧。
-
先把项目改回jdk8 并打包 solondemo.jar
使用jdk11run一下:
OK 没问题。
使用jlink 打一个基础的jre(只包含 java.base模块)
./bin/jlink.exe --module-path jmods --add-modules java.base --output ejre
生成的jre 40M
run一下试试:
虽然报错了,但是只是缺少包的错误,感觉有门啊。
- 创建一个jdk11的空项目,main函数里面执行
System.out.println(org.xml.sax.InputSource.class.getModule());
得到结果
module java.xml
把java.xml模块也打进去
./bin/jlink.exe --module-path jmods --add-modules java.base,java.xml --output ejre
再次运行
......
......
......
通过多次尝试,最终
./bin/jlink.exe --module-path jmods --add-modules java.base,java.xml,java.naming,java.management,java.desktop,java.sql --output ejre
打出的包能完美运行我的demo了
- 但是,jresize达到了60多M,有点超出预期了
- 分析之前的过程发现一个问题,java.desktop是个什么鬼???为啥我一个web项目会用到java.desktop?
- 而且desktop这个模块 有12M之大
-
翻找之前的执行过程,发现是这里使用了java.beans.IntrospectionException这个类,这个类在desktop里
群主帮忙分析后得出结论:是读取yaml的插件引用了这个类。
解决方案:不适用yaml配置,改为properties文件配置,并在pom里面 排除掉这个模块的引用solon.extend.properties.yaml
......
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-api</artifactId>
<exclusions>
<exclusion>
<groupId>org.noear</groupId>
<artifactId>solon.extend.properties.yaml</artifactId>
</exclusion>
</exclusions>
</dependency>
......
<dependency>
<groupId>org.noear</groupId>
<artifactId>weed3-solon-plugin</artifactId>
<exclusions>
<exclusion>
<groupId>org.noear</groupId>
<artifactId>solon.extend.properties.yaml</artifactId>
</exclusion>
</exclusions>
</dependency>
完美!!!!跑起来了,功能也正常(同时 项目jar再次减小0.3M)
最终可使用的jre生成配置:
./bin/jlink.exe --module-path jmods --add-modules java.base,java.xml,java.naming,java.management,java.sql --output ejre
生成的ejre目录总共52M