惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

TaoSecurity Blog
TaoSecurity Blog
L
LINUX DO - 最新话题
Help Net Security
Help Net Security
N
News | PayPal Newsroom
www.infosecurity-magazine.com
www.infosecurity-magazine.com
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
The Last Watchdog
The Last Watchdog
S
Security @ Cisco Blogs
W
WeLiveSecurity
C
CXSECURITY Database RSS Feed - CXSecurity.com
Webroot Blog
Webroot Blog
T
Troy Hunt's Blog
V
Vulnerabilities – Threatpost
Google Online Security Blog
Google Online Security Blog
N
News and Events Feed by Topic
T
Threat Research - Cisco Blogs
Security Archives - TechRepublic
Security Archives - TechRepublic
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tor Project blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
D
Darknet – Hacking Tools, Hacker News & Cyber Security
PCI Perspectives
PCI Perspectives
Google DeepMind News
Google DeepMind News
T
Tailwind CSS Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Apple Machine Learning Research
Apple Machine Learning Research
IT之家
IT之家
S
SegmentFault 最新的问题
J
Java Code Geeks
P
Privacy & Cybersecurity Law Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园 - 【当耐特】
博客园_首页
H
Hacker News: Front Page
T
Threatpost
Jina AI
Jina AI
博客园 - Franky
月光博客
月光博客
L
LINUX DO - 热门话题
The Cloudflare Blog
H
Heimdal Security Blog
博客园 - 司徒正美
酷 壳 – CoolShell
酷 壳 – CoolShell
Cloudbric
Cloudbric
雷峰网
雷峰网
Hugging Face - Blog
Hugging Face - Blog
S
Secure Thoughts
T
Tenable Blog
I
Intezer
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻

暗无天日

