优化容器镜像
通过优化容器镜像,您可以缩短加载时间和启动时间。您可以通过以下方式优化镜像:
尽可能减小化容器镜像大小。
避免使用嵌套 jar 包。
使用弹性微服务Jar / War 方式部署。
弹性微服务Jar / War 方式部署默认提供 Jar 包镜像构建的实践教程,弹性微服务默认提供能充分利用构建缓存的构建流程,使用新一代构建利器 Buildkit 进行高速构建,构建速度优化50%以上,并且整个构建流程可追溯,构建日志可查,简单高效。
设置应用加速
使用弹性微服务 Jar / War 方式部署,并选择 KONA JDK 11/Open JDK 11 运行环境,弹性微服务将默认开启应用加速功能,并且默认支持 SpringBoot 应用零改造加速。弹性微服务增强了 Open JDK 中的 AppCDS 特性,您无需改造原有的 SpringBoot 嵌套 Jar 包结构,弹性微服务将直接提供 Java 应用加速的实践教程,实例扩容时的启动时间将缩短至10%~40%。
JVM 参数优化
使用可以感知容器内存资源的 JDK
在虚拟机和物理机中,对于 CPU 和内存分配,JVM 会从常见位置(例如,Linux 中的/proc/cpuinfo
和/proc/meminfo
)查找其可以使用的 CPU 和内存。但是,在容器中运行时,CPU 和内存限制条件存储在/proc/cgroups/...
中。较旧版本的 JDK 会继续在/proc
(而不是/proc/cgroups
)中查找,这可能会导致 CPU 和内存用量超出分配的上限,并因此引发多种严重的问题:
线程过多,因为线程池大小由 Runtime.availableProcessors()
配置
JVM的对内存使用超出容器内存上限。并导致容器被 OOMKilled。
JDK 8u131 首先实现了 UseCGroupMemoryLimitForHeap
的参数。但这个参数存在缺陷,为应用添加 UnlockExperimentalVMOptions
和 UseCGroupMemoryLimitForHeap
参数后,JVM 确实可以感知到容器内存,并控制应用的实际堆大小。但是这并没有充分利用我们为容器分配的内存。
因此 JVM 提供 -XX:MaxRAMFraction
标志来帮助更好的计算堆大小,MaxRAMFraction
默认值是4(即除以4),但它是一个分数,而不是一个百分比,因此很难设置一个能有效利用可用内存的值。
JDK 10 附带了对容器环境的更好支持。如果在 Linux 容器中运行 Java 应用程序,JVM 将使用 UseContainerSupport
选项自动检测内存限制。然后,通过 InitialRAMPercentage
、MaxRAMPercentage
和 MinRAMPercentage
来进行对内存控制。这时,我们使用的是百分比而不是分数,这将更加准确。
默认情况下,UseContainerSupport
参数是激活的,MaxRAMPercentage
是25%,MinRAMPercentage
是50%。
需要注意的是,这里 MinRAMPercentage
并不是用来设置堆大小的最小值,而是仅当物理服务器(或容器)中的总可用内存小于250MB时,JVM 将用此参数来限制堆的大小。
同理,MaxRAMPercentage
是当物理服务器(或容器)中的总可用内存大小超过250MB时,JVM 将用此参数来限制堆的大小。
这几个参数已经向下移植到 JDK 8u191。UseContainerSupport 默认情况下是激活的。我们可以设置 -XX:InitialRAMPercentage=50.0 -XX:MaxRAMPercentage=80.0
来 JVM 感知并充分利用容器的可用内存。需要注意的是,在指定 -Xms -Xmx
时,InitialRAMPercentage
和 MaxRAMPercentage
将会失效。
关闭优化编译器
默认情况下,JVM 有多个阶段的 JIT 编译。虽然这些阶段可以逐渐提高应用的效率,但它们也会增加内存使用的开销,并增加启动时间。
对于短期运行的云原生应用,可以考虑使用以下参数来关闭优化阶段,以牺牲长期运行效率来换取更短的启动时间。
JAVA_TOOL_OPTIONS="-XX:+TieredCompilation -XX:TieredStopAtLevel=1"
关闭类验证
当 JVM 将类加载到内存中以供执行时,它会验证该类未被篡改并且没有恶意修改或损坏。但在云原生环境,CI/CD 流水线通常也由云原生平台提供,这表示我们的应用的编译和部署是可信的,因此我们应该考虑使用以下参数关闭验证。如果在启动时加载大量类,则关闭验证可能会提高启动速度。
JAVA_TOOL_OPTIONS="-noverify"
减小线程栈大小
大多数 Java Web 应用都是基于每个连接一个线程的模式。每个 Java 线程都会消耗本机内存(而不是堆内存)。这称为线程栈,并且每个线程默认为1MB。如果您的应用处理100个并发请求,则它可能至少有100个线程,这相当于使用了100MB的线程栈空间。该内存不计入堆大小。我们可以使用以下参数来减小线程栈大小。
JAVA_TOOL_OPTIONS="-Xss256k"
需要注意如果减小得太多,则将出现 java.lang.StackOverflowError
。您可以对应用进行分析,并找到要配置的最佳线程栈大小。
Spring boot 应用优化
使用 Spring Boot 2.2 或更高版本
从 2.2 版开始,Spring Boot 已针对启动速度进行了大量优化。如果您使用的是低于 2.2 版的 Spring Boot,请考虑升级或 手动进行优化。 使用延迟初始化
在 Spring Boot 2.2 及更高版本中,您可以开启全局延迟初始化,以提高启动速度,但代价是第一个请求的延迟时间可能变长,因为需要等待组件首次初始化。
您可以在 application.properties
中开启延迟初始化:
spring.main.lazy-initialization=true
或者,使用以下环境变量:
SPRING_MAIN_LAZY_INITIALIZATIION=true
本页内容是否解决了您的问题?