2016-09-11 31 views
1

当我运行以下JUnit测试时,java进程的内存不断增加。几个小时后,它使用超过2go。然而,当我用jvisualvm看,堆和permgen大小是稳定的,我没有看到任何泄漏。试验是用-Xmx32mGroovyCategorySupport和“系统”内存泄漏

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的错误,我正在寻找一种方法来缓解这个问题:

  • 我的测试可能是错的?如何泄漏“系统”内存而不会泄漏堆空间或permgen空间?
  • 它是Groovy和JRE 1.7之间的“已知”错误还是不兼容?
  • 如何使用1.7 jre的groovy类别,而不会遭受这种内存泄漏?

编辑

我可以通过调用VMPluginFactory.getPlugin().invalidateCallSites(),与这种 “纯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()就够了。

回答

2

是的,JRE有一个错误。本机内存泄漏内部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 probem仍然存在,但SwitchPoint()不再创建新的MemberNames。后者的修复也被移植到了JDK 7u91。

如果Groovy运行时检测到Java 7+,则使用MethodHandles。您可以通过修补VMPluginFactory来使用Java 6插件来解决此问题。这里是patch。如果包含在Groovy库之前的类路径中,它将强制Groovy运行时使用与Java 6兼容的VMPlugin。

所以,你有以下几种选择来解决该内存泄漏:

  • 使用JRE 8(推荐)
  • 使用JRE 7u91 +
  • 包括在类路径VMPluginFactory补丁
+0

谢谢!如果我强迫Groovy不使用MethodHandles,我想假设有一个副作用? –

+0

@JérémieB可能会有性能影响,但我不认为他们会发挥重大作用。当然,这取决于特定的应用。 – apangin

+0

7u91的更新日志没有列出JDK-8050166都没有JDK-8144848,并且我没有看到任何有关MethodHandle –