读:AI Agent 安全日志——从可见性与隐私的两难说起 - 暗无天日 AI写作的语言指纹——如何让文字不那么像机器 - 暗无天日 读:50 条 Claude Code 技巧——一个工程经理的六个月使用心得 读:AI 辅助开发为什么让 E2E 测试更有价值 - 暗无天日 读:在Emacs中使用Claude Code(Spacemacs适配版) - 暗无天日 Claude Code 背后的工程哲学——读 Agent Harness Engineering 读:Agent Harness Engineering——AI 智能体不只是模型,还有套件 - 暗无天日 browser-harness:让 AI 直接接管你的浏览器 - 暗无天日 读:Security-First CI/CD —— DevSecOps 自动化实践指南 TIL: 数字小键盘的小数点陷阱与行内算术求值 - 暗无天日 读:Immutability 不是万能药,它是一种权衡 - 暗无天日 Conducty:给 Claude Code 加上项目记忆和并行执行能力 - 暗无天日 读 — GitHub Trending 里的 Claude Code 技能包 读 — Prompt Caching 省钱指南 TIL: Emacs 中那些跟鼠标配合的冷门快捷键 - 暗无天日 读:Anvil——把 Emacs 变成 AI 的工具服务器 读:Emacs 代码折叠终极指南 - 暗无天日 读:Clojure 搭车客指南 - 暗无天日 git推送失败后恢复仓库损坏的完整记录 - 暗无天日 多智能体系统的两个有效模式——以及对 Claude Code 用户的启示 - 暗无天日 用 Org Babel 写 Literate 博文:扩展执行 + 定制导出 proced:Emacs 内置的进程查看器 - 暗无天日 从 proced 定制中学到的 Elisp 模式 读:让 Emacs proced 在 macOS 上显示 CPU 和内存 异步编程的函数着色税 - 暗无天日 链式调用的代价:JavaScript 和 Clojure 的共同教训 - 暗无天日 hyperfine:命令行基准测试工具 - 暗无天日 管道中的变量去哪了?——子 shell 作用域陷阱 - 暗无天日 开源包装器的信任陷阱:四个危险信号 - 暗无天日 程序员愿意为 AI 写文档,却不愿为同事写 - 暗无天日 mktemp: Shell 脚本中临时文件的安全陷阱与最佳实践 - 暗无天日 WSL9x —— 在 Windows 9x 里跑 Linux 内核 6.19 用 ox.el 做你想做的事 —— org-export 高级编程指南 读:Hot-wiring the Lisp Machine —— 用纯 Elisp 构建零依赖的 Org 静态站点生成器 Elisp 性能优化的六个实战教训 - 暗无天日 fcitx5 下 Emacs 无法切换输入法的排查 - 暗无天日 ERT 测试交互命令的三种方式 - 暗无天日 SEM Assistant: 当 Elisp 守护进程遇上 LLM 用 dmsg 给 Elisp 加上结构化调试日志 用 org-habit 追踪非每日习惯 - 暗无天日 Clojure X-Men:当编程语言特性变成超能力 - 暗无天日 TIL: 用 diff-hl 在 fringe 中显示 git 变更 读:llm-test —— 用 LLM agent 驱动 Emacs 测试 TIL: AI 时代的橡皮鸭调试 - 暗无天日 fcitx 启动后键盘输入卡顿的排查 - 暗无天日 TIL: 早期网页的图片热区导航 - 暗无天日 读 Seeing the Whole System 用 Emacs 自动生成每周链接推荐 - 暗无天日 读:ASCII control characters in my terminal 读 What to learn - 暗无天日 Lisp 的括号之痛——一个愚人节玩笑揭开的老伤疤 - 暗无天日 一本书该"线性读"还是"并行读" - 暗无天日 读 How to Monetize a Blog:一篇伪装成变现指南的讽刺文 Python Mock 第三方依赖的四种策略 - 暗无天日 Emacs Lisp 热重载实用指南 - 暗无天日 Prot 的 Emacs 配置哲学 - 暗无天日 TIL: 从直播对谈中学到的三个 Emacs 技巧 - 暗无天日 TIL: 自动使用项目虚拟环境的 Python - 暗无天日 TIL: 让 Help buffer 自动获得焦点 一条命令让本地开发用上 HTTPS —— slim 工具介绍 用 fsck 检查和修复 Linux 文件系统 排查Linux进程"卡死"实战:从strace到gdb全流程 - 暗无天日 PostgreSQL 索引:从基础到你可能不知道的高级用法 - 暗无天日 用 .pdbrc 自定义 Python 调试器 ANSI 转义码的标准化现状 - 暗无天日 终端程序的潜规则 - 暗无天日 PARA Org-mode 测试配置 - 暗无天日 AI越强越辣鸡?控制论说这是必然的 - 暗无天日 AI 越强越需要你盯着——反馈循环实操指南 - 暗无天日 你的AI代理正在偷你的密钥——四种你没想到的泄露通道 - 暗无天日 LLM 在 DevOps 中的三种角色 - 暗无天日 写作风格的反建议 - 暗无天日 反驳本质复杂性——Dan Luu 论为什么《没有银弹》错了 - 暗无天日 文件充满了危险——Dan Luu 谈文件系统的可靠性陷阱 - 暗无天日 AI 时代的 PARA 方法:用 Org-mode 和 AI 打造个人知识管理系统 Linux 数据去重学习笔记 - 暗无天日 创建跨平台 ZIP 文件的隐藏陷阱:Extra Field - 暗无天日 X11 Forwarding 排障指南 - 暗无天日 IP欺骗端口扫描:当别人冒充你去扫描别人 - 暗无天日 Linux 输入栈全景解析:从硬件按键到屏幕响应 - 暗无天日 Unix 系统中那些被埋没的配置开关——以 FontConfig 为例 - 暗无天日 在Linux上限制儿童使用电脑 - 暗无天日 GIF不仅仅是一种图片格式——用GIF流做些奇怪的事 - 暗无天日 Leiningen 学习笔记:Clojure 项目构建与管理从入门到实战配置 - 暗无天日 Google SRE Book 读书笔记 - 暗无天日 yes 管道 head 发生了什么 - 暗无天日 为什么 nohup 在 crontab 中不起作用 Bash中的Indirection与Nameref - 暗无天日 Linux PAM 简介 - 暗无天日 从Linux ISO文件启动计算机 - 暗无天日 用 Bash 打造一个Screen Locker 用GitHub Actions自动构建EGO博客 - 暗无天日 blocking I/O 的作用 - 暗无天日 mobileog 手机端同步提示Error:2 No such file 的解决方法 回收 WSL2 VHDX 文件占用空间 使用 org-mode columnview 生成任务列表 - 暗无天日 Emacs 作为 MPD 客户端 - 暗无天日 移动文件路径却不破坏org file link的方法 - 暗无天日 如何合理的导出help link 成HTML - 暗无天日 笑话理解之Biology - 暗无天日
读:JVM 后端性能调优备忘——从一次生产事故中学到的优化要点 - 暗无天日
2026-05-01 · via 暗无天日

