Java性能优化基础篇

Java性能优化 · 基础篇

本篇是针对 Java 语言基础内容(常用类、容器、IO、序列化、网络编程)的调优总结,其中的很多内容在 通用系统性能优化 里也提到过,例如充分利用 cpu cache(arraylist、填充字段避免伪共享)、调整 tcp 连接相关参数等。

字符串

String 的实现演进

版本 底层结构 关键变更
Java 6 及之前 char[] + offset + count 共享数组可导致 substring 内存泄漏
Java 7 ~ 8 char[] 取消 offset/count,substring 不再共享数组
Java 9+ byte[] + coder 节约内存:Latin-1 用 1 字节,UTF-16 用 2 字节

核心要点:String 的不可变性由 final class + private final char[]/byte[] 保证。

不可变性的四个作用

  1. 安全性:防止恶意篡改
  2. 哈希稳定:hash 不变,适合作为 HashMap/HashTable 的 key
  3. 字符串常量池:相同字面量可复用
  4. 线程安全:多线程下无需同步

优化策略

场景 推荐做法 原因
长字符串拼接 StringBuilder 避免循环内反复创建 String 对象
多线程拼接 StringBuffer 线程安全,但有锁开销,性能低于 StringBuilder
大量重复字符串 String.intern() 把对象移到常量池复用,节省堆内存
字符串分割 优先 indexOf 避免 split 内部正则回溯带来的 CPU 风险

关于 intern():常量池基于 HashTable 实现,数据量过大时遍历成本高,需结合场景使用。


正则表达式

引擎选型

引擎 时间复杂度 特性 代表
DFA O(n) 功能弱、构建昂贵 较少使用
NFA O(ns) 支持捕获组、环视、占有优先 Java/JS/Python

Java 正则基于 NFA,因此回溯是性能问题的根源

匹配模式

模式 写法 行为
贪婪 ab{1,3}c 尽可能多匹配,失败时回溯
懒惰 ab{1,3}?c 尽可能少匹配
独占 ab{1,3}+bc 尽可能多匹配,失败不回溯,直接结束

核心结论:能用独占模式解决的问题,不要用贪婪模式;能用懒惰模式时优先懒惰。

性能优化四原则

  1. 减少贪婪:能用独占就不要用贪婪
  2. 减少分支:把常用项放前面;提取公共前缀;简单分支可用 indexOf 替代 (X|Y|Z)
  3. 减少捕获嵌套:用 (?:X) 替代 (X),降低分组开销
  4. 避免回溯型灾难:警惕嵌套量词对相同字符重复匹配

集合与 Stream

ArrayList vs LinkedList 选型

维度 ArrayList LinkedList
底层 Object[] 双向链表
随机访问 O(1) O(n)
头部增删 O(n) 数组复制 O(1)
中间增删 O(n) 数组复制 O(n) 查找 + O(1) 修改
尾部追加(无扩容) O(1) 最快 O(1) 但有 new 对象开销
内存占用 紧凑 每个元素多 2 个指针

反直觉的结论:

  • “LinkedList 增删快” — 仅在头部成立;中间位置反而比 ArrayList 慢
  • “ArrayList 遍历快” — for 循环最快;迭代器循环两者相近
  • 遍历 LinkedList 严禁用 for 循环:每次循环都要遍历半个链表

ArrayList 三个易错点

  1. 初始化容量:预知大小时显式传入 new ArrayList<>(size),避免多次 1.5 倍扩容
  2. transient 不等于不可序列化:ArrayList 自定义 writeObject/readObject,只序列化有数据的部分
  3. 扩容公式:newCapacity = oldCapacity + (oldCapacity >> 1),即 1.5 倍

Stream

操作分类 说明
中间操作 懒执行,只构建调用链
终结操作 真正执行计算
无状态 不受前置元素影响(filter/map)
有状态 需要全部元素(sort/distinct)
短路 满足条件即可终止(find/anyMatch)
非短路 必须遍历全部

Stream 使用决策树:

1
2
3
数据量小 → 常规 for 循环
数据量大 + 单核 → 常规 for 循环(并行无优势)
数据量大 + 多核 CPU → Stream 并行流(ForkJoin 分片)

HashMap 设计与优化

数据结构演进

1
2
JDK 1.7:  数组 + 链表(头插法,扩容时可能死循环)
JDK 1.8: 数组 + 链表 + 红黑树(尾插法,链表长度 ≥ 8 转红黑树)

关键参数

参数 默认值 作用
initialCapacity 16 哈希表初始大小,必须是 2 的幂
loadFactor 0.75 触发扩容的填充阈值
threshold 16 × 0.75 = 12 实际扩容阈值
TREEIFY_THRESHOLD 8 链表长度超过此值转为红黑树
UNTREEIFY_THRESHOLD 6 红黑树退化回链表的阈值

0.75 的来源:在时间和空间成本上的折中 — 过大查询慢,过小空间浪费。

哈希算法

