Java:大量调用后构造函数中的数组复制意外变慢
优化高频调用的Board拷贝构造函数性能
针对你这种拷贝构造函数被20万-200万次高频调用的场景,核心要解决的是数组复制的累积开销以及对象创建/GC的额外消耗,下面是几个针对性的优化方案:
1. 替换手动循环为原生数组复制方法
原代码中手动循环复制9个元素的数组,虽然单次开销小,但高频调用下会累积出可观的性能损耗。Java提供的System.arraycopy()或Arrays.copyOf()是JVM优化过的原生方法,比Java层的for循环效率高得多(底层会用批量复制指令)。
优化后代码示例:
方案A:使用Arrays.copyOf()(代码更简洁)
public Board(Board board) { // 移除调试用的计时代码(必须去掉,否则每次调用都要读取系统时间) this.macroBoard = Arrays.copyOf(board.getMacroBoard(), 9); }
方案B:使用System.arraycopy()(性能略优,适合精确控制复制范围)
public Board(Board board) { this.macroBoard = new int[9]; System.arraycopy(board.getMacroBoard(), 0, this.macroBoard, 0, 9); }
2. 让macroBoard变为不可变,直接共享引用
如果macroBoard在创建后不会被修改(或者可以通过设计保证不可变),那完全可以避免数组复制,直接共享原对象的数组引用。这是性能最优的方案,因为完全消除了复制开销。
实现思路:
- 在
Board类中将macroBoard声明为final,并且不提供任何修改数组内容的方法; - 构造函数中仅在首次创建时复制数组,拷贝构造函数直接复用原对象的数组引用;
- 如果必须对外暴露数组,返回数组的副本(避免外部修改内部状态)。
代码示例:
public class Board { private final int[] macroBoard; // 普通构造函数:确保内部数组不可被外部修改 public Board(int[] initialMacroBoard) { this.macroBoard = Arrays.copyOf(initialMacroBoard, 9); } // 拷贝构造函数:直接共享不可变数组 public Board(Board board) { this.macroBoard = board.macroBoard; } // 对外提供的getter:返回副本防止外部修改(如果信任调用者也可以直接返回原数组) public int[] getMacroBoard() { return Arrays.copyOf(macroBoard, 9); } }
3. 使用对象池复用Board对象
如果Board对象频繁被创建和销毁,高频的对象分配会触发频繁的GC(垃圾回收),这也是性能损耗的重要来源。可以用对象池缓存空闲的Board对象,需要时直接复用,减少GC压力。
简单对象池实现示例:
import java.util.concurrent.LinkedBlockingQueue; public class BoardPool { // 根据你的并发场景调整池大小 private static final LinkedBlockingQueue<Board> POOL = new LinkedBlockingQueue<>(1000); // 获取复用对象,没有则创建新的 public static Board acquire(Board prototype) { Board board = POOL.poll(); if (board == null) { return new Board(prototype); } // 重置对象状态:将prototype的数据复制到复用对象中 System.arraycopy(prototype.getMacroBoard(), 0, board.macroBoard, 0, 9); return board; } // 释放对象回池 public static void release(Board board) { // 如果需要清理状态可以在这里处理,比如重置数组元素 POOL.offer(board); } }
4. 移除调试代码
原代码中的System.currentTimeMillis()计时逻辑是调试用的,上线前必须移除——每次调用获取系统时间都会带来额外的性能开销,在高频调用场景下会被放大。
内容的提问来源于stack exchange,提问作者Goujon
相关产品推荐
相关产品推荐

