所以我在构建Strings与使用StringBuilder所花费的时间之间愚弄了一点,并且还在垃圾收集器上乱七八糟。对我来说,作为第一年的CS学生,垃圾收集器有点像魔术,我只是清理一切,“只是工作”。当我想出这个时,我有点困惑:
public class Main
{
public static void main(String[] args)
{
long time = System.currentTimeMillis();
new Test();
for (int i= 0; i < 10000; i++)
{
String s = "a";
for (int c = 0; c < 26; c++)
{
s += (Character.toString((char) ('a' + c)));
//s += "testttzzz";
}
}
System.out.println(System.currentTimeMillis() - time);
}
}
class Test
{
@Override
protected void finalize() throws Throwable
{
System.out.println("FINALIZE TEST!");
}
}
在上面的代码中,如果我取消注释行的+ =“testttzzz”,将调用垃圾收集器并输出FINALIZE TEST!
。但是,如果该行被注释掉,则在程序运行时垃圾收集器将不会运行,并且根本不会输出FINALIZE TEST!
。这是为什么?
编辑:我尝试在第一个for循环中添加 System.out.println(i);
,这样我就能看到究竟何时调用System.gc()。似乎添加该行导致垃圾收集器不再运行。我真的很困惑。
Edit2:如果它有所不同,我使用JRE 1.7.0_45
并使用eclipse版本4.3.1编译代码
Edit3:我想只是看起来额外的代码行增加的时间使垃圾收集器有更多的时间来运行并允许调用finalize()。尽管有意思。
Edit4:嗯,据Jon Skeet说,情况并非如此。垃圾收集器非常有趣
答案 0 :(得分:1)
如果没有注释掉的部分,您只使用大约260 KB的有效内存。包括开销在内,您可能会获得高达1 MB的可废弃数据。
我运行你的程序并针对VisualVM进行检查,看看发生了什么。
VM的堆大小为63.5 MB,因此在需要分配新空间之前它具有该空间。 该程序的内存使用量约为6.5 MB,因此只占其内存的10%左右。在程序的过程中,内存使用量上升到8.1 MB,所以我给你,它是1.6 MB,但仍然,这没什么。虚拟机还剩下大量空间。现在运行GC只会浪费CPU时间。
我在程序结束时添加了一个无限循环来检查是否只是程序在GC启动之前停止运行,但是没有,它没有,GC没有运行。
接下来我做的就是取消注释这一行:
s += "testttzzz";
现在内存使用量上升了一点点,但似乎足以让它超越“神奇的障碍”并且GC运行。
GC有一些指标可以决定它何时运行,而且这些指标与运行Java的一个VM不同。其中一些是:
你说
“垃圾收集器有点像魔术,它只是清理一切,”只是工作“。
这就是它应该如何。在Java中,GC的实现取决于创建VM的人员。什么时候必须运行没有标准。有一种方法System.gc(),但是没有强制GC运行,但只是“建议”现在是运行GC的好时机。 即使您在不同的系统上使用相同的VM,它也可以完全不同。举个例子:我有一个带有4 GB RAM的Windows 7 64位系统,运行32位Java VM。我的默认最大堆大小是903.12 MB。我的妻子有一个Windows 8 64位系统,4 GB RAM运行相同的32位Java VM。她的默认最大堆大小只有247.5 MB! 这意味着,GC在我的系统上比在我妻子身上更加懒惰。
作为程序员,您可以信任GC来完成它的工作(清理内存以便为程序提供足够的内存)但不要让程序的任何部分依赖于GC以任何特定的方式执行,因为它不会不得不,而且可能不会。