Failed allocation

I have an app running on the debugger. Seems to be doing fine but I’m getting the following message periodically. I suspect it’s not good but it’s not apparent what’s happening. How do I debug this?

Failed allocation for 685 blocks, 8220 bytes

Failed allocation for 685 blocks, 8220 bytes

Failed allocation for 685 blocks, 8220 bytes

Failed allocation for 685 blocks, 8220 bytes

Failed allocation for 685 blocks, 8220 bytes

Failed allocation for 685 blocks, 8220 bytes

Failed allocation for 685 blocks, 8220 bytes

Failed allocation for 685 blocks, 8220 bytes

that is telling you that the memory allocation is failing, and forcing GC to run. Perhaps once GC runs your failures are no longer a problem, but this just means you’re running low on memory. Have you turned off GC diagnostics ? I’d suggest you turn that on and see how memory usage is changing in your app over time, and I’d also be focussing in on what is triggering the failed allocation and check you don’t have some memory leak in your code that GC is cleaning up (That might bite you later)

I’m not sure how to interpret this. It looks like I have plenty of memory available.

GC: 2msec 31812 bytes used, 32568 bytes available

Type 0F (STRING              ):    756 bytes
Type 11 (CLASS               ):   3852 bytes
Type 12 (VALUETYPE           ):     48 bytes
Type 13 (SZARRAY             ):   5664 bytes
Type 15 (FREEBLOCK           ):  32568 bytes
Type 17 (ASSEMBLY            ):  13068 bytes
Type 18 (WEAKCLASS           ):     48 bytes
Type 19 (REFLECTION          ):     24 bytes
Type 1B (DELEGATE_HEAD       ):    324 bytes
Type 1D (OBJECT_TO_EVENT     ):    360 bytes
Type 1E (BINARY_BLOB_HEAD    ):   2268 bytes
Type 1F (THREAD              ):   1536 bytes
Type 20 (SUBTHREAD           ):    192 bytes
Type 21 (STACK_FRAME         ):   1224 bytes
Type 22 (TIMER_HEAD          ):     72 bytes
Type 27 (FINALIZER_HEAD      ):    456 bytes
Type 31 (IO_PORT             ):    468 bytes
Type 33 (I2C_XACTION         ):     48 bytes
Type 34 (APPDOMAIN_HEAD      ):     72 bytes
Type 36 (APPDOMAIN_ASSEMBLY  ):   1332 bytes

Failed allocation for 685 blocks, 8220 bytes

GC: 3msec 26280 bytes used, 38100 bytes available

Type 0F (STRING              ):    756 bytes
Type 11 (CLASS               ):   3444 bytes
Type 12 (VALUETYPE           ):     72 bytes
Type 13 (SZARRAY             ):   1524 bytes
Type 15 (FREEBLOCK           ):  38100 bytes
Type 16 (CACHEDBLOCK         ):    192 bytes
Type 17 (ASSEMBLY            ):  13068 bytes
Type 18 (WEAKCLASS           ):     48 bytes
Type 19 (REFLECTION          ):     24 bytes
Type 1B (DELEGATE_HEAD       ):    288 bytes
Type 1D (OBJECT_TO_EVENT     ):    336 bytes
Type 1E (BINARY_BLOB_HEAD    ):    552 bytes
Type 1F (THREAD              ):   1536 bytes
Type 20 (SUBTHREAD           ):    192 bytes
Type 21 (STACK_FRAME         ):   1896 bytes
Type 27 (FINALIZER_HEAD      ):    432 bytes
Type 31 (IO_PORT             ):    468 bytes
Type 33 (I2C_XACTION         ):     48 bytes
Type 34 (APPDOMAIN_HEAD      ):     72 bytes
Type 36 (APPDOMAIN_ASSEMBLY  ):   1332 bytes

and then 10 minutes later…

GC: 2msec 31992 bytes used, 32388 bytes available

