看说OpenJ9的内存占用相较于Hotspot要低,对比文章。所以打算在本地打一个镜像试试。
其实AdoptOpenJDK提供的有OpenJ9的镜像,链接在此。因为需要做一些修改,所以直接用Dockerfile本地构建了。同时也分享一下是如何在服务器无法连接代理的情况下使用AdoptOpenJDK镜像脚本构建镜像。
前期准备
参考的构建脚本是AdoptOpenJDK的openjdk-docker项目,具体链接。具体内容就不放这里了,直接去链接里面看就好了。
为什么构建脚本那么长
OpenJ9现在基于Alpine的好像只能用glibc库,但是Alpine官方用的是musl-libc,所以脚本中很大一部分都是在安装glibc,具体可以看:Installing openjdk 11 on alpine:3.9
国内访问问题
脚本中有很多的curl操作,而且地址对应的是github和archlinux,github用的AWS云存储,国内无法直连,archlinux的archive直连下载只有20kb左右,我搜了下国内的镜像站点都没有收录,所以直接在服务器上构建不可取,需要稍微修改下。
修改脚本
下载文件
先在本地通过代理将所需文件下载至本地,本地搭建一个文件服务器供服务器使用(我这里所用的服务器和工作机是相同网段)。下面也给出了所需的文件,都放在天翼云上了, 国内下载应该是满速。
本地依赖下载:天翼云盘(访问码:rbp5)
搭建文件服务器
文件服务器很简单,有一个基于Chrome的插件可以直接开启服务。Web Server for Chrome
操作很简单,选择文件目录,开启服务就好了,确保服务器能够正常访问即可。
修改URL
在上下两个
RUN语法糖中都加入URL_PREFIX="http://10.20.61.27:8887"(完整Dockerfile见最后),修改所有出现的URL就可以了!别忘记添加Alpine CDN的国内镜像源。
构建JDK
脚本修改好之后,直接在服务端构建即可。
1 | docker build . -t openjdk:11.0-openj9 |
1 | Sending build context to Docker daemon 503.8kB |
使用Jlink精简JDK
做了一下小服务,不需要乱七八糟的依赖,就需要java.base和java.logging就行了,所以上手jlink,干掉多余的东西。
Jlink介绍
官方文档:jlink
JEP:JEP 282: jlink: The Java Linker
您可以使用jlink工具将一组模块及其依赖项组装和优化为自定义运行时映像。
ps. 这也就是OpenJDK9+没有再提供jre的原因,这个比jre强多了好吗,想咋改就咋改。
Jlink使用介绍:Using jlink to Build Java Runtimes for non-Modular Applications
注:如果想精简JDK镜像的话,先用jdeps查看下Jar包的依赖。做到心中有数。jdeps link
常用参数介绍
–add-modules mod[,mod…]
添加模块,后面加模块名的可变参数
-c={0|1|2} or –compress={0|1|2}
启用资源压缩
- 0:不压缩
- 1:常量字符串共享
- 2:ZIP
–no-header-files
排除头文件
–no-man-pages
排除用户手册
–output path
指定新JDK镜像位置
–strip-debug
不输出debug信息
参数示例
以我这边一个精简为例:
- 去掉头文件;
- 去掉用户手册;
- 不压缩(直接使用);
- 屏蔽debug信息;
- 添加指定模块:
java.base,java.logging,jdk.unsupported; - 输出到指定目录:
/opt/openjdk/jre
1 | jlink --no-header-files --no-man-pages --compress=0 --strip-debug \ |
基本上一个270Mb的HotSpot镜像,按照上面的需求精简完就40多Mb。
精简OpenJ9
用的命令和示例中相同,但是因为OpenJ9的JDK本来就大,所以精简完也会比HotSpot的大上10Mb左右。相较于内存节省的空间,在可接受范围内。
OpenJ9的精简基于前面构建的openjdk:11.0-openj9,通过Dockerfile的分层构建和基础镜像实现。具体思路如下:
- 选择
openjdk:11.0-openj9作为来源,精简其中的JDK,存放于指定目录。 - 重新拉取一个Alpine镜像作为底层,拷贝刚才制作的JDK镜像到新的Alpine中。
- 本地化配置
精简所用的Dockerfile详见附录
内存占用
通过OpenJ9镜像创建一个现有服务镜像,长时间稳定运行后,和之前对比。
测试用的是一个Spring Boot的MVC小项目,通过docker stats 查看内存占用。
说明:以client-demo镜像为例,长时间运行(24H以上)后对比。
1 | docker stats <service's-docker-name> |
Hotspot:

OpenJ9:

可以明显的看到,Hotspot JVM的内存占用远高于OpenJ9。具体原因未分析,先用着
附:Docker JVM本地化
JVM的Docker版本都是官方原版的,所以有很多的配置都是通用配置,下面说下几个修改点。
时区
时区是有JVM所在的基础镜像所控制的,如果没有在jar运行时手动配置,则会直接调用系统时区。所以为了一劳永逸,直接修改基础镜像的时区,这里以Alpine为例:
Alpine配置时区需要安装tzdata,首先替换仓库为中科大的镜像,然后安装tzdata,配置完后再删除即可。因为是在一条命令中执行的,所以构建好后只会有一层。
1 | ... |
语言
系统默认语言基本都是US,需要修改为CN才可以。这个其实在上面 构建JDK 的时候在输出中出现过了。主要就是设置下Dockerfile的 ENV 语法糖。然后配置下自启动配置就好了。
1 | ... |
附:Dockerfile
OpenJ9-full
- 本地编译适配,修改URL等
- 替换语言环境变量为中文
1 | FROM alpine:3.11 |
Openj9-slim
- 包括full所有修改
- 使用
jlink精简jdk - 设定时区为
Asia/shanghai
1 | FROM openjdk:11.0-openj9 as deps |
Hotspot-slim
Alpine官方已经提供apk,直接精简就可以了
1 | FROM alpine:3.11 as deps |
总结:要是为了镜像大小的话,可以用OpenJDK官方的,因为支持musl-libc,所以会比OpenJ9的小几十MB。
