2016-09-11 64 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 –