I have the following code:
private void picturebox_Paint(object sender, PaintEventArgs e)
{
System.Drawing.Image tmp = img[selected].RenderImage(0); //This creates an Image object
e.Graphics.Clear(System.Drawing.Color.Black);
e.Graphics.DrawImage(tmp, movingPoint.X, movingPoint.Y, 512, 512);
tmp.Dispose();
}
This triggers when the user press PageDown, it basically displays the next image in list.
Now, I see my application memory going up and up and barely decreasing in regular intervals.
Am I disposing the tmp Image correctly? I think that's what is causing my memory issues.
Thanks.
Try:
tmp = nothing (or null - my VB and c# gets confused)
or
gc.collect()
which will force it to clear.
Related
I have a simple WPF application that allocates memory, clears it, and interacts with the garbage collector. Unfortunately I can't ever see the garbage collected automatically clear memory. For example, say I click Alloc button 10 times it allocates a gig, then if I click the New button the allocated memory does not go down. However if I force a garbage collection with GC.Collect (GC button) it does free the memory. I have enabled large collections with gcAllowVeryLargeObjects set to true, as I would like to test with more than 2 gigs of use. Any idea how I can get the garbage collector to automatically collect and free the memory?
Simple code excerpt:
List<byte[]> m_allocs = new List<byte[]>();
private void AllocClick(object sender, RoutedEventArgs e)
{
int oneHundredMegsAsBytes = 100000000;
byte[] array = new byte[oneHundredMegsAsBytes];
Array.Clear(array, 0, oneHundredMegsAsBytes);
m_allocs.Add(array);
}
private void NewClick(object sender, RoutedEventArgs e)
{
m_allocs = new List<byte[]>();
}
private void ClearClick(object sender, RoutedEventArgs e)
{
m_allocs.Clear();
}
private void GCClick(object sender, RoutedEventArgs e)
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
You don't.
The GC has been developed to only run when needed. If yours isn't running, then it doesn't need to.
You don't want it to.
GC is expensive, the GC has to stop the program and figure out what is and isn't needed. If it did this at the end of every cycle your program would grind to a crawl.
However...
You could change the high-memory percent to a lower value, which will make the GC get much more aggressive, much earlier. This will probably do you more harm than good.
When experimenting based on the new information from your comments I found a solution. If I press alloc a lot of times, eg till 10 gig use, then click clear and continue allocating more, it eventually drops down the memory usage down to a lower value, eg 2 gig.
So the garbage collector is automatically running with the sample code, it just works when memory usage is higher than what I was testing with and further allocations help.
I have a C# desktop application in which one thread that I create continously gets an image from a source(it's a digital camera actually) and puts it on a panel(panel.Image = img) in the GUI(which must be another thread as it is the code-behind of a control.
The application works but on some machines I get the following error at random time intervals(unpredictable)
************** Exception Text **************
System.InvalidOperationException: The object is currently in use elsewhere.
Then the panel turns into a red cross, red X - i think this is the invalid picture icon that is editable from the properties. The application keeps working but the panel is never updated.
From what I can tell this error comes from the control's onpaint event where I draw something else on the picture.
I tried using a lock there but no luck :(
The way I call the function that puts the image on the panel is as follows:
if (this.ReceivedFrame != null)
{
Delegate[] clients = this.ReceivedFrame.GetInvocationList();
foreach (Delegate del in clients)
{
try
{
del.DynamicInvoke(new object[] { this,
new StreamEventArgs(frame)} );
}
catch { }
}
}
this is the delegate:
public delegate void ReceivedFrameEventHandler(object sender, StreamEventArgs e);
public event ReceivedFrameEventHandler ReceivedFrame;
and this is how the function inside the control code-behind registers to it:
Camera.ReceivedFrame +=
new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame);
I also tried
del.Method.Invoke(del.Target, new object[] { this, new StreamEventArgs(b) });
instead of
del.DynamicInvoke(new object[] { this, new StreamEventArgs(frame) });
but no luck
Does anyone know how I could fix this error or at least catch the error somehow and make the thread put the images on the panel once again?
This is because Gdi+ Image class is not thread safe. Hovewer you can avoid InvalidOperationException by using lock every time when you need to Image access, for example for painting or getting image size:
Image DummyImage;
// Paint
lock (DummyImage)
e.Graphics.DrawImage(DummyImage, 10, 10);
// Access Image properties
Size ImageSize;
lock (DummyImage)
ImageSize = DummyImage.Size;
BTW, invocation is not needed, if you will use the above pattern.
I had a similar problem with the same error message but try as I might, locking the bitmap didn't fix anything for me. Then I realized I was drawing a shape using a static brush. Sure enough, it was the brush that was causing the thread contention.
var location = new Rectangle(100, 100, 500, 500);
var brush = MyClass.RED_BRUSH;
lock(brush)
e.Graphics.FillRectangle(brush, location);
This worked for my case and lesson learned: Check all the reference types being used at the point where thread contention is occurring.
Seems to me, that the same Camera object is used several times.
E.g. try to use a new buffer for each received frame. It seems to me, that while the picture box is drawing the new frame, your capture library fills that buffer again. Therefore on faster machines this might not be an issue, with slower machines it might be an issue.
I've programmed something similar once, after each received frame, we had to request to receive the next frame and set the NEW frame receive buffer in that request.
If you can not do that, copy the received frame from the camera first to a new buffer and append that buffer to a queue, or just use 2 alternating buffers and check for overruns. Either use myOutPutPanel.BeginInvoke to call the camera_ReceivedFrame method, or better have a thread running, which checks the queue, when it has a new entry it calls mnyOutPutPanel.BeginInvoke to invoke your method to set the new buffer as image on the panel.
Furthermore, once you received the buffer, use the Panel Invoke Method to invoke the setting of the image (guarantee that it runs in the window thread and not the thread from your capture library).
The example below can be called from any thread (capture library or other separate thread):
void camera_ReceivedFrame(object sender, StreamEventArgs e)
{
if(myOutputPanel.InvokeRequired)
{
myOutPutPanel.BeginInvoke(
new Camera.ReceivedFrameEventHandler(camera_ReceivedFrame),
sender,
e);
}
else
{
myOutPutPanel.Image = e.Image;
}
}
I think this is multithreading problem
Use windows golden rule and update the panel in the main thread use panel.Invoke
This should overcome cross threading exception
I am using EmguCV 3.1.0.2282 and I found that when I use an image, there are times when it doesn't release its resources and eats up memory until the PC is out of resources and an out of memory exception is thrown.
Here is a test code I did within my application. When the button is clicked, a new local image is instantiated based on an existing bitmap in memory. It will do a manual dispose if the checkbox is checked.
private void button1_Click(object sender, EventArgs e)
{
Image<Bgr, Byte> TempImage = new Image<Bgr, Byte>(CurrentLeftBitmap);
TempImage.ThresholdBinary(new Bgr(2.2, 3.3, 4.4), new Bgr(100.0, 100.0, 100.0));
if (checkBox1.Checked)
{
TempImage.Dispose();
TempImage = null;
}
}
I found each time I click on the button, memory goes down and won't be released without an application restart. Even when I do a manual dispose, memory still goes down. Funny thing is that if I commented out the ThresholdBinary step, it works fine. However, it still requires a manual dispose. I've also tried the USING statement but still the same.
My question is that has anyone encounter something similar? What is the proper way of implementing these image objects?
Yes, doing this will run you out of memory. I managed to drain my 32GB system doing 5000 iterations of your method. The problem is ThresholdBinary return another Image, you are not taking that image so the memory gets allocated but has no way to be disposed of. Change
TempImage.ThresholdBinary(new Bgr(2.2, 3.3, 4.4), new Bgr(100.0, 100.0, 100.0));
To
Image<Bgr, byte> newImage = TempImage.ThresholdBinary(new Bgr(2.2, 3.3, 4.4), new Bgr(100.0, 100.0, 100.0));
Will help.
Because they are locals the GC will eventually get around to cleaning them up. But it is always a good idea to dispose of things. So I added
TempImage.Dispose();
newImage.Dispose();
Running this 5,000 time my memory usage hardly moved.
I have a system where I am able to collect 8-bit Gray images from a camera, place the data into a WriteableBitmap and display the images on a WPF Image object. This work happens in a camera thread. I used this article to help me: How to create a BitmapImage from a pixel byte array (live video display)
What I am trying to to do is make a copy of a subset of the pixel data of the image data. During the frame update in the camera thread I am trying to create the copy of the data in a separate byte array. My code appears to work at first, but then after a few iterations my buffer variable goes from having a range of grey levels (0-255) to only having values of 255 in every array element. The variable appears to accumulate data and max out as opposed to reset each time the background worker is called. I will present my code below.
Can anyone see and describe to me what I am doing wrong? Thank you.
public partial class MainWindow : Window
{
[DllImport("Kernel32.dll",EntryPoint="RtlMoveMemory")]
public static extern void CopyMemory(IntPtr Destination, IntPtr Source,
uint Length);
// Declarations
var pData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(byte))*FrameSize);
var pFocusData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(byte))
*FrameSize);
BackgroundWorker bw = new BackgroundWorker();
static CameraWorker cWorker;
static Thread cThread;
WriteableBitmap wbm;
public IntPtr wbmBackBuffer;
const int FrameSize = 1944*2592;
// CameraWorker Event Handler
void CW_FrameUpdated(object sender, CameraWorkerEventArgs e)
{
if (!e.Updated) return;
// e.pData is an IntPtr containing the camera frame data
CopyMemory(this.wbmBackBuffer, e.pData, FrameSize);
this.Dispatcher.Invoke(wbm.Lock);
this.Dispatcher.Invoke(()=>{ wbm.AddDirtyRect(
new Int32Rect(0,0,wbm.PixelWidth,wbm.PixelHeight)); });
this.Dispatcher.Invoke(wbm.Unlock);
// The above works and I get streaming data to my view port.
// Now I want to make a copy of the pixel data to send to another thread
// for processing. This is where I am having trouble.
if (bw.IsBusy) return;
CopyMemory(pFocusData, e.pData, FrameSize);
var args = new List<object>();
args.Add(pFocusData);
bw.RunWorkerAsync(args);
}
// BackgroundWorker event handlers
void bw_DoWork(object sender, DoWorkEventArgs e)
{
// This is where I see the result of the problem when debugging.
List<object> argu = e.Argument as List<object>;
var pData = (IntPtr) argu[0];
var fullFrame = new byte[FrameSize];
Marshal.Copy(pData,fullFrame,0,FrameSize);
// Perform operations on the byte array data.
// I extract a subregion from the byte array to process, however after a
// couple of iterations, all values in fullFrame equal 255. The pData that
// is coming in should be a copy of the pixel data that is being displayed
// on the screen. While the screen keeps updating with a live video image,
// the frameData variable appears to keep accumulating rather than resetting
// with each backgroundworker call.
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Update UI elements using Dispatcher with data obtained during DoWork.
}
// Window event handlers
private void MainWindow_Initialized(object sender, EventArgs e)
{
// Set up the WBM
wbm = new WriteableBitmap(width,height,96d,96d,PixelFormats.Gray8,null);
this.wbmBackBuffer = wbm.BackBuffer;
// Set up the camera to grab frames in another thread
cWorker = new CameraWorker(camera);
cWorker.CameraFrameUpdated += CW_FrameUpdated;
cThread = new Thread(new ThreadStart(cWorker.ThreadRun));
cThread.Start();
while(!cThread.IsAlive);
// Set up the background worker for processing image data
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
// Bind the image data to the Image object that has the name "viewer"
viewer.Source = wbm;
}
private void MainWindow_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
Marshal.FreeHGlobal(pData);
}
}
EDIT: I corrected the typo that Erti-Chris Eelmaa pointed out. It was just a transcription error on my part in showing the relevant portion of my code.
EDIT #2:
1) What happens if you do your BW stuff after if (!e.Updated) return; line? Will the wbm start having this accumulation error and your BW will be fine?
There is no difference in behavior. The wbm is still fine and my BW variable accumulates.
2) BackgroundWorker.IsBusy property will be false after bw_DoWork is finished. Are you okay with this behavior?
My intent was to only process a new frame when the BW was finished. I thought that IsBusy lasts until RunWorkerCompleted is run? I don't need to process every frame.
I tried to simply create a new BW for every call to CW_FrameUpdated, but that did not solve the problem either.
3) What does the MoveMemory do? Does it copy memory from SOURCE to DESTINATION without changing anything in SOURCE? Does it even copy anything?
RtlMoveMemory (CopyMemory) is supposed to copy bytes from one area of memory to the other. I thought that by having allocated two separate spaces of equal size by the AllocHGlobal function I could do a rapid copy of the 8MB of data from one variable to the other. From what I have been reading it appears that I am doing something that the managed memory does not like. I need a fast deep copy of the data. I will try System.Buffer.BlockCopy again in case I missed something the first time.
4) What buffer variable are you speaking about? On every stage, verify ALL BUFFERS and nail down the EXACT place where the buffers differ. You need to create function DumpMemory(IntPtr unmanagedMemory). You can cast IntPtr to byte and use fixed() statement to do so.*
I will set up the DumpMemory function and let you know what I find. In the meantime, here is the data flow and roster of relevant variables.
pData (IntPtr): unmanaged memory containing 1944*2592 bytes of image data in Gray8 format with no header
wbm (WriteableBitmap): WPF variable that is bound to an Image object on the main window
wbmBackBuffer (IntPtr): local variable that points to the same location as the wbm.BackBuffer
pData is copied to wbmBackBuffer using CopyMemory, and because wbm is bound to the Image object, the current image frame is updated on the main window
pFocusData (IntPtr): this is a local pointer that is allocated memory in the size of a full frame of data
pData is copied to pFocusData through CopyMemory.
fullFrame (byte []): this is a byte array copy of pFocusData. This is where I see the accumulation occur.
EDIT #3: I finally solved the issue. It turns out there was a logic error in the sub-arrays I was selecting. My view port is about 800*400 while the image array is about 3.5 times larger. I did not scale the coordinates properly, so my sample regions were all looking at a small and similar region of the frame data. I was able to notice this by following the 4th suggestion and using a memory dump to see exactly what was going on at each step. It helped me see that nothing was wrong with the code ultimately and what I thought was accumulation was actually just the camera saturating in a small region.
The good news is that the code posted above is correct and works. I ultimately ended up using Buffer.BlockCopy and created a duplicate frame that I pass through the camera worker event args.
Thank you very much for your help Erti-Chris Eelmaa!
You're getting tired, it's time for a Christmas now :P
if (bw.IsBusy) return;
// replace this.wbmBackBuffer with pFocusData
CopyMemory(this.wbmBackBuffer, e.pData, FrameSize);
var args = new List<object>();
args.Add(pFocusData);
bw.RunWorkerAsync(args);
// edit, so looking at your code, I have few questions / suggestions.
1) What happens if you do your BW stuff after if (!e.Updated) return; line? Will the wbm start having this accumulation error and your BW will be fine?
2) BackgroundWorker.IsBusy property will be false after bw_DoWork is finished. Are you okay with this behavior?
3) What does the MoveMemory do? Does it copy memory from SOURCE to DESTINATION without changing anything in SOURCE? Does it even copy anything?
4) What buffer variable are you speaking about? On every stage, verify ALL BUFFERS and nail down the EXACT place where the buffers differ. You need to create function DumpMemory(IntPtr unmanagedMemory). You can cast IntPtr to byte* and use fixed() statement to do so.
my code extracts file icons(or even thumbs). however if i have many files it may take a while. I've tried to use background thread to load icons.
Bitmap created from icon extracted form file and stored in list. It seems that for each native bitmap it handle exist only in owner thread (that is in thread where bitmap created).
In UI thread create WPF bitmaps from those native.
So the problem is that i don't know how to use bitmaps created in background thread in UI thread.
-- or --
2b. Create wpf bitmap in background thread and use them in UI thread
But the problem is exactly the same.
You just need to freeze the images after you load them. Frozen objects are read-only and safe to use across threads. For instance :
private void _backgroundWorkerLoadImage_DoWork(object sender, DoWorkEventArgs e)
{
BitmapImage img = new BitmapImage();
img.BeginInit();
img.UriSource = imageUri;
img.EndInit();
img.Freeze();
e.Result = img;
}
void _backgroundWorkerLoadImage_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
var img = e.Result as ImageSource;
imageControl.Source = img;
}
If I understand what you are doing correctly, one way to improve performance might be to introduce some intelligence into the process which is reading the file icons.
Consider the situation in which there are lots of .DOC files in a directory and there isn't much point in reading the file icon for all of them.
You would have a cache of file icons which have been read already instead so it wouldn't be necessary to read the file icon for each of the .DOC files. There is a trade off here for holding the images in memory but you should be able to get a happy medium between performance and using too much memory.