Java数组本身不产生内存碎片,但基本类型数组内存紧凑无碎片,包装类数组因独立对象分配引发双重碎片,超大数组易导致老年代外部碎片,应优先使用基本类型数组、复用缓存实例并避免频繁创建大数组。

Java 数组本身不产生内存碎片,但使用方式不当会显著加剧堆内存碎片化——关键不在“数组”这个结构,而在于数组元素的类型和分配行为。
基本类型数组:天然紧凑,零碎片
像 int[]、long[] 这类数组,JVM 一次性在堆中分配连续内存块:对象头 + 所有元素数据。例如 new int[10000] 占用约 40,016 字节(16 字节头 + 40,000 字节数据),中间无空隙,访问局部性好,GC 压力小。
- 元素直接内联存储,不涉及对象创建
- 内存布局完全连续,不会因释放部分元素留下间隙
- 适合高频读写、大数据量场景,如缓存、计算中间结果
包装类数组:双重碎片源
Integer[] arr = new Integer[10000] 这行代码只分配一个引用数组(约 80 KB),但真正的问题在后续初始化:每个 Integer 对象需单独分配堆空间(约 24 字节/个),且彼此地址不连续。
- 即使使用
Integer.valueOf(100),-128~127 范围外仍触发独立分配 - 频繁创建/销毁时,小对象反复分配释放,形成大量无法合并的小空闲块
- 这些碎片虽总和可观,却无法满足后续大对象或连续数组的分配请求
大数组与老年代碎片风险
超大数组(如 byte[10MB])会被 JVM 直接分配到老年代。一旦老年代出现多个分散的小空闲区,就构成典型的外部碎片——总空闲空间足够,但无连续区域容纳新大对象,最终可能触发 Full GC。
立即学习“Java免费学习笔记(深入)”;
- 数组必须连续分配,对内存连续性要求高
- 老年代碎片不易被常规 GC 整理(尤其 CMS),G1 的 Mixed GC 也受限于可用连续区域
- 频繁创建销毁大数组(如临时缓冲区),会加速碎片累积
实用规避策略
不是所有场景都能避免包装类,但可大幅降低碎片影响:
- 优先选用基本类型数组;集合场景改用 FastUtil、Trove 等原生支持基本类型的库
- 必须用
Integer[]时,控制值范围在 -128~127 内,复用缓存实例 - 避免循环中
new Integer(i),统一用valueOf;高频场景考虑对象池 - 超大数组(如百万级)可分块处理,或改用
MappedByteBuffer绕过堆内存


















