Understanding the GC dump

I’m trying to understand what the GC dumps are telling me. The snapshots below are after a few minutes of operation and then again about 15 minutes later. The ‘Type 11 (CLASS ):’ keeps growing and the total amount of memory used keeps growing. I’m just receiving and transmitting data over a UART and the buffer sizes are small and quickly used so I can’t fathom why I am using/leaking memory. HELP! :wall:

Snapshot 1:
GC: 84msec 1592100 bytes used, 5747568 bytes available
Type 0F (STRING ): 3456 bytes
Type 11 (CLASS ): 31284 bytes
Type 12 (VALUETYPE ): 5196 bytes
Type 13 (SZARRAY ): 12504 bytes
Type 01 (BOOLEAN ): 60 bytes
Type 03 (U1 ): 1812 bytes
Type 04 (CHAR ): 900 bytes
Type 07 (I4 ): 1728 bytes
Type 0F (STRING ): 2256 bytes
Type 11 (CLASS ): 5328 bytes
Type 12 (VALUETYPE ): 420 bytes
Type 15 (FREEBLOCK ): 5747568 bytes
Type 17 (ASSEMBLY ): 41508 bytes
Type 18 (WEAKCLASS ): 144 bytes
Type 19 (REFLECTION ): 216 bytes
Type 1B (DELEGATE_HEAD ): 3996 bytes
Type 1C (DELEGATELIST_HEAD ): 192 bytes
Type 1D (OBJECT_TO_EVENT ): 552 bytes
Type 1E (BINARY_BLOB_HEAD ): 1480776 bytes
Type 1F (THREAD ): 2304 bytes
Type 20 (SUBTHREAD ): 240 bytes
Type 21 (STACK_FRAME ): 2460 bytes
Type 22 (TIMER_HEAD ): 72 bytes
Type 27 (FINALIZER_HEAD ): 1080 bytes
Type 31 (IO_PORT ): 648 bytes
Type 34 (APPDOMAIN_HEAD ): 72 bytes
Type 36 (APPDOMAIN_ASSEMBLY ): 5400 bytes

Snapshot #2:
GC: 89msec 1641732 bytes used, 5697936 bytes available
Type 0F (STRING ): 3492 bytes
Type 11 (CLASS ): 63828 bytes
Type 12 (VALUETYPE ): 5232 bytes
Type 13 (SZARRAY ): 17112 bytes
Type 01 (BOOLEAN ): 60 bytes
Type 03 (U1 ): 1812 bytes
Type 04 (CHAR ): 900 bytes
Type 07 (I4 ): 1728 bytes
Type 0F (STRING ): 2256 bytes
Type 11 (CLASS ): 9936 bytes
Type 12 (VALUETYPE ): 420 bytes
Type 15 (FREEBLOCK ): 5697936 bytes
Type 17 (ASSEMBLY ): 41508 bytes
Type 18 (WEAKCLASS ): 144 bytes
Type 19 (REFLECTION ): 216 bytes
Type 1B (DELEGATE_HEAD ): 16200 bytes
Type 1C (DELEGATELIST_HEAD ): 192 bytes
Type 1D (OBJECT_TO_EVENT ): 552 bytes
Type 1E (BINARY_BLOB_HEAD ): 1480776 bytes
Type 1F (THREAD ): 2304 bytes
Type 20 (SUBTHREAD ): 240 bytes
Type 21 (STACK_FRAME ): 2664 bytes
Type 22 (TIMER_HEAD ): 72 bytes
Type 27 (FINALIZER_HEAD ): 1080 bytes
Type 31 (IO_PORT ): 648 bytes
Type 34 (APPDOMAIN_HEAD ): 72 bytes
Type 36 (APPDOMAIN_ASSEMBLY ): 5400 bytes

a Little bit of jitter is normal,
But if it is only growing, I assume some objects are created and stored, liken in a ArrayList?

@ Jeff_Birt - if you periodically force GC does the used memory still grow?

Can you post your code? There are a couple of threads that discuss ways to prevent GC you may to check out.

https://www.ghielectronics.com/community/forum/topic?id=15274

and

https://www.ghielectronics.com/community/forum/topic?id=13007

As a general rule, if you have to force a GC, it’s nearly always a bug in your code.

Forcing a GC, however, is a very useful tool for diagnosing a memory leak. Force a GC, then do your thing, then force a GC again. If your before and after numbers don’t match, then you’ve got memory leaking.

OK, first rule of solving a GC problem is to read the WHOLE error message. The second step is to then remove your head from your backside :slight_smile:

So, I have a timer that fires every 100ms which decodes any queued up received messages and updates the display. I chose 100ms as it is a nice round number and fast enough screen update (for now). In my packet decoding and encoding methods I’m using a lot of string manipulation so I expect a fair amount of GC action to be going on. My first goal was to get something working and stable and then optimize it later.

Here is the GC dump when I get the out of memory error. I first got off track by seeing that a ‘queue’ was mentioned and I though of the queue I was using to send packets between the RX thread to the timer thread. Of course that was completely wrong. Take a look at how long the GC takes, 90ms, some of them took over 100ms. So, my 100ms timer was backing up every time the GC fired! DUH!!! ???

Now I added a timer.stop() when I enter the timer_tick handler and then restart the timer at the end of the handler. This works and is stable so now I an move on to trying to eliminate some of the GC’s work load with all of my string manipulation.

GC: 90msec 1730004 bytes used, 5609664 bytes available
Type 0F (STRING ): 3444 bytes
Type 11 (CLASS ): 123348 bytes
Type 12 (VALUETYPE ): 5232 bytes
Type 13 (SZARRAY ): 23100 bytes
Type 01 (BOOLEAN ): 60 bytes
Type 03 (U1 ): 1656 bytes
Type 04 (CHAR ): 900 bytes
Type 07 (I4 ): 1728 bytes
Type 0F (STRING ): 2256 bytes
Type 11 (CLASS ): 16080 bytes
Type 12 (VALUETYPE ): 420 bytes
Type 15 (FREEBLOCK ): 5609664 bytes
Type 17 (ASSEMBLY ): 41508 bytes
Type 18 (WEAKCLASS ): 144 bytes
Type 19 (REFLECTION ): 216 bytes
Type 1B (DELEGATE_HEAD ): 38448 bytes
Type 1C (DELEGATELIST_HEAD ): 192 bytes
Type 1D (OBJECT_TO_EVENT ): 552 bytes
Type 1E (BINARY_BLOB_HEAD ): 1480752 bytes
Type 1F (THREAD ): 1920 bytes
Type 20 (SUBTHREAD ): 192 bytes
Type 21 (STACK_FRAME ): 3684 bytes
Type 22 (TIMER_HEAD ): 72 bytes
Type 27 (FINALIZER_HEAD ): 1080 bytes
Type 31 (IO_PORT ): 648 bytes
Type 34 (APPDOMAIN_HEAD ): 72 bytes
Type 36 (APPDOMAIN_ASSEMBLY ): 5400 bytes
Failed allocation for 2050 blocks, 24600 bytes

#### Exception System.OutOfMemoryException - CLR_E_OUT_OF_MEMORY (5) 

#### Message: 
#### System.Collections.Queue::Enqueue [IP: 0000] ####
#### Microsoft.SPOT.Dispatcher::BeginInvoke [IP: 001e] ####
#### Microsoft.SPOT.DispatcherTimer::Callback [IP: 0010] ####

GC: performing heap compaction…
A first chance exception of type ‘System.OutOfMemoryException’ occurred in mscorlib.dll
An unhandled exception of type ‘System.OutOfMemoryException’ occurred in mscorlib.dll

1 Like