Jérémie B Jérémie B - 1 year ago 158
Groovy Question

GroovyCategorySupport and "system" memory leak

When I run the following JUnit test, the memory of the java process is increasing constantly. After several hours, it uses more than 2go. However, when I look with jvisualvm, the heap and permgen size are stable, I don't see any leak. The test is run with


public class TestCat {
public static class A { }

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

I have tested it with Groovy 2.4.7, Windows and a JRE1.7_80, MacOS and JRE1.7_60.
I can't reproduce this bug with MacOS and JRE 1.8.0_91

I suppose it's related to a bug in the JRE1.7, and I am looking for a way to mitigate this issue:

  • my test is maybe wrong ? How is it possible to leak "system" memory without leaking heap space or permgen space ?

  • Is it a "known" bug or incompatibility between Groovy and a JRE 1.7 ?

  • How to use groovy category with a 1.7 jre and without suffering this memory leak ?


I can reproduce this bug by calling
, which translates with this "pure java" unit test :

public class TestSwitchPoint {

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

In fact, only
new SwitchPoint()
is enough.

Answer Source

Yes, there is a bug in JRE. Native memory leak happens inside JVM at the following place:

 - 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.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[])

It is a known issue with MemberNameTable: JDK-8152271. Unfortunately, it has been fixed only in JDK 9. By a lucky chance your problem is not seen on JDK 8 because of MethodHandles refactoring done in JDK-8050166. Although MemberNameTable probem remains, SwitchPoint() no longer creates new MemberNames. The latter fix was also backported to JDK 7u91.

Groovy runtime uses MethodHandles if it detects Java 7+. You may workaround this by patching VMPluginFactory to use Java 6 plugin. Here is the patch. If included in classpath before Groovy libraries, it will force Groovy runtime to use Java 6 - compatible VMPlugin.

So, you have the following options to workaround the memory leak:

  • use JRE 8 (recommended)
  • use JRE 7u91+
  • include VMPluginFactory patch in classpath
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download