GroovyCategorySupport和“系统”内存泄漏

kuarbcqp  于 2022-11-21  发布在  Go
关注(0)|答案(1)|浏览(129)

当我运行下面的JUnit测试时,java进程的内存不断增加。几个小时后,它使用了2go以上。但是,当我用jvisualvm查看时,堆和permgen大小是稳定的,我没有看到任何泄漏。测试是用-Xmx32m运行的

public class TestCat {  
  public static class A { }  

  @Test 
  public void testCategory() { 
    for(;;) { 
      GroovyCategorySupport.use(A.class, new Closure<Object>(null) { 
        public Object call() { return null; } 
      }); 
    } 
  } 
}

我已经用Groovy 2.4.7、Windows和JRE1.7_80、MacOS和JRE1.7_60测试过了。我无法用MacOS和JRE 1.8.0_91重现此错误
我认为这与JRE1.7中的一个bug有关,我正在寻找一种方法来缓解这个问题:

  • 我的测试可能是错误的?2怎么可能泄漏“系统”内存而不泄漏堆空间或permgen空间?
  • 这是Groovy和JRE 1.7之间的“已知”错误还是不兼容?
  • 如何使用groovy category和一个1.7的jre,而不遭受这种内存泄漏?
    编辑

我可以通过调用VMPluginFactory.getPlugin().invalidateCallSites()来重现这个bug,它可以翻译成这个“纯java”单元测试:

public class TestSwitchPoint {

  @Test
  public void testSP() {
    SwitchPoint switchPoint = new SwitchPoint();
    for(;;) {
      SwitchPoint old = switchPoint;
      switchPoint = new SwitchPoint();
      SwitchPoint.invalidateAll(new SwitchPoint[]{old});
    }
  }
}

实际上,只需要new SwitchPoint()就足够了。

7qhs6swi

7qhs6swi1#

是的,JRE中有一个bug。本机内存泄漏发生在JVM内部的以下位置:

(VM)
 - os::malloc(unsigned long, unsigned short, unsigned char*)
 - CHeapObj<(unsigned short)1792>::operator new(unsigned long, unsigned char*)
 - JNIHandleBlock::allocate_block(Thread*)
 - JNIHandleBlock::allocate_handle(oopDesc*)
 - JNIHandles::make_weak_global(Handle)
 - instanceKlass::add_member_name(int, Handle)
 - MethodHandles::init_method_MemberName(Handle, methodOopDesc*, bool, KlassHandle)
 - MethodHandles::init_method_MemberName(Handle, CallInfo&, Thread*)
 - MethodHandles::resolve_MemberName(Handle, KlassHandle, Thread*)
 - MHN_resolve_Mem
(JAVA)
 - java.lang.invoke.MethodHandleNatives.resolve(MemberName, Class)
 - java.lang.invoke.MemberName$Factory.resolve(byte, MemberName, Class)
 - java.lang.invoke.MemberName$Factory.resolveOrNull(byte, MemberName, Class)
 - java.lang.invoke.DirectMethodHandle.maybeRebind(Object)
 - java.lang.invoke.DirectMethodHandle.bindReceiver(Object)
 - java.lang.invoke.CallSite.makeDynamicInvoker()
 - java.lang.invoke.MutableCallSite.dynamicInvoker()
 - java.lang.invoke.SwitchPoint.<init>()
 - Test.main(java.lang.String[])

这是MemberNameTable的已知问题:JDK-8152271。不幸的是,仅在JDK 9中修复了该问题。幸运的是,由于在JDK-8050166中完成了MethodHandles重构,因此在JDK 8中不会出现该问题。尽管MemberNameTable问题仍然存在,但SwitchPoint()不再创建新的MemberName。后一个修复也已后移植到JDK 7u91。
如果Groovy运行时检测到Java 7+,它将使用MethodHandles。您可以通过修补VMPluginFactory以使用Java 6插件来解决此问题。下面是patch。如果包含在Groovy库之前的类路径中,它将强制Groovy运行时使用Java 6兼容的VMPlugin。
因此,您可以使用以下选项来解决内存泄漏问题:

  • 使用JRE 8(推荐)
  • 使用JRE 7u91+
  • 在类路径中包括VMPluginFactory修补程序

相关问题