How to prevent memory leak when using getframe? (edit: not memory leak)

20 views (last 30 days)
Hi,
Edit: After communicating with Mathworks support we deduced that it was not a memory leak and it was expected behavior. Storing the large frames in a large matrix using a for loop eventually consumes a lot of RAM. By making the AVI file first and writing to it each iteration the code generates the same animation but primarily uses disk memory instead of RAM.
I am creating a script to simulate the motion of multiple particles in 2D space to test various algorithms for swarm control. As part of this simulation I would like to create a real-time animation of the particles.
Currently I am using 'getframe' and 'movie2avi' to accomplish this. The issue I am having is that 'getframe' causes a memory leak. Additionally the code I have runs very slow. As an example, I am using ode45 to solve the system model for 2 particles and when I let it choose the time-steps it creates 877 points for 5 seconds of data. This nearly maxed out my RAM. As soon as I cleared the movie variable from the workspace my RAM went back to normal levels. This will obviously become a serious problem when I start to simulate many more particles (I need to simulate at least 5 and I would like to reach 10-20).
So my question is: is there a way to prevent 'getframe' from eating all my RAM or is there another way I can generate the animation that is faster and more efficient with memory resources?
I have attached the segment of my code in question along with a .mat file with data to make it run (this is the data from the 5 second simulation I described). I left out the line that creates the AVI file b/c that doesn't cause any issues. The code is recreated here for ease.
Thanks,
John
load('TestData.mat');
figure;
set (gcf, 'Units', 'normalized', 'Position', [0,0.05,1,0.875]);
M(1:size(r1a,1)-1) = getframe(gcf);
for i=2:size(r1a,1);
% Find Delta Distance Vectors
dr1=r1a(i,:)-r1a(i-1,:);
dr2=r2a(i,:)-r2a(i-1,:);
% Plot Frenet-Serret Vectors
quiver(r1a(i-1,1),r1a(i-1,2),dr1(1),dr1(2),'Color','r','AutoScale', 'OFF','ShowArrowHead','OFF','Marker','.');
hold on
quiver(r2a(i-1,1),r2a(i-1,2),dr2(1),dr2(2),'Color','m','AutoScale', 'OFF','ShowArrowHead','OFF','Marker','.');
hold on
axis equal
grid on
% Build Movie
M(i-1) = getframe(gcf);
end

Accepted Answer

Geoff Hayes
Geoff Hayes on 8 Sep 2014
John - I dont think that you are observing a memory leak, rather just a lot of memory being allocated due to the number of frames and the size of your figure. As soon as the array of frames is initialized at
M(1:size(r1a,1)-1) = getframe(gcf);
the memory spikes. Assuming that I'm in the debugger, if I run the following command immediately after the above
whos M
I observe
Name Size Bytes Class Attributes
M 1x876 4511014688 struct
That's a lot of bytes, though since the figure does take up much of the screen, each frame will have an 8-bit unsigned integer matrix (on my workstation) of dimension 894x1920x3.
An alternative to maintaining all of the frames until the end of the loop is to use the VideoWriter and write each frame to file as you build it. That way you don't have to hang on to each frame. Try adding the following to your code - replace the initialization of the frame array M with
% create the video writer with 1 fps
writerObj = VideoWriter('myVideo.avi');
writerObj.FrameRate = 1;
% open the video writer
open(writerObj);
You can adjust the frame rate as you see fit, but this will ensure one second between each frame. Then, in the for loop, replace
M(i-1) = getframe(gcf);
with
frame = getframe(gcf);
writeVideo(writerObj, frame);
And finally, outside of the for loop, close your video writer object as
% close the writer object
close(writerObj);
Your avi file should be created (mine was roughly 100 MB at 14.5 minutes) and ready to go. Note that I ran the above code on R2014a.
NOTE don't move the figure once it is set as then the frame size (for the figure) will be different on each iteration that follows the re-positioning of the figure, and an error will be thrown from the video writer. (I did this with the frame allocation method, and noticed that the memory allocated to the M array decreased over time which I thought odd...)
  2 Comments
