1

次の JUnit テストを実行すると、Java プロセスのメモリが常に増加しています。数時間後、2合以上使用。しかし、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 のバグに関連していると思われます。この問題を軽減する方法を探しています。

  • 私のテストは多分間違っていますか?ヒープスペースまたは 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()で十分です。

4

1 に答える 1

3

はい、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 プローブは残っていますが、SwitchPoint()新しい MemberNames は作成されなくなりました。後者の修正も JDK 7u91 にバックポートされました。

Groovy ランタイムは、Java 7 以降を検出すると MethodHandles を使用します。VMPluginFactoryJava 6 プラグインを使用するようにパッチを適用することで、これを回避できます。これがパッチです。Groovy ライブラリの前にクラスパスに含まれている場合、Groovy ランタイムは Java 6 互換の VMPlugin を使用するように強制されます。

したがって、メモリ リークを回避するには、次のオプションがあります。

  • JRE 8 を使用する (推奨)
  • JRE 7u91+ を使用
  • クラスパスに VMPluginFactory パッチを含める
于 2016-09-11T20:22:39.823 に答える