避免超出PermGen内存和GC开销限制

时间:2013-10-24 17:38:25

标签: java garbage-collection classloader java-6 permgen

我正在尝试生成类并在运行时加载它们 我正在使用ClassLoader对象来加载类。因为我不想耗尽PermGen内存,所以我不时引用类加载器并创建一个新的加载要使用的新类。这似乎工作正常,我没有得到PermGen内存不足。 问题是,当我这样做时,一段时间后我得到以下错误:

java.lang.OutOfMemoryError: GC overhead limit exceeded 

所以我的问题是,什么时候应该取消引用类加载器以避免错误?:我应该在我的代码中监视PermGen用法,以便我取消引用类加载器和当System.gc()用法接近限制时,请致电PermGen 或者我应该采用不同的方法?
谢谢

3 个答案:

答案 0 :(得分:6)

没有一个正确的答案。

一方面,如果取消链接类加载器正在解决你的permgen泄漏问题,那么你应该继续这样做。

另一方面,“超出GC开销限制”错误意味着您的应用程序花费了太多时间进行垃圾回收。在大多数情况下,这意味着您的堆太满了。但这可能意味着两件事之一:

  • 堆太小,无法满足应用程序的要求。

  • 您的应用程序存在内存泄漏。

您可以假设问题是前者,只是增加堆大小。但如果真正的问题是后者,那么增加堆大小只是推迟不可避免的......并且正确的要做的事情是找到修复内存泄漏。


请勿致电System.gc()。它无济于事。

答案 1 :(得分:1)

您是否多次加载同一个班级? 因为你应该缓存加载的类。

如果没有,你加载了多少个类? 如果它们很多,你可能必须修复加载类的限制(这个数字可以基于堆大小,也可以基于加载类所需的内存数量),并在加载下一个类时丢弃最少使用的数量之一。

答案 2 :(得分:1)

我有类似卸载类的情况。

我正在使用几个类加载器来模拟JUnit测试中的多个JVM(这通常用于与Oracle Coherence集群一起使用,但我也成功地使用了这种技术来启动JVM内的多节点HBase / Hadoop集群)

由于各种原因,测试可能需要重新启动此类“虚拟”JVM,这意味着放弃旧的ClassLoader并创建新的ClassLoader。

如果您使用Full GC,JVM有时会延迟类卸载事件,这会在以后导致各种问题。

我发现有一种技术可用于强制JVM收集PermSpace。

public static void forcePermSpaceGC(double factor) {
    if (PERM_SPACE_MBEAN == null) {
        // probably not a HotSpot JVM
        return;
    }
    else {
        double f = ((double)getPermSpaceUsage()) / getPermSpaceLimit();
        if (f > factor) {

            List<String> bloat = new ArrayList<String>();
            int spree = 0;
            int n = 0;
            while(spree < 5) {
                try {
                    byte[] b = new byte[1 << 20];
                    Arrays.fill(b, (byte)('A' + ++n));
                    bloat.add(new String(b).intern());
                    spree = 0;
                }
                catch(OutOfMemoryError e) {
                    ++spree;
                    System.gc();
                }
            }
            return;
        }
    }
}   

Full sourcecode

我使用intern()使用String填充PermSpace,直到JVM收集它们。

但是

  • 我正在使用该技术进行测试
  • 硬件/ JVM版本的各种组合可能需要不同的阈值,因此重新启动整个JVM通常更快,而不是强制它正确收集所有垃圾