【问题标题】:Avoiding PermGen out of memory and GC overhead limit exceeded避免 PermGen 内存不足和超出 GC 开销限制
【发布时间】:2013-11-03 13:32:40
【问题描述】:

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

java.lang.OutOfMemoryError: GC overhead limit exceeded 

所以我的问题是,我应该什么时候取消引用类加载器以避免这两个错误?:
我应该在我的代码中监控 PermGen 的使用情况,以便我取消引用类加载器并调用 @987654326 @当PermGen的使用量接近极限时?
还是我应该采用不同的方法?
谢谢

【问题讨论】:

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


    【解决方案1】:

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

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

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

    • 堆对于您的应用程序的要求来说太小了。

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

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


    不要打电话给System.gc()。没用。

    【讨论】:

    • 我正在使用jvisualvm来监控堆使用情况,最大大小为1 Gb,使用量在200 mb到600 mb之间变化,因此远未达到限制。
    • @otonakav - 你的 GC 调整参数可能有问题。
    【解决方案2】:

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

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

    【讨论】:

    • 可能有一个限制可以最大限度地减少我取消引用类加载器的次数,这样 GC 就不会运行太多次,但我假设如果存在 GC 就不会运行大量堆和 PermGen 只是因为我取消引用了一个类加载器,特别是如果它要通过开销限制。
    • 我试图指出问题可能出在类加载器的使用上,而不是类加载器本身。
    【解决方案3】:

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

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

    由于各种原因,测试可能需要重新启动这种“虚拟”JVM,这意味着放弃旧的 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() 用字符串填充 PermSpace,直到 JVM 收集它们。

    但是

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-04-10
      • 2018-04-20
      • 1970-01-01
      • 1970-01-01
      • 2011-05-21
      • 2017-12-27
      • 2013-07-13
      • 2018-03-29
      相关资源
      最近更新 更多