优先复用数组而非循环内new,可避免eden区快速填满触发minor gc;需在循环外预分配、重置内容,或使用arraypool/threadlocal缓存,并警惕arrays.aslist等隐式分配。
直接复用数组、控制生命周期、避开隐式分配,是避免循环内 new 数组引爆 minor gc 的核心路径。这不是 jvm 配置问题,而是代码对堆内存的使用方式出了偏差。
优先复用数组,别每次 new
高频循环中反复 new int[100][100] 这类大数组,等于每轮都在 Eden 区塞入 40KB+ 对象,几轮就填满,必然触发 Minor GC。解决办法不是调大堆,而是复用:
- 在循环外预分配数组,循环内只重置或重写内容(如 Array.clear(arr, 0, usedLen))
- .NET 中用 ArrayPool
.Shared.Rent(n) 申请,用完立刻 Return(array, clearArray: false);漏归还会导致池枯竭 - Java 可封装为线程局部的数组缓存(ThreadLocal
),避免并发竞争,也防止跨请求持有
警惕“看似轻量”的隐式分配
有些写法表面没 new,实则悄悄分配新数组:
- Arrays.asList(arr)、Collectors.toList() 每次都生成新集合对象,底层仍是 new ArrayList
- Stream 链式调用(如 map/filter/collect)在高密度循环中会堆积大量中间节点对象,哪怕没显式 new
- 字符串拼接 "a" + "b" 在循环里可能触发 StringBuilder 扩容+复制,等价于反复分配 char[]
用空集合单例替代 new 空容器
如果循环中只是临时构造空结构(比如返回空响应体),千万别写 new HashMap() 或 new ArrayList():
- 统一改用 Collections.emptyMap()、emptyList()、emptySet()
- 它们是 JVM 预创建的静态 final 实例,零堆分配、零 GC 开销、无同步成本
- 尤其在微服务高频接口中,每秒省下上万次小对象分配,Minor GC 次数可下降明显
验证是否真由数组分配引起
别靠猜测,用工具快速定位:
- 加 -Xloggc:gc.log -XX:+PrintGCDetails,看 GC Cause 是否密集出现 Allocation Failure
- 用 jstat -gc
2000 观察 EU(Eden 使用量)是否“冲高→骤降”联动 YGC 次数陡增 - 注释掉 new 数组那行,其余逻辑照跑,对比 GC 频率变化——这是最直接的归因手段











