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



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

java.lang.OutOfMemoryError: GC overhead limit exceeded 

所以我的问题是,我什么时候应该取消引用类加载器以避免这两个错误?:
我应该在我的代码中监视PermGen用法,以便我取消引用类加载器并在PermGen使用率接近限制时调用System.gc()
还是我应该遵循不同的方法?
谢谢

这个问题没有一个正确的答案。

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

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

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

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

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


不要打电话给System.gc(). 这无济于事。

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

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

我在类卸载时遇到了一些类似的情况。

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

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

有时,如果您使用完整 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;
        }
    }
}   

完整源代码

我正在使用intern()用字符串填充PermSpace,直到JVM收集它们。

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

相关内容

最新更新