Spring Boot + Java 21:内存减少 60%,启动速度提高 30% — 零代码

简介: 通过调整三个JVM和Spring Boot配置开关,无需重写代码即可显著优化Java应用性能:内存减少60%,启动速度提升30%。适用于所有在JVM上运行API的生产团队,低成本实现高效能。

您可以购买更多服务器。

或者,您可以停止浪费已经购买的服务器。

我们在一个普通的 Spring Boot 服务上调整了三个开关。RAM

减少了 60%。启动速度提高了 30%。

无需重写。无需英雄事迹。无需凌晨两点回滚。

如果您在 JVM 上交付 API,这将是您能获得的最经济的性能。

这个故事适合从事生产的团队。

因为不知道的代价是内存失控、冷启动不符合 SLA,以及寻呼机将周末视为工作日。

我们开始的基线

一个无聊的 Java 17 + Spring Boot 3.x REST 服务。

Tomcat。HikariCP。Jackson。标准配置。

稳定负载:200-300 RPS。

更改前:稳定状态下 RSS 为 780 MB。冷启动到“已启动……”耗时 620 毫秒。

没有什么“着火”,但每个豆荚的成本都超过了应有的水平。

我们打开的 3 个开关(以及它们为何有效)

1)迁移到 Java 21 LTS + 开启字符串重复数据删除

工作原理:现代 HotSpot plus-XX:+UseStringDeduplication压缩由 JSON、标头和 ORM 层创建的重复字符串。

相同的数据,更少的字节。

如何操作:

  • 使用 Java 21 基础镜像(例如 Temurin)。
  • 添加此 JVM 标志:-XX:+UseStringDeduplication

解析文本或分配大量小字符串的服务中,堆内存会更精简。大多数 API 都是如此。

按 Enter 键或单击即可查看完整尺寸的图像

2)为应用程序工作启用虚拟线程

其工作原理:虚拟线程为您提供数千个廉价、阻塞友好的线程,而不会导致堆栈膨胀。

你的代码仍然保持命令式。并发性上升。池化的内存占用减少。

如何用最少的代码来实现:

// src/main/java/com/example/Config.java
package com.example;

import java.util.concurrent.Executors;
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.core.task.TaskExecutor;
import org.springframework.core.task.TaskExecutorAdapter;

public class Config {
  @Bean(name = TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)
  TaskExecutor appExecutor() {
    return new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor());
  }
}

现在@Async,您的调度程序和内部执行程序使用每个任务虚拟线程模型,而无需删除逻辑。

提示:保持同步热部分较短以避免固定。

3)使用 CDS + Lazy Init 加速冷启动(在安全的地方)

工作原理: 类数据共享 (CDS)会预热类元数据,以便 JVM 链接速度更快。

延迟初始化会推迟启动时不需要的 bean 的创建。

如何操作:

  • 运行-Xshare:auto(许多构建中的默认设置)或在构建时生成共享存档。
  • 添加(小心地)spring.main.lazy-initialization=true以快速启动,然后在启动运行器或就绪探测器中预热关键路径。

无需手动调整每个 bean,启动速度就会更快。

之前与之后:重要的数字

  • RSS 内存: 780 MB → 310 MB(-60%)。
  • 冷启动: 620 毫秒 → 430 毫秒(-31%)。
  • 稳定的 p99 延迟: 240 毫秒 → 215 毫秒(-10%)。
  • 吞吐量余量:在相同限制下增加 25%。

简单、可比、有意义。

这就是利益相关者所记住的。

底层发生了什么变化

Before
+------------------------+
| 200 platform threads   |  -> hefty stacks, fixed pool, idle cost
+------------------------+

After
+------------------------+
|  20 platform threads   |  -> carriers only
+------------------------+
             ||
             \/
+------------------------------------------+
| thousands of virtual threads on demand   | -> cheap, block-friendly
+------------------------------------------+

您不再需要为主要等待 I/O 的线程支付堆栈内存。

我们遇到的陷阱是为了防止您遇到

  • 固定陷阱:synchronized块或本机调用会固定虚拟线程。保持它们简短;最好在小型临界区周围使用锁。
  • ThreadLocals:大量ThreadLocal使用虚拟线程会导致线程使用量成倍增加。请审计所有在其中存储大型对象的操作。
  • 延迟初始化的意外情况:/ready第一个请求可能会初始化 bean。在启动时或使用轻量级运行器进行简单预热即可。
  • 关于池大小的误区:一旦虚拟线程处理突发事件,通常可以大幅缩小固定池的大小。务必进行实际测试。

