kaisalmon / mockito

Automatically exported from code.google.com/p/mockito
0 stars 0 forks source link

Memory leak/Retained heap in RegisteredInvocations (LinkedList<Invocation> invocations) #470

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
What steps will reproduce the problem?

I can easily reproduce the problem while using Mockito with Arquillian because 
it is storing class references to ThreadLocal(s) which stores also reference to 
Arquillian CDIExtension which then hold reference to BeanManager which then 
holds whole deployment in memory.

Saying that the problem is not related to Arquillian (just a quicker way to 
throw OOM) so you can reproduce the problem with a simple test.

As you can see below, ModuleClassLoaders trace back to thread locals that 
Mockito has set on FinalizerThreads:

Class Name                                                                      
           | Ref. Objects | Shallow Heap | Ref. Shallow Heap | Retained Heap
--------------------------------------------------------------------------------
-----------------------------------------------------------------------------
java.lang.ref.Finalizer$FinalizerThread @ 0xaed98860  Finalizer Thread          
           |            1 |          104 |                88 |        15,528
'- threadLocals java.lang.ThreadLocal$ThreadLocalMap @ 0xaed98910               
           |            1 |           24 |                88 |        15,336
   '- table java.lang.ThreadLocal$ThreadLocalMap$Entry[16] @ 0xaed98928                    |            1 |           80 |                88 |        15,312
      '- [6] java.lang.ThreadLocal$ThreadLocalMap$Entry @ 0xc51c0318                       |            1 |           32 |                88 |         5,008
         '- value org.mockito.internal.progress.MockingProgressImpl @ 0xc51c0338           |            1 |           40 |                88 |         4,976
            '- <class> class org.mockito.internal.progress.MockingProgressImpl @ 0xc35c9538|            1 |            8 |                88 |             8
               '- <classloader> org.jboss.modules.ModuleClassLoader @ 0xba2a3e98           |            1 |           88 |                88 |   138,490,072
--------------------------------------------------------------------------------
-----------------------------------------------------------------------------

Class Name                                                                      
                                | Shallow Heap | Retained Heap
--------------------------------------------------------------------------------
---------------------------------------------------------------
java.lang.ref.Finalizer$FinalizerThread @ 0x781503c08  Finalizer Native Stack, 
Thread                           |          104 |    19 942 048
|- <class> class java.lang.ref.Finalizer$FinalizerThread @ 0x781a52678 System 
Class                             |            0 |             0
|- blockerLock java.lang.Object @ 0x780308900                                   
                                |           16 |            16
|- threadLocals java.lang.ThreadLocal$ThreadLocalMap @ 0x780308910              
                                |           24 |    19 941 856
|  |- <class> class java.lang.ThreadLocal$ThreadLocalMap @ 0x78407d858 System 
Class                             |            8 |             8
|  |- table java.lang.ThreadLocal$ThreadLocalMap$Entry[32] @ 0x7b7980308        
                                |          144 |    19 941 832
|  |  |- <class> class java.lang.ThreadLocal$ThreadLocalMap$Entry[] @ 
0x781a66e60                               |            0 |             0
|  |  |- [28] java.lang.ThreadLocal$ThreadLocalMap$Entry @ 0x780308928          
                                |           32 |            32
|  |  |- [21] java.lang.ThreadLocal$ThreadLocalMap$Entry @ 0x780308948          
                                |           32 |            32
|  |  |- [9] java.lang.ThreadLocal$ThreadLocalMap$Entry @ 0x798975400           
                                |           32 |         5 200
|  |  |- [14] java.lang.ThreadLocal$ThreadLocalMap$Entry @ 0x7989754d0          
                                |           32 |            72
|  |  |- [16] java.lang.ThreadLocal$ThreadLocalMap$Entry @ 0x798975518          
                                |           32 |            48
|  |  |- [4] java.lang.ThreadLocal$ThreadLocalMap$Entry @ 0x7992a4b00           
                                |           32 |            48
|  |  |- [0] java.lang.ThreadLocal$ThreadLocalMap$Entry @ 0x7992a4b30           
                                |           32 |     6 263 400
|  |  |  |- <class> class java.lang.ThreadLocal$ThreadLocalMap$Entry @ 
0x781a66670 System Class                 |            0 |             0
|  |  |  |- queue java.lang.ref.ReferenceQueue$Null @ 0x783c4bf00               
                                |           32 |            48
|  |  |  |- referent java.lang.ThreadLocal @ 0x798c46c80                        
                                |           16 |            16
