两段Java代码执行结果差异原因探究:println的影响
两段Java代码执行差异的原因分析
代码示例
1. 含System.out.println(i++)的代码
public class Test { public static void main(String[] args) { List<Book> books = new ArrayList<>(); int i=0; for (;;) { books.add(new Book("book")); System.out.println(i++); } } } class Book { private String name; public Book(String name) { this.name = name; } }
执行结果:代码持续运行,短时间内不会抛出内存溢出错误。
2. 无System.out.println(i++)的代码
public class Test { public static void main(String[] args) { List<Book> books = new ArrayList<>(); int i=0; for (;;) { books.add(new Book("book")); } } } class Book { private String name; public Book(String name) { this.name = name; } }
执行结果:代码很快抛出OutOfMemoryError。
差异原因分析
两段代码的核心区别在于循环执行速率和GC线程的调度机会:
第二段代码是纯内存密集型循环,JIT编译器会对其做极致优化,循环执行速度极快,对象创建的速率远远超过GC线程的处理能力。由于
ArrayList持有所有Book对象的强引用(GC无法回收这些对象),堆内存会被瞬间填满,直接触发内存溢出错误。第一段代码中的
System.out.println起到了关键的“减速”和“调度触发”作用:- 同步IO拖慢循环速度:
println是同步控制台输出操作,每次调用都要等待输出完成才能继续循环,这使得Book对象的创建速率大幅降低,堆内存增长变得非常缓慢,需要很长时间才会被填满,看起来像是“持续运行”。 - 触发GC线程调度:IO操作会让当前线程暂时阻塞并让出CPU时间片,GC线程因此获得更多运行机会,能及时清理内存中的临时对象(比如
i++转换出的字符串),进一步缓解内存压力。
- 同步IO拖慢循环速度:
需要注意的是:第一段代码并非永远不会溢出,只是因为循环速度太慢,内存增长的速度远低于JVM堆内存的容量上限,短时间内不会触发错误。
内容的提问来源于stack exchange,提问作者runoob
相关产品推荐
相关产品推荐

