forked from mockito/mockito
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes mockito#1614: Clean up mocks when session finishes.
Due to the introduction of map from weak reference from mock instance to its invocation handler, Mockito became vunerable to memory leaks as there are multiple situations where Mockito could unintentionally hold strong references to mock instances in the map record. The strong references could be through spiedInstance for spies, and arguments used to faciliate method stubbing. Mockito could never know if the arguments passed in for method stubbing are also strongly referenced somewhere else or not, so Mockito needs to save a strong reference to these arguments to avoid premature GC. Therefore to solve cyclic strong references through arguments Mockito needs to explicitly know when mocks are out of their life, and clean up all internal strong references associated with them. User can call MockitoSessionBuilder#trackAndCleanUpMocks() to let the session track and clean up mocks. It also relies on implementation of mock makers to cleanUpMock(Object). SubclassByteBuddyMockMaker is a intentional no-op to avoid unnecessary behavior changes in stable features. This commit also fixes mockito#1532 and fixes mockito#1533.
- Loading branch information
Showing
25 changed files
with
656 additions
and
87 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
src/main/java/org/mockito/exceptions/misusing/MultipleTrackingMockSessionException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
/* | ||
* Copyright (c) 2019 Mockito contributors | ||
* This program is made available under the terms of the MIT License. | ||
*/ | ||
|
||
package org.mockito.exceptions.misusing; | ||
|
||
import org.mockito.MockitoSession; | ||
import org.mockito.exceptions.base.MockitoException; | ||
|
||
/** | ||
* Reports the misuse where user tries to open more than one {@link MockitoSession} that tracks and cleans up mocks. | ||
* User needs to finish previous session that tracks and cleans up mocks before trying to open a new one. Note this | ||
* doesn't prevent user from opening sessions that doesn't track or clean up mocks. | ||
* | ||
* @since 2.24.4 | ||
*/ | ||
public class MultipleTrackingMockSessionException extends MockitoException { | ||
public MultipleTrackingMockSessionException(String message) { | ||
super(message); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
/* | ||
* Copyright (c) 2019 Mockito contributors | ||
* This program is made available under the terms of the MIT License. | ||
*/ | ||
|
||
package org.mockito.internal.junit; | ||
|
||
import java.lang.ref.WeakReference; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import org.mockito.internal.listeners.AutoCleanableListener; | ||
import org.mockito.listeners.MockCreationListener; | ||
import org.mockito.mock.MockCreationSettings; | ||
import org.mockito.plugins.MockMaker; | ||
|
||
public class MockTracker implements MockCreationListener, AutoCleanableListener { | ||
|
||
private final List<WeakReference<Object>> mocks = new ArrayList<WeakReference<Object>>(); | ||
private final MockMaker mockMaker; | ||
private boolean listenerDirty; | ||
|
||
public MockTracker(MockMaker mockMaker) { | ||
this.mockMaker = mockMaker; | ||
} | ||
|
||
@Override | ||
public void onMockCreated(Object mock, MockCreationSettings settings) { | ||
synchronized (mocks) { | ||
mocks.add(new WeakReference<Object>(mock)); | ||
} | ||
} | ||
|
||
public void testFinished() { | ||
WeakReference[] localMocks; | ||
synchronized (mocks) { | ||
localMocks = mocks.toArray(new WeakReference[0]); | ||
} | ||
|
||
for (WeakReference weakMock : localMocks) { | ||
Object mock = weakMock.get(); | ||
if (mock != null) { | ||
mockMaker.cleanUpMock(mock); | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public boolean isListenerDirty() { | ||
return listenerDirty; | ||
} | ||
|
||
public void setListenerDirty() { | ||
listenerDirty = true; | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
src/main/java/org/mockito/internal/listeners/MockitoListenerUtil.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
* Copyright (c) 2019 Mockito contributors | ||
* This program is made available under the terms of the MIT License. | ||
*/ | ||
|
||
package org.mockito.internal.listeners; | ||
|
||
import java.util.LinkedList; | ||
import java.util.List; | ||
import java.util.Set; | ||
import org.mockito.internal.exceptions.Reporter; | ||
import org.mockito.listeners.MockitoListener; | ||
|
||
public class MockitoListenerUtil { | ||
public static void addListener(MockitoListener listener, Set<MockitoListener> listeners) { | ||
List<MockitoListener> delete = new LinkedList<MockitoListener>(); | ||
for (MockitoListener existing : listeners) { | ||
if (existing.getClass().equals(listener.getClass())) { | ||
if (existing instanceof AutoCleanableListener && ((AutoCleanableListener) existing).isListenerDirty()) { | ||
//dirty listener means that there was an exception even before the test started | ||
//if we fail here with redundant mockito listener exception there will be multiple failures causing confusion | ||
//so we simply remove the existing listener and move on | ||
delete.add(existing); | ||
} else { | ||
Reporter.redundantMockitoListener(listener.getClass().getSimpleName()); | ||
} | ||
} | ||
} | ||
//delete dirty listeners so they don't occupy state/memory and don't receive notifications | ||
for (MockitoListener toDelete : delete) { | ||
listeners.remove(toDelete); | ||
} | ||
listeners.add(listener); | ||
} | ||
} |
Oops, something went wrong.