DZone 上有一篇 Java 后端性能调优的实战记录,讲的是一套 Spring Boot + Hibernate 的订单系统在流量高峰时出问题,以及团队怎么从硬件堆砌转向真正的性能诊断。本文提炼其中的关键工具、排查思路和优化策略,作为一份备忘。

诊断工具链:先拿数据再说

遇到性能问题,原文第一条经验是: 不要猜,先做 profile 。猜来猜去只会浪费时间,profile 摆在面前的数据不会骗人。

Java Flight Recorder (JFR)

JFR 是 JDK 自带的低开销事件记录器,可以在生产环境持续运行。原文团队在流量高峰期录了 20 分钟的 JFR 数据。开销很低,可以放心在生产环境开着。

JFR 最早在 JDK 7u4(2012 年)中以商业特性提供,需要额外加参数解锁。从 JDK 11 起已开源,所有实现(包括 OpenJDK)都直接可用。

启用方式:在 JVM 启动参数加 -XX:StartFlightRecording=filename=recording.jfr,duration=60s 会在启动后录制 60 秒。对已经在运行的应用,可以用 jcmd <pid> JFR.start duration=60s 命令动态开启录制,用 jcmd <pid> JFR.dump filename=recording.jfr 导出数据。

三大常见性能杀手

高对象分配率(Object Churn)

原文的核心问题在于每个请求都创建了大量临时对象,这些对象迅速填满年轻代(Eden 区),频繁的触发 Minor GC。GC 线程忙着回收,业务线程就只能等了。

问题出在数据转换工具方法,这个方法把 Entity 转为 DTO 时,每个字段都 new 了 ArrayList,还在循环里用字符串拼接。单次调看不出来什么影响,一分钟内涌入几千笔订单就引发了灾难。

下面的代码演示了两种写法的差异:

public static OrderDto badTransform(long id, String[] rawTags) {
    OrderDto dto = new OrderDto();
    dto.id = "ORD-" + id;
    dto.tags = new ArrayList<>();
    for (String t : rawTags) dto.tags.add(t);
    String s = "";
    for (String t : rawTags) s += t + ";";
    dto.desc = s;
    dto.summary = s;
    return dto;
}

public static OrderDto goodTransform(long id, String[] rawTags) {
    OrderDto dto = new OrderDto();
    dto.id = "ORD-" + id;
    dto.tags = Arrays.asList(rawTags);
    StringBuilder sb = new StringBuilder();
    for (String t : rawTags) sb.append(t).append(";");
    dto.desc = sb.toString();
    dto.summary = sb.toString();
    return dto;
}

在本地跑 20 万次转换的耗时对比:

Bad  (每个字段 new 集合 + 循环拼接): 633 ms
Good (Arrays.asList + StringBuilder): 427 ms
Speedup: 1.5 x

这个 demo 只跑了个裸循环,1.5 倍的差距不算太大。但在真实 Spring Boot 应用中,每个请求的 DTO 转换会在 Hibernate 会话、事务拦截器、JSON 序列化等多个环节叠加对象分配。加上几千笔订单并发,GC 频繁暂停的放大效应就很可观了。原文称这一改动减少了约 80% 的对象创建。

N+1 查询