John
John on 9 Sep 2014
Edited: John on 9 Sep 2014
Geoff,
Maybe it isn't a memory leak; I could be misusing the term. Though I don't see an instant spike in memory usage when I run the script; rather I see a gradual increase in memory usage over time until it max's out (seen from the Windows Task Manager). I don't get an out of memory error; perhaps it taps into virtual memory or starts to get rid of old stuff? As soon as the script ends and I run "clear M" from the command window to delete the movie variable my RAM instantly returns to normal. Here is a screenshot of the memory history (also attached).
So I tried what you suggested with VideWriter and it works perfectly. I do not see the memory problem in the above figure. It stays pretty flat through the process. And the video I get at the end is pretty good too. I tried to use this the other night before I posted the question and I must have done something terribly wrong b/c when I opened the video file it made my display flicker like crazy and then the computer froze. I tried to make it give me a different format than AVI so I could have a smaller file but I must have set it up incorrectly.
My problem is solved but I am still wondering why using 'getframe' in the for loop and storing the output into a preallocated structure array caused the memory issue. Both this and the VideoWriter method work basically the same except for the way the frames are being stored. Is the explanation that VideoWriter writes the frames to a file on disk while the getframe/array method stores it all in RAM (if so is that b/c it is running in a loop)?
Thanks for the assistance!
John
Geoff Hayes
Geoff Hayes on 9 Sep 2014
Glad that you got it to write out a video, John!
As for the memory problem, I realize now that I misspoke when I said As soon as the array of frames is initialized at...the memory spikes. I just assumed that it did given that I had allocated (using your code) the 877 frames of size 894x1920x3. But when I launched the activity monitor on my Mac, as I stepped through the code and passed through the line that created the array of frames, there was no change to the amount of free memory on my computer! It was only as the code started iterating, calling the getframe and initializing the ith frame, that the free memory started to decrease until (eventually) I had 4+ gigs less in free memory that I had previously.
I then tried the following: I modified your code to not call getframe but just set the cdata field to some dummy data
% M(i-1) = getframe(gcf);
M(i-1).cdata = uint8(randi(255,894,1920,3));
Note that I commented out the rest of the code in the for loop as it wasn't needed for this test. Strangely enough (to me anyway), I noticed the same behaviour! It was only on each iteration that the free memory started to decrease despite the fact that I "thought" that I had allocated memory to the array of frames (and using whos M does seem to imply that that memory was allocated).
Even doing
M(1:size(r1a,1)-1) = struct('cdata',uint8(zeros(894,1920,3)),'colormap',[]);
doesn't allocate the memory, and it isn't until we start iterating that the real memory is allocated.
So I think it is clear that this isn't a getframe problem, but more of a "how does MATLAB allocate memory for a struct array?" question because it doesn't seem to allocate the memory, but MATLAB does know that x amount of bytes should be for the struct array (as evidenced by whos).
Perhaps someone else reading this will have an idea and be able to provide some insight.

Sign in to comment.

More Answers (1)

Image Analyst
Image Analyst on 8 Sep 2014
Should your second "hold on" actually be a "hold off"? Sometimes I notice that if you keep piling stuff into the axes, it will accumulate them all, even though the underlying layers might not be seen. You might turn that last hold on into a hold off and put a "cla" or "cla reset" at the beginning of the loop to clear everything out.
  2 Comments
John
John on 9 Sep 2014
Image Analyst,
I had a similar thought and wasn't sure the best way to go about testing that theory. I tried "clf" as well as your suggestions and they don't fix the problem and furthermore they are not good for my application b/c I want each frame to keep the previous history of points. Thanks for the insight though!

Sign in to comment.

Categories

Find more on Animation in Help Center and File Exchange

Products

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!