1
2
3
4
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
  • 高 16 位异或低 16 位:打散哈希高位,降低冲突概率
  • (n - 1) & hash 用位运算替代取模:n 为 2 的幂时,等价于 hash % n

优化建议

  1. 预知容量: initialCapacity = 预期元素数 / loadFactor + 1,减少 resize
  2. 容量必须是 2 的幂:位运算代替取模;扩容时只需看新增的最高位
  3. 重写 hashCode:分散性好的 hashCode 能显著降低冲突
  4. JDK 1.8 扩容优化:通过 (e.hash & oldCap) == 0 判断新位置,无需重新计算所有 hash

I/O 模型

传统 I/O 两大瓶颈

  1. 多次内存复制:用户空间 ↔ 内核空间 ↔ 设备,至少两次拷贝
  2. 阻塞:read/write 是同步阻塞调用,线程数随连接数线性增长

五种 I/O 模型

模型 特点 适用
阻塞 I/O 一连接一线程 连接少
非阻塞 I/O 轮询检查,EAGAIN 错误 极少使用
I/O 复用 select/poll/epoll 单线程管理多连接 高并发主流方案
信号驱动 内核 SIGIO 通知 UDP 场景
异步 I/O 内核完成所有操作后通知 Windows AIO

select / poll / epoll 对比:

维度 select poll epoll
最大 fd 数 1024 无上限 无上限
数据结构 数组 fd_set 动态数组 红黑树 + 就绪链表
扫描方式 线性扫描 线性扫描 事件回调
时间复杂度 O(n) O(n) O(1)

关于三种多路复用模式更详细的对比可以阅读 IO多路复用不同实现及对比

NIO 三大组件

1
2
3
Channel(通道)     — 双向数据通道,可读可写
Buffer(缓冲区) — 块操作的基本单位
Selector(多路复用) — 单线程监听多个 Channel 的事件

零拷贝

零拷贝 的主要优势就是减少了数据的拷贝次数与操作系统状态的切换。

  • mmap:用户空间与内核空间共享同一块物理内存,避免数据拷贝
  • DirectBuffer:JVM 堆外内存,内核可直接访问
  • Netty 的 FileRegion、transferTo 底层都基于 mmap/sendfile

Reactor 线程模型演进

1
2
单线程 Reactor   →  多线程 Reactor  →  主从 Reactor(Netty/Tomcat NIO 采用)
(Acceptor + Worker) (Acceptor 池 + Worker 池)

Tomcat 调优关键参数

参数 含义 默认 调优建议
acceptorThreadCount Acceptor 线程数 1 连接量大时调大
maxThreads Worker 线程数 200 按业务调整,不是越大越好
acceptCount accept 队列长度 100 keep-alive 关闭时可调大
maxConnections 最大连接数 10000 NIO 模式下应远大于 maxThreads

RPC 通信

关于 RPC 的发展史和约定可以阅读 选择合适的RPC ,详细介绍了 RPC 的核心内容和 SOAP、Rest 发展历程

RMI 三大瓶颈

瓶颈 来源
Java 默认序列化 性能差、不能跨语言
TCP 短连接 高并发下连接创建/销毁开销大
阻塞式 I/O 线程无法复用

高并发 RPC 优化七层路径

  1. 协议选型:TCP(可靠)/UDP(局域网高效)

  2. 连接模式:服务间使用单一长连接,避免短连接反复握手

  3. 通信框架:Netty 替代传统 Socket

  4. 报文格式:小报体、易解析

  5. 序列化:Protobuf/Hessian

    | 框架 | 性能 | 体积 | 跨语言 | 易用性 | 典型场景 |

|——|——|——|——–|——–|———|
| Java Serialization | 差 | 大 | 否 | 高 | 已不推荐 |
| Hessian | 中 | 中 | 是 | 高 | Dubbo 默认 |
| JSON (FastJson/Jackson) | 中 | 较大 | 是 | 高 | 跨语言 API |
| Kryo | 高 | 小 | 否 | 中 | RPC、游戏 |
| Protobuf | | | | 中 | 推荐首选 |

  1. TCP 参数调优:见下表

  2. Linux 内核参数:net.core.somaxconn、tcp_tw_reuse 等

Netty 是 Java 非常强大的网络编程框架,有很多高性能的设计,Netty 系列中对其的无锁串行化、核心架构都有介绍。

TCP 关键参数

参数 作用 调优建议
TCP_NODELAY 关闭 Nagle 算法 时延敏感场景关闭
SO_RCVBUF / SO_SNDBUF 收发缓冲区大小 根据包大小调整
SO_BACKLOG accept 队列长度 高并发调大
SO_KEEPALIVE 长连接保活 缩短探测间隔

关于 TCP 连接的参数还有很多,可以参考 系统性能优化之TCP篇 的内容~


Java性能优化基础篇
https://zhuwenjie0716.github.io/2026/07/04/Java性能优化基础篇/
作者
Wenjie Zhu
发布于
2026年7月4日
许可协议