Type 0F (STRING              ):    792 bytes
Type 11 (CLASS               ):   3852 bytes
Type 12 (VALUETYPE           ):     48 bytes
Type 13 (SZARRAY             ):   5664 bytes
Type 15 (FREEBLOCK           ):  32388 bytes
Type 17 (ASSEMBLY            ):  13068 bytes
Type 18 (WEAKCLASS           ):     48 bytes
Type 19 (REFLECTION          ):     24 bytes
Type 1B (DELEGATE_HEAD       ):    324 bytes
Type 1D (OBJECT_TO_EVENT     ):    360 bytes
Type 1E (BINARY_BLOB_HEAD    ):   2412 bytes
Type 1F (THREAD              ):   1536 bytes
Type 20 (SUBTHREAD           ):    192 bytes
Type 21 (STACK_FRAME         ):   1224 bytes
Type 22 (TIMER_HEAD          ):     72 bytes
Type 27 (FINALIZER_HEAD      ):    456 bytes
Type 31 (IO_PORT             ):    468 bytes
Type 33 (I2C_XACTION         ):     48 bytes
Type 34 (APPDOMAIN_HEAD      ):     72 bytes
Type 36 (APPDOMAIN_ASSEMBLY  ):   1332 bytes

Failed allocation for 685 blocks, 8220 bytes

GC: 3msec 25788 bytes used, 38592 bytes available

Type 0F (STRING              ):    744 bytes
Type 11 (CLASS               ):   3444 bytes
Type 12 (VALUETYPE           ):     72 bytes
Type 13 (SZARRAY             ):   1524 bytes
Type 15 (FREEBLOCK           ):  38592 bytes
Type 16 (CACHEDBLOCK         ):    192 bytes
Type 17 (ASSEMBLY            ):  13068 bytes
Type 18 (WEAKCLASS           ):     48 bytes
Type 19 (REFLECTION          ):     24 bytes
Type 1B (DELEGATE_HEAD       ):    288 bytes
Type 1D (OBJECT_TO_EVENT     ):    336 bytes
Type 1E (BINARY_BLOB_HEAD    ):    696 bytes
Type 1F (THREAD              ):   1536 bytes
Type 20 (SUBTHREAD           ):    192 bytes
Type 21 (STACK_FRAME         ):   1272 bytes
Type 27 (FINALIZER_HEAD      ):    432 bytes
Type 31 (IO_PORT             ):    468 bytes
Type 33 (I2C_XACTION         ):     48 bytes
Type 34 (APPDOMAIN_HEAD      ):     72 bytes
Type 36 (APPDOMAIN_ASSEMBLY  ):   1332 bytes

I notice that whenever the allocation fails the SZARRAY and the BINARY_BLOB_HEAD are larger.

Does GC run at a specific interval unless there’s an allocation failure?

Does the message just mean that GC has to be run sooner?

I don’t see any item growing steadily larger. Would that be the sign of a memory leak?

What it seems to me is that the GC is cleaning up unused objects and increasing available space - you go from 31k down to 26k used when the GC runs. The other thing GC does is make space contiguous, and depending what you were allocating it’s likely that needed to be contiguous.

I’ve figured out that the message occurs when I am create a PersistentStorage object, mount the SD file system and open a small text file on an SD card on a Fez Panda II. I can imagine that takes some dynamic memory space but I don’t see what I can do about it.

Is the “allocation failure” a problem is just some kind of warning that there could be a problem?

The FREEBLOCK is 38K bytes which sounds like plenty of free memory to me but maybe I’m misinterpreting the numbers. In the line below is the 26148 bytes used coming out of 38232 total bytes available or is the 38232 what’s available over and above the 26148 used?

GC: 3msec 26148 bytes used, 38232 bytes available

If this is the case, you should force GC to run, after you are finished working with the file.

OK. I now call Debug.GC(true) just before dealing with the file and that appears to have stopped the message.

I still don’t understand if it was just an informational message or something to be concerned about. It seems that when the message occurred it ran GC for me so there wasn’t any negative consequence.

Also how much memory do I have to spare - 38K or 38K- 26K?

GC: 3msec 26148 bytes used, 38232 bytes available

The message is “informational” in that the allocation actually worked, but it had a consequence. Because of the failed allocation, GC was forced (costs you a few msec or more of time) and then the allocation succeeded and your app continued. Your failure wasn’t because of lack of memory but because of fragmented available memory.

This is something you need to worry about, but at the same time you don’t need to worry. As you can see, GC deals with this specific situation well and you continue happily afterwards, but if you were running on very tight timing you may find the GC delay is impacting you. It’s likely that your file handling code is the cause of this issue and a small change could potentially remove the GC - if you want to look deeper, you’ll need to share code, but as a first pointer, if you search the forum for USING statements and file handling you’ll find similar threads that talk about the disposal of file objects.

