2

我正在分析垃圾行为,java.lang.String看起来每次你在任何类中第一次实例化一个字符串时,它总是会产生垃圾。有人会知道为什么吗?

public abstract class AbstractTest {

    protected static String SERGIO = "sergio";

    private String someValue;

    public void init() {
        this.someValue = new String(SERGIO);
    }
}

public class Test extends AbstractTest {

    private static String JULIA = "julia";

    private Runtime runtime = Runtime.getRuntime();
    private String anotherValue;
    private String yetAnother;

    private void gc() throws InterruptedException {
        System.gc();
        Thread.sleep(100);
    }

    private long usedMemory() {
        return runtime.maxMemory() - runtime.freeMemory();
    }

    public void test() throws Exception {
        gc();
        this.anotherValue = new String(SERGIO); // a bunch of garbage is created!
        long usedMemory = usedMemory();
        gc();
        long usedMemoryAfterGC = usedMemory();
        System.out.println("Collected: " + (usedMemory - usedMemoryAfterGC));
        gc();
        this.yetAnother = new String(JULIA); // no more garbage
        usedMemory = usedMemory();
        gc();
        usedMemoryAfterGC = usedMemory();
        System.out.println("Collected: " + (usedMemory - usedMemoryAfterGC));
    }

    public static void main(String[] args) throws Exception {
        Test t = new Test();
        t.test();
    }

输出:

已收集:704336 已
收集:0

没关系。第一次它创建垃圾,然后后续实例化不产生垃圾。

奇怪的是,当您在超类中强制创建字符串时,它仍然会在您第一次在子类中实例化 String 时在子类中创建垃圾:

public void test() throws Exception {
    gc();
    init(); // creates a String in the superclass
    gc();
    this.yetAnother = new String(JULIA);
    long usedMemory = usedMemory();
    gc();
    long usedMemoryAfterGC = usedMemory();
    System.out.println("Collected: " + (usedMemory - usedMemoryAfterGC));
}

输出:

收藏:348648

知道为什么吗?

(顺便说一下,我在 MAC 和 JDK 1.6.0_37 上运行它)

EDIT1:我稍微更改了代码以明确字符串内部化不是这里的罪魁祸首,至少看起来不像。

EDIT2:如果在整个代码中将 String 更改为 Object,则会得到相同的垃圾,所以我想这与Java 中如何通过new进行对象分配有关。第一次在类中分配对象时,您会得到垃圾。第二次你没有。奇怪的是每个班级

EDIT3:我写了一篇博客文章,其中讨论了如何强制 GC 分析您的应用程序以进行垃圾创建,就像我在上面的代码中所做的那样。

4

2 回答 2

3

类中的文字字符串将在第一次引用时被“实习”(如果不是之前的话)。Interning 通常会涉及丢弃 String 的原始版本并使用 interned 版本,并且在此过程中可能会创建和丢弃更多的对象。

(当然,如果没有特殊的内部钩子,实际上没有可靠的方法来检测在任何单个操作中产生了多少垃圾,因此您的测量结果充其量是可疑的。)

于 2012-11-21T21:16:35.043 回答
1

在这里阅读彼得的回答后,很明显 TLAB 是罪魁祸首。如果您使用该选项-XX:-UseTLAB禁用 TLAB,问题就会消失。根据我从这里的理解,看起来对于 TLAB,一个线程最初分配了一大块内存以避免以后出现竞争条件。我能够通过为它设置更大的大小来证明 TLAB 是罪魁祸首,-XX:TLABSize=64m并看到这个数量被分配了。

于 2012-11-23T18:50:52.903 回答