前言 最近自己用SpringCloudAlibaba做了一个微服务架构的项目,部署的时候遇到了难题:内存不够。目前该项目有7个微服务,因为我只有一台阿里云的服务器(2C4G),所以我只能把所有的微服务部署在一台服务器上,部署方式是使用docker制作springboot的fatjar镜像,每个微服务在不加任何JVM调优参数的情况下所占内存约500M。 由于是微服务所以肯定还要部署:nacos,除此之外还用到了redis、sentinel、rocketmq、elk等(mysql买的阿里云的),光是运行这些应用就占用内存2个多G,剩下的1个多G内存在部署4个微服务后就满了,于是开始对springboot应用的内存进行初步优化: 添加JVM参数优化内存大小JVM初始分配的内存由Xms指定,默认是物理内存的164Xms128mJVM最大分配的内存由Xmx指定,默认是物理内存的14Xmx128m规定了每个线程虚拟机栈及堆栈的大小,一般情况下,256k是足够的,此配置将会影响此进程中并发线程数的大小。Xss256k指定并行GC线程的数量,一般最好和CPU核心数量相当XX:ParallelGCThreads2 默认空余堆内存小于40时,JVM就会增大堆直到Xmx的最大限制;空余堆内存大于70时,JVM会减少堆直到Xms的最小限制。 因此服务器一般设置Xms、Xmx相等以避免在每次GC后调整堆的大小。对象的堆内存由称为垃圾回收器的自动内存管理系统回收。 默认情况下,当CPU数量小于8,ParallelGCThreads的值等于CPU数量,我的服务器是2C的所以这个参数可省略。配置完成后,启动服务发现内存确实变小了,由原来的500M降至100200M,但不是我想要的效果,我期望的效果是达到几十M的级别。 经网上查阅大量资料得知可以使用SpringNative这门新技术来实现我的需求。(该技术正处于快速迭代阶段,变动较大,建议用于个人学习,不要用于生产) SpringBoot项目使用SpringNative后:应用启动速度特别快,毫秒级别运行时更低的内存消耗,官方展示的含有SpringBoot,SpringMVC,Jackson,Tomcat的镜像大小是50M为了达到前面的效果,代价是构建时间更长(即使是一个HelloWord构建也需要2分钟,不过主要取决于电脑配置,我的是2min左右)SpringNative是什么 简而言之就是为了提高Java在云原生的竞争力(个人理解)。 以下内容摘抄自GitHub上SpringNative的自述文件: SpringNative为使用GraalVM原生映像编译器将Spring应用程序编译为原生可执行文件提供beta支持,以提供通常设计为打包在轻量级容器中的原生部署选项。实际上,目标是在这个新平台上支持几乎未修改的SpringBoot应用程序。 以下内容摘抄自其他博客: 近几年原生一词一直泛滥在云计算、边缘计算等领域中,而原生宠幸的语言也一直都是Golang,Rust等脱离Sandbox运行的开发语言。Java得益于上世纪流行的一次编译,到处执行的理念,流行至今,但也因为这个原因,导致Java程序脱离不了JVM运行环境,使得不那么受原生程序的青睐。在云原生泛滥的今天,臃肿的JVM使Java应用程序对比其他语言显得无比的庞大,各路大神也想了很多方式让Java变的更原生。 实战 本次实战相关的环境信息如下:OS:Windows1021H1IDE:IntelliJIDEA2021。2。3JDK:graalvmcejava1121。3。0Maven:3。6。3DockerDesktopforWindows:20。10。12SpringBoot:2。6。2SpringNative:0。11。1 从官方文档得知(上图) 使用SpringNative的应用程序应该使用Java11或Java17编译。 构建SpringBoot原生应用程序有两种主要方法:使用SpringBootBuildpacks支持生成包含本机可执行文件的轻量级容器。使用GraalVM原生镜像Maven插件支持生成原生可执行文件。 经过各种踩坑后在本机上成功的使用了方法1和方法2。简单来说: 方法1就是在SpringBoot2。3后,可以使用springbootmavenplugin插件来构建docker镜像,使用mvnspringboot:buildimage命令结合Docker的API来实现SpringBoot原生应用程序的构建,成功执行后会直接生成一个docker镜像,然后run这个镜像就可以了,不用我们再写Dockerfile了,相关的参数配置都在pom。xml中配置(该插件的configuration标签下,和fabric8或spotify的dockermavenplugin很相似)。 方法2不需要安装docker,但要安装VisualStudio,然后执行mvnPnativepackage命令后会生成一个可执行文件(。exe),运行即可。 主要区别如下1环境依赖不同方法1需要安装Docker方法2需要安装VisualStudio(需要用到部分单个组件:2个MSVC,1个Windows10SDK)2执行的maven命令不同方法1是mvnspringboot:buildimage方法2是mvnPnativepackage 因为每个微服务使用Docker部署而不是exe文件,所以方法1正好符合我的需求,所以后文使用SpringBootBuildpacks的方式构建SpringBoot原生应用程序。1安装GraalVM(graalvmcejava11windowsamd64) 官方下载地址: https:www。graalvm。orgdownloads 2配置环境变量 针对方法1的话,上面三张图好像只用配置JAVAHOME就行,想一次成功的话建议3个都配,后续可以自行测试。扩展:最全的java面试题库 检验是否安装成功 3安装nativeimage 打开新的cmd,输入以下命令,等待安装guinstallnativeimage 这一步我执行失败了,解决方法就是从github上手动下载nativeimage,然后解压、安装 https:github。comgraalvmgraalvmcebuildsreleasesdownloadvm21。3。0nativeimageinstallablesvmjava11windowsamd6421。3。0。jar jar用WinRAR也是可以解压的,解压后如下 在bin目录下打开cmd,输入以下命令,等待安装guinstallLnativeimage4安装DesktopforWindows 具体步骤略,按照官方文档操作即可: https:docs。docker。comdesktopwindowsinstall5配置pom。xml 前面都是准备工作,这一步开始才是重点 首先快速创建一个SpringBoot项目,我命名为springnative 完整的pom如下lt;?xmlversion1。0encodingUTF8?projectxmlnshttp:maven。apache。orgPOM4。0。0xmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:maven。apache。orgPOM4。0。0https:maven。apache。orgxsdmaven4。0。0。xsdmodelVersion4。0。0modelVersionparentgroupIdorg。springframework。bootgroupIdspringbootstarterparentartifactIdversion2。6。2versionrelativePath!lookupparentfromrepositoryparentgroupIdltd。pcddgroupIdspringnativeartifactIdversion0。0。1SNAPSHOTversionnamespringnativenamedescriptionspringnativedescriptionpropertiesjava。version11java。versionrepackage。classifierspringnative。version0。11。1springnative。versionpropertiesdependenciesdependencygroupIdorg。springframework。bootgroupIdspringbootstarterwebartifactIddependencydependencygroupIdorg。springframework。experimentalgroupIdspringnativeartifactIdversion{springnative。version}versiondependencydependenciesbuildpluginsplugingroupIdorg。springframework。experimentalgroupIdspringaotmavenpluginartifactIdversion0。11。1versionexecutionsexecutionidgenerateidgoalsgoalgenerategoalgoalsexecutionexecutionsplugin!SpringBoot2。3发布后带来了新特性之一就是对构建镜像的便捷支持plugingroupIdorg。springframework。bootgroupIdspringbootmavenpluginartifactIdconfigurationimagebuilderpaketobuildpacksbuilder:tinybuilderenvBPNATIVEIMAGEtrueBPNATIVEIMAGEenvimageconfigurationpluginpluginsbuildrepositoriesrepositoryidspringreleaseidnameSpringreleasenameurlhttps:repo。spring。ioreleaseurlrepositoryrepositoriespluginRepositoriespluginRepositoryidspringreleaseidnameSpringreleasenameurlhttps:repo。spring。ioreleaseurlpluginRepositorypluginRepositoriesproject 本文介绍的是SpringNative0。11。1版本,其对应的SpringBoot版本必须是2。6。2,以上只是一个最基本的配置案例,实际开发中还需要在springbootmavenplugin插件的configuration标签下配置其他许许多多的参数。 例如docker远程的地址和证书的路径、jvm调优参数、配置文件指定、docker镜像名端口仓库地址等等,最好的方法就是看springbootmavenplugin的官方文档,这里以配置jvm参数为例 通过官方文档得知只需要在configuration标签下配置即可,例如imagebuilderpaketobuildpacksbuilder:tinybuilderenvBPNATIVEIMAGEtrueBPNATIVEIMAGEBPEDELIMJAVATOOLOPTIONSxml:spacepreserveBPEDELIMJAVATOOLOPTIONSBPEAPPENDJAVATOOLOPTIONSXms128mBPEAPPENDJAVATOOLOPTIONSBPEAPPENDJAVATOOLOPTIONSXmx128mBPEAPPENDJAVATOOLOPTIONSBPEAPPENDJAVATOOLOPTIONSXss256kBPEAPPENDJAVATOOLOPTIONSBPEAPPENDJAVATOOLOPTIONSXX:ParallelGCThreads2BPEAPPENDJAVATOOLOPTIONSBPEAPPENDJAVATOOLOPTIONSXX:PrintGCDetailsBPEAPPENDJAVATOOLOPTIONSenvimage 其他的配置参数还有很多。扩展:最全的java面试题库 官方文档: https:docs。spring。iospringbootdocs2。6。2mavenpluginreferencehtmlsinglebuildimage6执行maven命令mvncleanmvnDmaven。test。skiptruespringboot:buildimage 下载完相关依赖后,电脑风扇就开始呼呼的转,查看任务管理器发现CPU利用率100,内存使用量飙升,最后稳定在90。 构建成功 7创建并运行容器 查看所有镜像 springnative就是构建的镜像 创建并运行容器 在DockerDesktop查看日志,发现应用成功启动,启动仅耗时。,也就是59ms,果然印证了SpringNative启动是毫秒级别这句话。 成功调用接口 在DockerDesktop查看占用内存,仅28M左右。 不使用SpringNative启动应用 启动耗时3s,占用内存高达511M,高下立判。 来源:blog。csdn。netweixin43553153articledetails122486769