|  |  |  |- value org.mockito.internal.progress.MockingProgressImpl @ 
0x7992a4b50                               |           40 |     6 263 368
|  |  |  |  |- <class> class org.mockito.internal.progress.MockingProgressImpl 
@ 0x798c5cf30                    |            0 |             0
|  |  |  |  |- reporter org.mockito.exceptions.Reporter @ 0x7992a4b78           
                                |           16 |            16
|  |  |  |  |- argumentMatcherStorage 
org.mockito.internal.progress.ArgumentMatcherStorageImpl @ 0x7992a4b88    |     
      16 |           104
|  |  |  |  |- iOngoingStubbing 
org.mockito.internal.stubbing.OngoingStubbingImpl @ 0x7ae68b788                 
|           16 |     6 263 208
|  |  |  |  |  |- <class> class 
org.mockito.internal.stubbing.OngoingStubbingImpl @ 0x798c5e768                 
|            0 |             0
|  |  |  |  |  |- invocationContainerImpl 
org.mockito.internal.stubbing.InvocationContainerImpl @ 0x798a32b38   |         
  32 |     6 263 192
|  |  |  |  |  |  |- <class> class 
org.mockito.internal.stubbing.InvocationContainerImpl @ 0x798c50e38          |  
         16 |            16
|  |  |  |  |  |  |- stubbed java.util.LinkedList @ 0x798a32b58                 
                                |           32 |            80
|  |  |  |  |  |  |- mockingProgress 
org.mockito.internal.progress.ThreadSafeMockingProgress @ 0x798a34cd0      |    
       16 |            16
|  |  |  |  |  |  |- answersForStubbing java.util.ArrayList @ 0x798a34ce0       
                                |           24 |            80
|  |  |  |  |  |  |- registeredInvocations 
org.mockito.internal.verification.RegisteredInvocations @ 0x798a34d30|          
 16 |     6 251 088
|  |  |  |  |  |  |  |- <class> class 
org.mockito.internal.verification.RegisteredInvocations @ 0x798c511a8     |     
       8 |             8
|  |  |  |  |  |  |  |- invocations java.util.Collections$SynchronizedList @ 
0x798a34d40                        |           24 |     6 251 072
--------------------------------------------------------------------------------
---------------------------------------------------------------

So looks like Mockito is setting thread locals that it is not properly cleaning 
up, leading to a leak from retained classloaders.
I tried to call explicitly Mockito.clear() after and before each tests but the 
memory leak still persist.

What version of the product are you using? On what operating system?
Mockito 1.9.5
Ubuntu 13.04

Please provide any additional information below.

Original issue reported on code.google.com by Grosset...@gmail.com on 23 Jan 2014 at 5:17

GoogleCodeExporter commented 8 years ago
Thanks we'll take a look.

Contributions are welcome :)

Original comment by szcze...@gmail.com on 27 Jan 2014 at 6:58

GoogleCodeExporter commented 8 years ago
Hierarchy : MockingProgressImpl > OngoingStubbingImpl > InvocationContainerImpl 
> DefaultRegisteredInvocations > LinkedList<Invocation> invocations 

I think DefaultRegisteredInvocations should expose a clear() method to cleanup 
invocations. But the method Mockito.reset() calls resetOngoingStubbing() that 
set iOngoingStubbing  to null. So I don't understand why invocations are 
retained in memory ?

Original comment by Grosset...@gmail.com on 27 Jan 2014 at 8:41

GoogleCodeExporter commented 8 years ago
In order to verify them, I believe the git repo has a fix for that allowing to 
create mocks for stubbing only, in the settings API.

Would that work for you ?

Cheers,
Brice

Original comment by brice.du...@gmail.com on 4 Mar 2014 at 9:22

GoogleCodeExporter commented 8 years ago
There : 
https://github.com/mockito/mockito/blob/master/src/org/mockito/MockSettings.java
#L233

Original comment by brice.du...@gmail.com on 4 Mar 2014 at 9:25

GoogleCodeExporter commented 8 years ago
Hi Brice,

I didn't know about that feature... but that would not work for us because we 
need to verify the number of invocations.

If you want I can validate that I can't reproduce this issue when using 
stubOnly() ?

Original comment by Grosset...@gmail.com on 4 Mar 2014 at 12:35

GoogleCodeExporter commented 8 years ago
nope it has been done already :)

This is obviously one of the trade-off of the verification after approach. 
Maybe Easymock can work better for you in this case.

Original comment by brice.du...@gmail.com on 12 Mar 2014 at 11:03

GoogleCodeExporter commented 8 years ago

Original comment by brice.du...@gmail.com on 5 Aug 2014 at 1:27