Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exception "The mock object was garbage collected." #1802

Closed
basdebakker opened this issue Oct 10, 2019 · 5 comments · Fixed by #2034
Closed

Exception "The mock object was garbage collected." #1802

basdebakker opened this issue Oct 10, 2019 · 5 comments · Fixed by #2034
Assignees

Comments

@basdebakker
Copy link

We received the following exception while running tests using Mockito, either version 3.0.0 or 3.1.0, and I'm filing this bug as requested:

Exception in thread "main" java.lang.IllegalStateException: The mock object was garbage collected. This should not happen in normal circumstances when using public API. Typically, the test class keeps strong reference to the mock object and it prevents getting the mock collected. Mockito internally needs to keep weak references to mock objects to avoid memory leaks for certain types of MockMaker implementations. If you see this exception using Mockito public API, please file a bug. For more information see issue #1313.
	at org.mockito.internal.invocation.mockref.MockWeakReference.get(MockWeakReference.java:32)
	at org.mockito.internal.invocation.InterceptedInvocation.getMock(InterceptedInvocation.java:106)
	at org.mockito.internal.stubbing.InvocationContainerImpl.invokedMock(InvocationContainerImpl.java:157)
	at org.mockito.internal.stubbing.OngoingStubbingImpl.<init>(OngoingStubbingImpl.java:22)
	at org.mockito.internal.handler.MockHandlerImpl.handle(MockHandlerImpl.java:83)
	at org.mockito.internal.handler.NullResultGuardian.handle(NullResultGuardian.java:29)
	at org.mockito.internal.handler.InvocationNotifierHandler.handle(InvocationNotifierHandler.java:35)
	at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:61)
	at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:49)
	at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor$DispatcherDefaultingToRealMethod.interceptAbstract(MockMethodInterceptor.java:126)
	at org.mockito.codegen.Runnable$MockitoMock$1840149894.run(Unknown Source)
	at MockitoTest.runTest(MockitoTest.java:13)
	at MockitoTest.main(MockitoTest.java:6)

I've reduced the problem to the following test program:

import org.mockito.*;

public class MockitoTest {

  public static void main(String[] args) {
    while (true) runTest();
  }

  private static void runTest() {
    Node list = createList();
    while (list != null) {
      Node next = list.next;
      list.object.run();
      list = next;
    }
  }

  private static Node createList() {
    Node node = null;
    for (int i = 0; i < 1000; ++i) {
      Node next = new Node();
      next.next = node;
      node = next;
    }
    return node;
  }

  private static class Node {
    final Runnable object = Mockito.mock(Runnable.class);
    Node next;
  }
}

Whether the problem occurs depends on what exactly the JVM does with the code. It looks like the mock object can be garbage collected between the call to the mocked method and Mockito trying to use the weak reference to it. I'm using AdoptOpenJDK 11.0.3.7-hotspot on Windows. My test program more often than not reproduces the exception within a few seconds, but occasionally it can run forever without failing.

@tokuhirom
Copy link
Contributor

I tried bisecting. This issue happens after this PR.
#1544

@raphw
Copy link
Member

raphw commented Oct 29, 2019

That makes sence, if the BaseStubbing instance wraps the mocks and the latter instance is referenced via a weak reference then that latter instance can be GC-ed even if the actual mock instance is still in use.

Thanks for the test, since we both have a test for the "too weak" and the "not sufficiently weak" mock applications, I hope that I can come up with a scenario that satisfies both conditions.

@ghackett
Copy link

ghackett commented Sep 2, 2020

This issue just came up as we tried to upgrade from 3.3.3 to either 3.4.6 or 3.5.9. A large chunk of our unit tests became flaky as a result. Holding onto a strong reference of MockitoAnnotations.openMocks() does not seem to have any impact. The only thing that seemed to rectify the issue was creating and retaining a MockitoSession like via MockitoJUnit.rule() (Using the MockspressoJUnitRunner shows the same flakiness as no session is initialized in it).

Considering that Mockito.mockitoSession() is labeled as optional and @Incubating, and considering that the junit rule & TestRunner have always been optional historically, this seems like a legitimate bug that will block mockito upgrades in many large codebases (especially those relying on mockito-kotlin's inline mock() method).

@raphw
Copy link
Member

raphw commented Sep 2, 2020

Yes, this needs to be addressed. The alternatives right now are unfortunately a leak or premature discarding. I think a leak is the better alternative, though since you can explicitly clear Mockito's mock store.

The updates within 3 should not have an impact but can have one as a result of changed GC cycles.

I will try to solve this one the next time I find some free time to work on Mockito.

@ghackett
Copy link

ghackett commented Sep 9, 2020

Awesome! Thanks so much for addressing this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants