Class.forName导致类加载器移动到老年代?

3

我曾看到 Class.forName() 导致了老年代被填满。我怀疑JVM内部有些东西会将ClassLoader对象转移到老年代。例如,以下代码:

public class Test  {
    public static void main(String[] args) throws Exception {
        for (int i=0 ;i<30000;i++) {
            test();
        }
    }
    private static void test() throws Exception {
        MyClassLoader cl = new MyClassLoader();
        Class.forName("java.lang.String", false, cl);
    }
}

public class MyClassLoader extends ClassLoader {}

将输出gc日志:

[GC [DefNew: 512K->64K(576K), 0.0041095秒] 512K->344K(1984K), 0.0042064秒]
[GC [DefNew: 576K->64K(576K), 0.0032096秒] 856K->682K(1984K), 0.0032937秒]
[GC [DefNew: 575K->63K(576K), 0.0032085秒] 1194K->1021K(1984K), 0.0033686秒]
[GC [DefNew: 575K->64K(576K), 0.0025146秒] 1533K->1359K(1984K), 0.0026305秒]
[GC [DefNew: 576K->64K(576K), 0.0025942秒][Tenured: 1634K->166K(1664K), 0.0169541秒] 1871K->166K(2240K), 0.0197106秒]
[GC [DefNew: 512K->64K(576K), 0.0019209秒] 678K->505K(1984K), 0.0020053秒]
[GC [DefNew: 576K->63K(576K), 0.0022846秒] 1017K->844K(1984K), 0.0024271秒]
[GC [DefNew: 575K->63K(576K), 0.0023358秒] 1356K->1182K(1984K), 0.0024235秒]
[GC [DefNew: 575K->64K(576K), 0.0025660秒][Tenured: 1457K->166K(1536K), 0.0136841秒] 1694K->166K(2112K), 0.0164004秒]

如果将Class.forName更改为loadClass

private static void test() throws Exception {
    MyClassLoader cl = new MyClassLoader();
    cl.loadClass("java.lang.String");
    //Class.forName("java.lang.String", false, cl);
}

然后gc的输出将会是:

[GC [DefNew: 512K->63K(576K), 0.0028769秒] 512K->138K(1984K), 0.0029627秒]
[GC [DefNew: 575K->0K(576K), 0.0009856秒] 650K->138K(1984K), 0.0010711秒]
[GC [DefNew: 512K->0K(576K), 0.0006255秒] 650K->138K(1984K), 0.0007062秒]
[GC [DefNew: 512K->0K(576K), 0.0002065秒] 650K->138K(1984K), 0.0002861秒]
[GC [DefNew: 512K->0K(576K), 0.0001936秒] 650K->138K(1984K), 0.0002674秒]
[GC [DefNew: 512K->0K(576K), 0.0002045秒] 650K->138K(1984K), 0.0002796秒]
[GC [DefNew: 512K->0K(576K), 0.0001704秒] 650K->138K(1984K), 0.0002481秒]
[GC [DefNew: 512K->0K(576K), 0.0002229秒] 650K->138K(1984K), 0.0003118秒]

在sun jdk1.5和1.6中复现。JVM内部(类加载和垃圾回收)到底发生了什么?

谢谢。


根据我注意到的行为,MyClassLoader对象可以进行垃圾回收。但是由于它们填充了tenured generation,会导致长时间运行时频繁发生全面垃圾回收,这是不希望发生的。 - leviastan
1个回答

1

你的样本太小了,无法真正显示GC行为差异的明确原因。首先,你的MyClassLoader实际上不应该加载java.lang.String类,因为它已经存在于引导类加载器中。你的ClassLoader对象(以及Class对象本身)应该全部移动到PermGen空间,所以你对它们在年老代中的猜测有点可疑。

基本上,你的问题“JVM内部发生了什么”太泛泛了——仅仅读取GC日志输出并不能告诉你太多信息,因为即使是看似微不足道的代码差异也可能导致完全不同的GC模式,特别是在小型测试代码上。


ClassLoader对象在堆中,而不是PermGen。 - leviastan
我使用visualgc工具来监控垃圾回收,PermGen始终保持稳定,而Young和Tenured则反复变化。此外,通过使用IBM HA工具分析堆转储,发现堆中有许多MyClassLoader对象(尽管它们符合垃圾回收的条件)。测试代码有意加载“java.lang.String”以消除任何可能的外部影响。因此,基本上我只想知道,在这样一个简单的测试用例中,使用Class.forName()时,为什么会填满tenured代,而loadClass()却没有。 - leviastan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接