循环性能损耗主要源于循环体内重复执行的“小动作”,如每次迭代都调用list.size()、重复创建StringBuilder、反复计算固定值等;应将不变计算(如size、length、配置乘积)提至循环外,高频对象外提并重置(如sb.setLength(0)),同时提前校验null、外提边界检查,并遵循“外小内大”嵌套原则。

循环性能损耗常被低估,其实很多开销并不来自业务逻辑本身,而是循环控制结构反复执行的“小动作”——比如每次迭代都重新读取集合长度、重复创建对象、反复计算固定值。往外提变量不是炫技,而是把本该做一次的事,从循环体内挪到体外。
把不变的计算结果提前算好
像 list.size()、array.length、x * y 这类表达式,只要不依赖循环变量,就是典型的循环无关代码。JVM 编译器(如 Graal 或 C2)虽能自动外提,但显式写出更可靠,也更易读。
- ❌ 错误写法:
for (int i = 0; i < list.size(); i++)—— 每次都调用方法,对 LinkedList 可能触发遍历 - ✅ 正确写法:
for (int i = 0, n = list.size(); i < n; i++)—— size 只查一次,n 的作用域也刚好限于该循环 - 同理,
int factor = config.getMultiplier() * 100;这类配置计算,也不该放在 for 体内反复执行
把高频创建的对象提到循环外复用
循环里每轮 new 一个 StringBuilder、ArrayList 或 ByteBuffer,等于主动制造内存抖动。这些对象内部有可重用缓冲区,只需清空状态就能反复使用。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 必须满足三个前提:可复用、无状态残留、单线程或线程安全(如用 ThreadLocal 包 SimpleDateFormatter)
- 外提后一定要重置:
sb.setLength(0)、list.clear()、buffer.clear() - 避免漏掉重置导致数据污染,比如拼接字符串时上一轮内容还留在 buffer 里
把循环外的判断和检测提前执行
一些隐式开销容易被忽略,比如数组访问前的 null 检查、下标越界检查。现代 JVM 会尝试将这类安全性检测外提,但显式处理更可控。
立即学习“Java免费学习笔记(深入)”;
- 先校验输入:
if (arr == null) throw new IllegalArgumentException();,再进循环,避免每次迭代都做 null 判断 - 对固定范围的数组操作,可配合
-XX:+UseLoopPredicate让 JVM 更激进地外提边界检查 - 如果循环内有
map.get(key)且 key 不变,直接提成局部变量,别让哈希查找重复发生
嵌套循环中变量声明也值得外提
不止是业务变量,连循环控制变量本身也能优化。把 int i, j, k 统一声明在所有 for 外部,能减少栈空间重复分配的字节码指令。
- 虽然 JVM 对局部变量优化已很强,但在百万级迭代的嵌套场景下,仍可观测到轻微收益
- 更重要的是配合“外小内大”原则:把迭代次数最少的循环放最外层,此时外层变量初始化频次大幅下降(例如从 1000 次降到 10 次)
- 注意同步调整函数调用参数顺序,确保业务语义不变


















