tencent cloud

文档反馈

Java 应用调优最佳实践

最后更新时间:2024-01-09 12:42:59

    优化容器镜像

    通过优化容器镜像,您可以缩短加载时间和启动时间。您可以通过以下方式优化镜像:
    尽可能减小化容器镜像大小。
    避免使用嵌套 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 的参数。但这个参数存在缺陷,为应用添加 UnlockExperimentalVMOptionsUseCGroupMemoryLimitForHeap 参数后,JVM 确实可以感知到容器内存,并控制应用的实际堆大小。但是这并没有充分利用我们为容器分配的内存。
    因此 JVM 提供 -XX:MaxRAMFraction 标志来帮助更好的计算堆大小,MaxRAMFraction 默认值是4(即除以4),但它是一个分数,而不是一个百分比,因此很难设置一个能有效利用可用内存的值。
    JDK 10 附带了对容器环境的更好支持。如果在 Linux 容器中运行 Java 应用程序,JVM 将使用 UseContainerSupport 选项自动检测内存限制。然后,通过 InitialRAMPercentageMaxRAMPercentageMinRAMPercentage 来进行对内存控制。这时,我们使用的是百分比而不是分数,这将更加准确。
    默认情况下,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 时,InitialRAMPercentageMaxRAMPercentage 将会失效。

    关闭优化编译器

    默认情况下,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
    
    
    联系我们

    联系我们,为您的业务提供专属服务。

    技术支持

    如果你想寻求进一步的帮助,通过工单与我们进行联络。我们提供7x24的工单服务。

    7x24 电话支持