安全推出检查清单

  • 运送一个带有 Java 21、CDS 和字符串重复数据删除的金丝雀荚。
  • 首先启用虚拟线程执行器来执行内部任务。
  • 观察RSS分配率p95/p99GC 暂停时间
  • 如果图表显示一天的交通模式为绿色,则扩展到车队。
  • 只有在冷击不会造成损害的路径上才考虑延迟初始化。

小步前进,实实在在的收获,没有波澜。

为什么这种方法可以跨团队扩展

  • 这是配置优先的。大部分工作都集中在 Docker 标志和一个 bean 上。
  • 它是框架原生的。Spring Boot 拥抱 Java 21 特性;你不需要与你的堆栈作斗争。
  • 它对企业友好。更低的内存意味着更高的节点密度。更快的启动速度意味着更顺畅的部署。这不仅能降低账单,还能让值班更安心。

每个人都忘记的部分

性能不是一种特性,而是一种没有浪费的东西。

Java 21 + Spring Boot 为您提供了现代运行时、更便宜的并发性和更快的启动,而无需触及产品代码。

如果您正在关注云账单或 pod 限制,请从这里开始。

拨动这些开关。衡量。保留有用的。

您的用户不会注意到任何事情——除了您的应用程序感觉更快并且不再要求更多内存。

相关文章
|
1月前
|
Java 开发工具
【Azure Storage Account】Java Code访问Storage Account File Share的上传和下载代码示例
本文介绍如何使用Java通过azure-storage-file-share SDK实现Azure文件共享的上传下载。包含依赖引入、客户端创建及完整示例代码,助你快速集成Azure File Share功能。
337 4
|
1月前
|
Java 数据处理 API
为什么你的Java代码应该多用Stream?从循环到声明式的思维转变
为什么你的Java代码应该多用Stream?从循环到声明式的思维转变
234 115
|
1月前
|
安全 Java 编译器
为什么你的Java代码需要泛型?类型安全的艺术
为什么你的Java代码需要泛型?类型安全的艺术
172 98
|
1月前
|
安全 前端开发 Java
《深入理解Spring》:现代Java开发的核心框架
Spring自2003年诞生以来,已成为Java企业级开发的基石,凭借IoC、AOP、声明式编程等核心特性,极大简化了开发复杂度。本系列将深入解析Spring框架核心原理及Spring Boot、Cloud、Security等生态组件,助力开发者构建高效、可扩展的应用体系。(238字)
|
1月前
|
Java 编译器 API
java最新版和java8的区别,用代码展示
java最新版和java8的区别,用代码展示
245 43
|
1月前
|
Java 大数据 Go
从混沌到秩序:Java共享内存模型如何通过显式约束驯服并发?
并发编程旨在混乱中建立秩序。本文对比Java共享内存模型与Golang消息传递模型,剖析显式同步与隐式因果的哲学差异,揭示happens-before等机制如何保障内存可见性与数据一致性,展现两大范式的深层分野。(238字)
63 4
|
1月前
|
安全 Java 测试技术
《深入理解Spring》单元测试——高质量代码的守护神
Spring测试框架提供全面的单元与集成测试支持,通过`@SpringBootTest`、`@WebMvcTest`等注解实现分层测试,结合Mockito、Testcontainers和Jacoco,保障代码质量,提升开发效率与系统稳定性。
|
1月前
|
存储 缓存 Java
【深入浅出】揭秘Java内存模型(JMM):并发编程的基石
本文深入解析Java内存模型(JMM),揭示synchronized与volatile的底层原理,剖析主内存与工作内存、可见性、有序性等核心概念,助你理解并发编程三大难题及Happens-Before、内存屏障等解决方案,掌握多线程编程基石。
|
4月前
|
存储
阿里云轻量应用服务器收费标准价格表:200Mbps带宽、CPU内存及存储配置详解
阿里云香港轻量应用服务器,200Mbps带宽,免备案,支持多IP及国际线路,月租25元起,年付享8.5折优惠,适用于网站、应用等多种场景。
1635 0
|
4月前
|
存储 缓存 NoSQL
内存管理基础:数据结构的存储方式
数据结构在内存中的存储方式主要包括连续存储、链式存储、索引存储和散列存储。连续存储如数组,数据元素按顺序连续存放,访问速度快但扩展性差;链式存储如链表,通过指针连接分散的节点,便于插入删除但访问效率低;索引存储通过索引表提高查找效率,常用于数据库系统;散列存储如哈希表,通过哈希函数实现快速存取,但需处理冲突。不同场景下应根据访问模式、数据规模和操作频率选择合适的存储结构,甚至结合多种方式以达到最优性能。掌握这些存储机制是构建高效程序和理解高级数据结构的基础。
472 1