假设以下代码:
public static void main(String []args) {
MessageProcessor processor = Util.createMessageProcessor();
MessageArchiver archiver = Util.createMessageArchiver();
List<Message> messages = new ArrayList<>();
for (int i = 0; i < Util.EXPECTED_TOTAL; i++) {
Message msg = Util.random();
processor.processMessage(msg);
messages.add(msg);
msg = null;
}
archiver.archiveMessages(messages, m -> m.getSubject().startsWith("A"));
Util.validate();
}
我注意到该方法在每个循环中创建了对象msg。要显式删除对象,我设置msg = null。我的问题是,Java的自动垃圾收集是在每个循环结束时还是在循环的退出之后拾取对象。
答案 0 :(得分:7)
我的问题是,Java的自动垃圾收集会在每个循环结束时还是在循环退出后拾取对象。
两者都没有,它只会在main
完成后(当GC下次运行时,如果它执行*)才会选择它,因为您正在将消息对象添加到messages
列表中,该列表维护着引用该对象,使该对象不符合GC条件。 变量不是GC'd(在您指的意义上),对象是。
只有在main
messages
超出范围时才能使该列表符合GC条件,这意味着该列表的条目符合GC条件(前提是其他任何内容仍然指代它们)。
关于循环,不需要msg = null;
语句,它没有做任何重要的事情。您的msg
变量的范围限定为循环迭代,并且在每次循环迭代结束时超出范围,这将释放它对其引用的任何内容的任何保留。
一般情况下:当您面前遇到与内存相关的性能问题时,请担心与内存相关的性能问题,而不是之前。 : - )
*“如果确实如此” - 在JVM终止时,它可以解除分配整个JVM堆而不用担心GC。
答案 1 :(得分:1)
垃圾收集发生在应用程序范围之外,它发生在底层运行时。它由您的应用程序向运行时发出请求以分配新对象(通常使用new
关键字,但在您的情况下Util.random()
将创建新对象)触发。
以最简单的形式,如果运行时在堆上找不到足够的空间来放置这个新对象,那么它将启动一个垃圾收集循环。此时它将冻结应用程序的运行,然后删除所有不再引用的对象,希望留下足够的空间来分配新的对象。此时您的申请将恢复。
每次分配对象时,更智能的GC算法都会让您缴税。税仍然暂停应用程序,但仅限于从堆的一小部分检查和删除未引用的对象所花费的时间。这导致应用程序在任何给定时间暂停很短的时间。
还有其他更复杂的GC算法版本,但这些应该足以理解基本方法。
正如在另一个答案中暗示的那样,GC是否会在示例应用程序的情况下运行取决于你绕循环的次数(Util.TOTAL_EXPECTED
评估的内容)和每个{的大小{ {1}}将对象与Java堆的大小进行比较。