内部类与静态嵌套类的GC性能受到影响

Tha*_*lur 23 java garbage-collection nested-class

我只是遇到了一个奇怪的效果,在跟踪它时,我注意到收集内部和静态嵌套类似乎有很大的性能差异.考虑以下代码片段:

public class Test {
    private class Pointer {
        long data;
        Pointer next;
    }
    private Pointer first;

    public static void main(String[] args) {
        Test t = null;
        for (int i = 0; i < 500; i++) {
            t = new Test();
            for (int j = 0; j < 1000000; j++) {
                Pointer p = t.new Pointer();
                p.data = i*j;
                p.next = t.first;
                t.first = p;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

那么代码所做的就是使用内部类创建一个链表.该过程重复500次(用于测试目的),丢弃上次运行中使用的对象(受GC影响).

在内存限制严格(例如100 MB)下运行时,此代码在我的计算机上执行大约需要20分钟.现在,通过简单地用静态嵌套类替换内部类,我可以将运行时减少到不到6分钟.以下是更改:

    private static class Pointer {
Run Code Online (Sandbox Code Playgroud)

                Pointer p = new Pointer();
Run Code Online (Sandbox Code Playgroud)

现在我从这个小实验中得出的结论是,使用内部类会使GC更难以确定是否可以收集对象,这使得静态嵌套类在这种情况下的速度提高了3倍以上.

我的问题是这个结论是否正确; 如果是的话是什么原因,如果没有为什么内部课程这么慢?

dis*_*tor 16

我想这可能是由于两个因素造成的.你已经触及过的第一个.第二种是使用非静态内部类导致更多的内存使用.你为什么问?因为非静态内部类也可以访问其包含类的数据成员和方法,这意味着您正在分配一个基本上扩展超类的Pointer实例.对于非静态内部类,您不会扩展包含类.这是我正在谈论的一个例子

Test.java(非静态内部类)

public class Test {
    private Pointer first;

    private class Pointer {
        public Pointer next;
        public Pointer() {
            next = null;
        }
    }

    public static void main(String[] args) {
        Test test = new Test();
        Pointer[] p = new Pointer[1000];
        for ( int i = 0; i < p.length; ++i ) {
            p[i] = test.new Pointer();
        }

        while (true) {
            try {Thread.sleep(100);}
            catch(Throwable t) {}
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Test2.java(静态内部类)

public class Test2 {
    private Pointer first;

    private static class Pointer {
        public Pointer next;
        public Pointer() {
            next = null;
        }
    }

    public static void main(String[] args) {
        Test test = new Test();
        Pointer[] p = new Pointer[1000];
        for ( int i = 0; i < p.length; ++i ) {
            p[i] = new Pointer();
        }

        while (true) {
            try {Thread.sleep(100);}
            catch(Throwable t) {}
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当两者都运行时,您可以看到非静态占用的堆空间比静态占用的多.具体来说,非静态版本使用2,279,624 B,静态版本使用10,485,760 1,800,000 B.

因此,它归结为非静态内部类使用更多内存,因为它包含对包含类的引用(至少).静态内部类不包含此引用,因此永远不会为其分配内存.通过将堆大小设置得如此之低,您实际上正在颠倒堆,从而导致3倍的性能差异.

  • 我不确定2,279,624大于10,485,760.你把它们混合了吗? (4认同)

Ed *_*aub 7

当您接近最大堆大小(-Xmx)时,垃圾收集的成本非常非线性地增加,其中JVM最终放弃并且抛出OutOfMemoryError的接近无限的人为限制.在这种特殊情况下,您会看到该曲线的陡峭部分位于内部类之间是静态的还是非静态的.除了使用更多内存和拥有更多链接之外,非静态内部类并不是真正的原因.我已经看到许多其他的代码更改"导致"GC捶打,他们恰好是将其推到边缘的不幸的闷棍,并且堆限制应该简单地设置得更高.这种非线性行为通常不应被视为代码的问题 - 它是JVM的固有特性.

当然,另一方面,膨胀是膨胀的.在目前的情况下,一个好习惯是使内部类"默认"为静态,除非访问外部实例是有用的.