Java 垃圾收集器、字符串和字符串

Java 垃圾收集器、字符串和字符串,java,garbage-collection,Java,Garbage Collection,因此,与使用StringBuilder相比,我在构建字符串所需的时间上有点胡闹,并且在垃圾收集器上也有点胡闹。作为CS一年级的学生,垃圾收集器对我来说也有点像魔术,它只是清理所有的东西并且“正常工作”。当我想到这个时,我有点困惑: public class Main { public static void main(String[] args) { long time = System.currentTimeMillis(); new Test

因此,与使用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!");
    }
}
公共类主
{
公共静态void main(字符串[]args)
{
长时间=System.currentTimeMillis();
新测试();
对于(int i=0;i<10000;i++)
{
字符串s=“a”;
对于(int c=0;c<26;c++)
{
s+=(Character.toString((char)('a'+c));
//s+=“testttzzz”;
}
}
System.out.println(System.currentTimeMillis()-time);
}
}
课堂测试
{
@凌驾
受保护的void finalize()抛出可丢弃的
{
System.out.println(“完成测试!”);
}
}
在上面的代码中,如果我取消注释掉行的+=“testttzzz”,垃圾收集器将被调用,它将输出
FINALIZE TEST。但是,如果该行被注释掉,垃圾收集器将不会在程序运行时运行,并且不会输出
FINALIZE TEST完全正确。为什么会这样

编辑:我尝试添加
System.out.println(I)在第一个for循环中,这样我就可以看到调用System.gc()的确切时间。添加该行似乎导致垃圾收集器不再运行。我真的很困惑

Edit2:如果有区别的话,我将使用
JRE 1.7.0_45
并使用eclipse版本4.3.1来编译代码

Edit3:我猜额外代码行增加的时间似乎给了垃圾收集器更多的运行时间,并允许调用finalize()。不过很有趣


Edit4:根据Jon Skeet的说法,情况并非如此。垃圾收集器非常有趣

如果没有注释掉的部分,您只使用了大约260 KB的有效负载内存。包括开销,您可能会获得高达1MB的可刮取数据

我运行了你的程序,并对照VisualVM检查了一下,看看发生了什么

VM以63.5MB的堆大小开始,因此它在需要分配新空间之前就拥有了该空间。 该程序一开始的内存使用量约为6.5MB,因此仅使用了大约10%的内存。在程序运行的过程中,内存使用量上升到了8.1MB,所以我给你一个,上升了1.6MB,但这仍然算不了什么。虚拟机还有很多空间。现在运行GC只会浪费CPU时间

我在程序的末尾添加了一个无限循环,以检查是否只是程序在GC启动之前停止了,但是不,不是,GC没有运行

我做的下一件事是取消这行的注释:

s += "testttzzz";
现在,内存使用量增加了一点点,但似乎刚刚足够让它超越“魔障”,GC运行起来

GC有一些指标决定它何时运行,这些指标在运行Java的VM之间有所不同。其中包括:

  • 使用的堆内存百分比
  • 已用内存的增长率
  • 可以为堆分配的可用内存
  • 当前CPU使用率
你说

“垃圾收集器对我来说也有点像魔术,它只是清理所有的东西并且“正常工作”

这就是它应该是什么样子。在Java中,GC的实现取决于创建VM的人。没有关于何时必须运行的标准。有方法System.gc(),但甚至没有强制gc运行,只是“暗示”现在是运行gc的好时机。 即使您在不同的系统上使用相同的VM,它的行为也可能完全不同。举个例子:我有一个Windows7 64位系统,带有4GB内存,运行一个32位Java虚拟机。我的默认最大堆大小是903.12 MB。我妻子有一个Windows 8 64位系统,4 GB RAM运行相同的32位Java虚拟机。她的默认最大堆大小只有247.5 MB! 这意味着,GC在我的系统上比在我妻子的系统上要懒得多


作为一名程序员,你可以信任GC来完成它的工作(清除内存,使你的程序有足够的内存),但不要让你的程序的任何部分依赖于GC以任何特定的方式执行,因为它不必也可能不会这样做。

这是因为它不是确定性行为。垃圾收集器工作得很好,它可能不会在您期望的时候收集,所以除非您正在调优GC,否则您不应该在它上面花费太多时间。@Kayaman是的,我对GC一点也不了解。我只是觉得这是一件有趣的事情,也许这不仅仅是unpredictability@arshajii如果程序在GC运行之前结束,则不会。添加更多的代码会导致GC运行。@Kayaman是的,我想是这样。相信我,确实如此。如果程序正在退出,GC将不会费心运行。这就是为什么您不能相信正在运行的终结器。