如何在循环中复用StringBuilder?复用后性能不升反降问题求助
问题分析与解决方案
为什么复用StringBuilder反而更慢?
- 内存过载与GC开销:复用的StringBuilder会持续保留超大容量的内部char数组(每次region循环都会生成巨量字符),每次调用
toString()会生成一个引用该char数组的大String。即便写完文件,这个大char数组也会被StringBuilder持有,无法及时回收,导致内存占用飙升,触发频繁的垃圾回收,拖慢整体速度。而循环内创建StringBuilder的话,每次region循环结束后,旧的StringBuilder会被GC回收,内存压力小很多。 - 无效的复用逻辑:
setLength(0)不会释放内部char数组的内存,只是重置长度标记,后续append时虽然不用扩容,但大内存块的持续持有反而成为性能负担。
优化方案
1. 重构写入逻辑:直接输出到流,避免大内存缓存
不要先把所有内容拼到StringBuilder再一次性写入,而是生成一段就写一段,彻底规避大内存块的占用:
@Override public void run() { long start = System.currentTimeMillis(); // 用try-with-resources自动关闭流,避免资源泄漏 try (PrintWriter writer = new PrintWriter("res/NUMBERS " + i + ".txt")) { char letters[] = {'У', 'К', 'Е', 'Н', 'Х', 'В', 'А', 'Р', 'О', 'С', 'М', 'Т'}; // 预先缓存所有补位后的地区码和数字,避免重复计算 String[] regionCodes = new String[200]; for (int rc = 1; rc <= 199; rc++) { regionCodes[rc] = padNumber(rc, 2); } String[] numbers = new String[1000]; for (int num = 1; num < 1000; num++) { numbers[num] = padNumber(num, 3); } for (int regionCode = 1; regionCode <= 199; regionCode++) { String rcStr = regionCodes[regionCode]; for (int number = 1; number < 1000; number++) { String numStr = numbers[number]; for (char firstLetter : letters) { for (char secondLetter : letters) { for (char thirdLetter : letters) { writer.append(firstLetter) .append(numStr) .append(secondLetter) .append(thirdLetter) .append(rcStr) .append('\n'); } } } } } System.out.println(System.currentTimeMillis() - start); } catch (IOException e) { e.printStackTrace(); } }
2. 优化padNumber方法,消除低效字符串拼接
原方法每次补位都通过'0' + numberStr创建新字符串,性能极差,改用格式化API实现:
private static String padNumber(int number, int numberLength) { // 用String.format直接补位,比循环拼接高效数倍 return String.format("%0" + numberLength + "d", number); }
3. 可选:若坚持复用StringBuilder,需控制内存占用
如果一定要复用StringBuilder,每次循环后手动缩小容量,避免内存浪费:
stringBuilder.setLength(0); // 缩小容量到合理值,降低内存持有压力 stringBuilder.trimToSize();
但这种方式的性能提升远不如直接写入流的方案。
效果说明
优化后,内存占用会大幅降低,GC次数锐减,同时避免了大String的创建与销毁,整体速度会远快于两种原方案。
内容的提问来源于stack exchange,提问作者DAN
相关产品推荐
相关产品推荐