Hibernate 最常见的问题之一就是N+1查询。举个例子:加载一批订单,Hibernate 先发一条 SELECT ... FROM orders 查出所有订单(1 条 SQL)。然后你遍历每个订单,访问它的订单明细(order.getItems()),Hibernate 就为 每个 订单再发一条 SELECT ... FROM order_items WHERE order_id?= (N 条 SQL)。100 笔订单就变成 1 + 100 = 101 条 SQL。每条 SQL 都有网络往返开销,乘起来就是灾难。

修复方式是用 Entity Graph 。这是 JPA(Java Persistence API,Java 持久化规范)提供的一种机制,让你在查询时显式声明"这次要把关联数据一并拉出来"。Hibernate 收到声明后会在一条 SQL 里用 JOIN 查出所有关联数据,不再逐条发查询。这既减少应用端的等待时间,也降低数据库端的 CPU 负载。

锁竞争

原文的缓存实现用了 `synchronized` 关键字。在 Java 里 `synchronized` 是一把互斥锁——同一时刻只允许一个线程执行被保护的代码块。如果多个线程同时访问缓存,它们就得排队一个一个来,读也要排队,不能并行。这就把并发访问变成了串行化。

修复方式是把缓存换成了 `ConcurrentHashMap`。这是 Java 提供的线程安全哈希表,内部做了分段加锁,读操作完全不需要锁,写操作也只锁住特定分段,多线程可以同时读写不互相阻塞。

线程池也需要关注:默认工作线程数不够,性能不足会导致队列堵塞,堵塞多了可能引发 OOM。应该按 CPU 核心数调整线程池大小,队列设一个合理上限而非无限增长。

JVM 参数调优要点

堆大小

容器化部署时,JVM 默认堆大小对于生产负载常常不够。传统的做法是用 -Xmx 设一个固定值,比如 -Xmx2g ,但问题是:如果你的容器内存配置改成了 4GB, -Xmx2g 还是只用了 2GB,白白浪费了一半内存。

-XX:MaxRAMPercentage 是 JVM 的参数,告诉 JVM "拿容器可用内存的百分之多少做堆"。比如 -XX:MaxRAMPercentage=75.0 表示最多使用容器内存的 75%,剩下 25% 留给 JVM 自身和其他进程。这样容器扩了内存,堆大小也跟着自动扩,不用修改启动参数。

JDK 版本要求:这个参数在 JDK 8u131 中引入,但早期版本还需要同时加 -XX:+UseContainerSupport 显式启用容器支持。从 JDK 8u191 起容器支持默认开启,直接设 MaxRAMPercentage 即可。当前环境是 OpenJDK 8u492,验证可用。

G1GC 参数

G1GC 的 -XX:InitiatingHeapOccupancyPercent 控制何时启动并发标记,默认 45%。原文降到 30%,让 GC 更早开始,避免等到堆快满时才触发,那时已经来不及了,会引发 Full GC 暂停。

GC 日志

开启 GC 日志可以持续监控 GC 行为,在问题恶化前发现异常。这是长期维护的必备工具。

可持续的性能实践清单

原文团队事后总结的几点,每条都有具体产出:

建议 怎么做
开发阶段就做 profile 本地开 JFR,早发现高分配模式
监控 GC 指标 跟踪 GC 暂停时间和频率,设置告警
预防 N+1 检查 Hibernate SQL 日志,用 JOIN FETCH 或 Entity Graph
减少对象分配 复用对象,避免在循环中创建临时集合,用 StringBuilder
容器化适配 用 MaxRAMPercentage 代替固定 Xmx
负载测试要真实 模拟长时间会话和高并发,不要只跑简单的单元测试
异步处理 耗时任务丢到后台队列,不阻塞 HTTP 线程

启示

这篇事故记录最有价值的地方不在具体参数值(参数需要根据你的实际负载调整),而在务实的诊断思路:从堆硬件到堆工具,从猜原因到读数据。性能优化不是玄学,JFR 和火焰图这些工具让工程师能看清问题出在哪。