To answer your memory available question: you have 26k of memory that is used by your data, and you have 38k available.

Thank you Brett. I will continue to explore this topic as time permits. I don’t mean to be a pain but I would appreciate one more clarification.

In your answer you say “To answer your memory available question: you have 26k of memory that is used by your data, and you have 38k available.” but I would like to be clear that the 38K available is in addition to the memory used - the 26K - meaning that I have a total amount of memory (used and available) on the order of 64K. Is that correct?

what device are you using?

To be clear, yes you have used 26k already, and you have not used 38k (making it available for use).

Fez Panda II

ok cool. Check out the brochure https://www.ghielectronics.com/catalog/product/256 and it tells you you have 62k for user data in RAM, which roughly correlates with your observed values.

Yes I did figure out that I clearly had that much RAM. I’m getting ready to work some more on the SD card file I/O again and I hope to figure out why with >30K of RAM I had an allocation failure. Off hand do you know if there is any way I can check in SW the size of the FREEBLOCK? I’ve been watching the GC output after I perform certain functions which is a bit tedious and uncertain.

not that granular, but

Debug.Print("Free mem : " + Debug.GC(false).ToString())

will help you.

To me it seems like you have lingering storage handles. USING will dispose of them quicker, but still may not give you the “ideal” scenario, but forcing GC to run (Debug.GC(true)) before you open a new handle may help.

@ sa319 - I’ve noticed the following issues on the Panda II:

[ul]Even when GC reports > 18K available it sometimes can’t allocate 6K. Problably because it needs a contiguous block of 6K, which isn’t available?
FileStream constructor, File.Open, and StreamReader constructor all use 8K+ of RAM, and don’t seem to give all of it back even if I force GC to run. I’ve read about some memory leaks in these stream classes in 4.1 that were fixed in 4.2?
Running GC(true) too often, seems to have the opposite affect than intended. This is when I’m running under the debugger, which may interact in some funny way?
Sometimes doing a Thread.Sleep(100) after something is closed/disposed and after GC(true) is run seems to help?[/ul]

Here’s a pattern I use

                                using (FileStream myFile = File.Open(SDCard.RootDirectory + Filename_temps, FileMode.Append))
                                {
                                    byte[] logMsg = UTF8Encoding.UTF8.GetBytes(logStr);
                                    myFile.Write(logMsg, 0, logMsg.Length);
                                    Debug.Print("Wrote " +logStr);
                                    myFile.Flush();
                                }

Jasdev has much more experience than I do in these things, so his comments are very valid. Yes you need about 8k contiguous memory for filestream, that’s confirmed and if you’re low (~18K from Jasdev’s experience) you may run into the situation where you can’t write the file. Whether there’s bug fixes or not in 4.2 is somewhat moot point since the USBizi firmware isn’t moving forward into 4.2 so we would have no way to take those fixes, but if you were so inclined you could always look at the fixes on codeplex.

@ Brett - Thanks, but I’m probably more lost than most when it comes to figuring out how to make my code efficient enough to run comfortably on the Panda II. Every time I end up spending many hours moving things around, tweaking buffers, and modifying code until my applications are able to startup and run without out of memory errors! It’s all a bit of a mystery to me… especially how the stream classes allocate and deallocate memory. They seem to ignore buffer size parameters, don’t always allocate the exact same amount of memory, and they release a varying amount of that memory when they are closed and disposed.

I tried looking at the 4.1 CPP files on CodePlex with the hope of learning how memory is allocated and deallocated, but quickly got lost in the complexity.

I’m a little concerned based on the experiences that jasdev has discussed. I’m working with this file stuff today and I’m getting these messages “Failed allocation for 685 blocks, 8220 bytes” that we’ve been talking about but everything is working OK. It appears that when there’s a failure GC runs and then everything’s OK. I know I have lots of free memory (34K+) so I suspect there won’t be a problem. But this raises two questions.

  1. I’m only aware of the problem because of the debugger. How do I get this message in my program when I’m not running the debugger? Some kind of exception?

  2. what occurs if the allocation failure isn’t/can’t be corrected?

  1. no way
  2. you will get out of memory exception.