Reputation: 4960
I'm transferring images from a high-FPS camera into a memory buffer (a List), and as those images are pretty large, the computer runs out of memory pretty quickly.
What I would like to do is to stop the transfer some time before the application runs out of memory. During my testing, I have found it to be consistent with the "Free Physical Memory" indicator getting close to zero.
Now the problem is that I can't find a way actually to get this value programmatically; in XP, it is not even displayed anywhere (just in the Vista/7 task manager).
I have tried all the ways I could find (WMI, performance counters, MemoryStatus, ...), but everything I got from those was just the "Available Physical Memory," which is of course not the same.
Any ideas?
Update Unfortunately, I need the data to be in memory (yes, I know I can't guarantee it will be in physical memory, but still), because the data is streamed in real-time and I need to preview it in memory after it's been stored there.
Upvotes: 6
Views: 6040
Reputation: 2124
Wanted to add my own answer because the otherwise good answer by OwenP has two important errors in the way it is using System.Runtime.MemoryFailPoint.
The first mistake is a very simple to fix: The constructor signature is public MemoryFailPoint(int sizeInMegabytes)
so the AverageFrameSize
argument should be in megabytes, not bytes. Also note the following about the size:
MemoryFailPoint operates at a granularity of 16 MB. Any values smaller than 16 MB are treated as 16 MB, and other values are treated as the next largest multiple of 16 MB.
The second mistake is that the MemoryFailPoint
instance must be kept alive until after the memory you wish to use has been allocated, and then disposed!
This can be a bit harder to fix and might require design changes to be made depending on what OP's actual code looks like.
The reason that you have to dispose it in this fashion is that the MemoryFailPoint
class keeps a process-wide record of memory reservations made from it's constructor. This is done to ensure that if two threads perform a memory-check at roughly the same time, they will not both succeed unless there is enough memory to meet the demands of both threads. (Otherwise the MemoryFailPoint
class would be useless in multi-threaded applications!)
The memory "reserved" by the constructor is unreserved when calling Dispose()
. Thus the thread should dispose the MemoryFailPoint
-instance as soon as possible after it has allocated the required memory, but not before that.
(The "as soon as possible" part is preferred but not critical. Delaying the dispose can lead to other memory-checks failing needlessly, but at least you err on the conservative side.)
The above requirement is what requires alteration to the codes design. Either the method checking for memory has to also perform the allocation, or it has to pass out the MemoryFailPoint
instance to the caller, which makes it the callers responsibility to dispose it at the correct time. (The latter is what the example code on MSDN does.)
Using the first approach (and a fixed buffer-size) might look something like this:
const int FrameSizeInMegabytes = 10; // 10MB (perhaps more is needed?)
const int FrameSizeInBytes = FrameSizeInMegabytes << 20;
// shifting by 20 is the same as multiplying with 1024 * 1024.
bool TryCreateImageBuffer(int numberOfImages, out byte[,] imageBuffer)
{
// check that it is theoretically possible to allocate the array.
if (numberOfImages < 0 || numberOfImages > 0x7FFFFFC7)
throw new ArgumentOutOfRangeException("numberOfImages",
"Outside allowed range: 0 <= numberOfImages <= 0x7FFFFFC7");
// check that we have enough memory to allocate the array.
MemoryFailPoint memoryReservation = null;
try
{
memoryReservation =
new MemoryFailPoint(FrameSizeInMegabytes * numberOfImages);
}
catch (InsufficientMemoryException ex)
{
imageBuffer = null;
return false;
}
// if you get here, there's likely to be enough memory
// available to create the buffer. Normally we can't be
// 100% sure because another thread might allocate memory
// without first reserving it with MemoryFailPoint in
// which case you have a race condition for the allocate.
// Because of this the allocation should be done as soon
// as possible - the longer we wait the higher the risk.
imageBuffer = new byte[numberOfImages, FrameSizeInBytes];
//Now that we have allocated the memory we can go ahead and call dispose
memoryReservation.Dispose();
return true;
}
0x7FFFFFC7
is the maximum indexer allowed in any dimension on arrays of single-byte types and can be found on the MSDN page about arrays.
The second approach (where the caller is responsible for the MemoryFailPoint
instance) might look something like this:
const int AverageFrameSizeInMegabytes = 10; // 10MB
/// <summary>
/// Tries to create a MemoryFailPoint instance for enough megabytes to
/// hold as many images as specified by <paramref name="numberOfImages"/>.
/// </summary>
/// <returns>
/// A MemoryFailPoint instance if the requested amount of memory was
/// available (at the time of this call), otherwise null.
/// </returns>
MemoryFailPoint GetMemoryFailPointFor(int numberOfImages)
{
MemoryFailPoint memoryReservation = null;
try
{
memoryReservation =
new MemoryFailPoint(AverageFrameSizeInMegabytes * numberOfImages);
}
catch (InsufficientMemoryException ex)
{
return null;
}
return memoryReservation;
}
This looks a lot simpler (and is more flexible), but it is now up to the caller to handle the MemoryFailPoint
instance and dispose of it at the correct point in time. (Added some mandatory documentation since I didn't come up with a good and descriptive name for the method.)
Memory is not "reserved" in the sense that it is guaranteed to be available (to the calling thread). It only means that when a thread uses MemoryFailPoint
to check for memory, assuming it succeeds, it adds it's memory size to a process-wide (static) "reserved" amount that the MemoryFailPoint
class keeps track of. This reservation will cause any other call to MemoryFailPoint
(e.g. from other threads) to perceive the total amount of free memory as the actual amount minus the current process-wide (static) "reserved" amount. (When MemoryFailPoint
instances are disposed they subtract their amount from the reserved total.). However the actual memory allocation system itself doesn't know or care about this so called "reservation" which is one of the reasons that MemoryFailPoint
doesn't have strong guarantees.
Note also that memory "reserved" is simply kept track of as an amount. Since it isn't an actual reservation of a specific segment of memory this further weakens the guarantees as is illustrated by the following frustrated comment found in the reference source:
// Note that multiple threads can still ---- on our free chunk of address space, which can't be easily solved.
It's not hard to guess what the censored word is.
Here is an interesting article about how to overcome the 2GB limit on arrays.
Also if you need to allocate very large data structures you will need to know about <gcAllowVeryLargeObjects>
which you can set in your app-config.
It is worth nothing that this doesn't really have anything to do with physical memory exclusively as the OP really wanted. Matter of fact, one of the things MemoryFailPoint
will try to do before it gives up and reports failure is to increase the size of the page-file. But it will do a very decent job of avoiding getting an OutOfMemoryException
if used correctly, which is at least half of what the OP wanted.
If you really want to force data into physical memory then, as far as I know, you have to go native with AllocateUserPhysicalPages which isn't the easiest thing in the world with a plethora of things that can go wrong, requires the appropriate permissions and is almost certainly overkill. The OS doesn't really like to be told how to manage memory so it doesn't make it easy to do so...
Upvotes: 2
Reputation: 25408
I'm late to the party, but have you considered using the System.Runtime.MemoryFailPoint class? It does a bunch of stuff to ensure that the requested allocation would succeed and throws InsufficientMemoryException if it fails; you can catch this and stop your transfer. You can probably predict an average size of incoming frames and try to allocate 3 or 4 of them, then stop acquisition when a failure is made. Maybe something like this?
const int AverageFrameSize = 10 * 1024 * 1024 // 10MB
void Image_OnAcquired(...)
{
try
{
var memoryCheck = new MemoryFailPoint(AverageFrameSize * 3);
}
catch (InsufficientMemoryException ex)
{
_camera.StopAcquisition();
StartWaitingForFreeMemory();
return;
}
// if you get here, there's enough memory for at least a few
// more frames
}
I doubt it'd be 100% foolproof, but it's a start. It's definitely more reliable than the performance counter for reasons that are explained in the other answers.
Upvotes: 8
Reputation: 4960
Thanks for all the answers.
I've been thinking about it some more and came to a conclusion that it would be quite difficult (if not impossible) to do what I initially wanted, that is to somehow detect when the application is about to run out of memory.
All answers seem to point in the same direction (to somehow keep data out of memory), however unfortunately I cannot go there, as I really "need" the data to stay inside the memory (physical if possible).
As I have to make a compromise, I decided to create a setting for user to decide the memory usage limit for captured data. It is at least easy to implement.
Upvotes: 1
Reputation: 116471
Getting an OutOfMemoryException just means that the current memory allocation could not be honored. It doesn't necessarily mean that the system or even the process is running out of memory. Imagine a hello world type application that starts off by allocating a 2 GB chunk of memory. On a 32 bit system, that will most likely trigger an exception despite the fact that the process hasn't really allocated any significant memory at this point.
A common source of OutOfMemoryExceptions is not enough contiguous memory available. I.e. plenty of memory is available, but no chunk is big enough to honor the current request. In other words trying to avoid OOM by watching the free memory counters is not really feasible.
Upvotes: 0
Reputation: 660455
Correlation is not causation. You can "run out of memory" even with loads of physical memory still free. Physical memory is almost certainly irrelevant; what you are probably running out of is address space.
People tend to think of "memory" as consuming space on a chip, but that hasn't been true for over a decade. Memory in modern operating systems is often better thought of as a large disk file that has a big hardware cache sitting on top of it to speed it up. Physical memory is just a performance optimization of disk-based memory.
If you're running out of physical memory then your performance is going to be terrible. But the scarce resource is actually address space that you are running out of. A big list has to have a large contiguous block of address space, and there might not be any block large enough left in the size you want.
Don't do that. Pull down a reasonably sized block, dump it to disk, and then deal with the file on disk as needed.
Upvotes: 9
Reputation: 22646
You can't use the free memory counter on its own in Vista/7 as a guide since it may be close to zero all the time. The reason for this is Vista/7's superfetch which uses free memory to cache stuff from disk that it thinks you're likely to use.
Linky: http://www.codinghorror.com/blog/2006/09/why-does-vista-use-all-my-memory.html
In addition if you're running a 32-bit C# process you are limited to 2GB of memory per process anyway (actually more like 1.5GB in practice before things become unstable) so even if your box shows you have loads of free memory you will still get an out of memory exception when your process hits the 2GB limit.
As Tergiver comments above, the real solution is to avoid holding all of the file in memory and instead swapping bits of the image in and out of memory as required.
Upvotes: 2