Unity3D: optimize garbage collection - optimization

Unity3D Profiler gives me spikes that is mostly about garbage collection. In the screenshot below, the three red spikes represent three stalls that I had in my gameplay. Each of these stalls are 100+ms and most of the time was spent on TrackDependencies.
According to Unity instruction, I tried adding this to my code:
if (Time.frameCount % 30 == 0)
{
System.GC.Collect();
}
This didn't help. I still have spikes and they still take 100+ms. What exactly is going on and what can I do to optimize my game?
PS:
I am dynamically creating and destroying a lot of GameObjects in my game. Could that be a problem?
I don't have string concatenation in a loop or array as return value as caveated in the post.

This didn't help. I still have spikes and they still take 100+ms. What
exactly is going on and what can I do to optimize my game?
With System.GC.Collect you are simply force a garbage collection. If you have allocated a lot of memory to be deallocated from the last collect, than you can't avoid spikes. This is only useful in order to try to distribute garbage collection over time avoiding a massive deallocation.
I am dynamically creating and destroying a lot of GameObjects in my
game. Could that be a problem?
Probably this could be the problem.
Some hints:
Try to allocate (LoadResource and Instantiate) as much as possible of your resources at the begin of you application. If the memory required isn't too much, you can simply instantiate all the resources you need and disable/enable them on demand. If the resource memory requirements are huge this is not achievable.
Avoid ingame calls to Instantiate and Destroy. Create a pool of object where a set of resources is Instantiated when the application starts. Enable the resources you need, and disable all the rest. Instead of destroying an object release it to the pool, so that it can be disabled and reanabled on demand.
Avoid ingame calls to Resources.UnloadUnusedAssets. This can only increase the time required to Instantiate a new resource if you have previously release it. It is useful to opitmize memory usage, but calling it at costant intervals or every time you destroy an object makes no sense.

Related

Memory not fully freed

I just started creating an app using SceneKit and SpriteKit and ARC for the first time. I noticed that the memory usage is quickly increasing when I switch between different Views. My first thought was that I have memory leaks but I am not sure now. The behavior even occurs in this basic example:
for(int r=0;r<9999999;r+=1){
NSString *s=[NSString stringWithFormat:#"test%i",r];
s=nil;
}
From my understanding an NSString Object is created and directly released in this loop. I've tried this example in the iPhone-Simulator and on an iPhone and it makes the app use several hundreds MB of RAM after this loop is executed. (I am checking the memory usage with the Xcode debug navigator)
I am obviously misunderstanding something. Why is this example still retaining memory afterwards?
edit:
You could also create a new project: iOS -> Game -> Game Technology: SceneKit
Then add this into viewDidLoad:
for(int r=0;r<999999;r+=1){
SCNNode *tn=[SCNNode node];
tn=nil;
}
The memory will peak at 550MB and go down to 300MB which would be to much if there objects were fully released and removed from the RAM.
Don't rely on NSString for memory diagnostics. It has fairly atypical behavior.
This is a not-uncommon scenario, one that I've seen on S.O. more than once, that in an attempt to reduce some complicated memory problem to something simpler, the developer creates a simplified example using NSString, unaware that choosing that particular class introduces curious, unrelated behaviors. The new "Debug Memory Graph" tool or the old tried-and-true Instruments (discussed below) is the best way to diagnose the underlying issues in one's code.
As an aside, you talk about releasing objects immediately. If your method doesn't start with alloc, new, copy or mutableCopy, the object returned will not deallocated immediately after falling out of scope, because they're autorelease objects. They're not released until the autorelease pool is drained (e.g., you yield back to the run loop).
So, if your app's "high water" mark is too high, but memory eventually falls back to acceptable levels, then consider the choice of autorelease objects (and/or the introducing of your own autorelease pools). But generally this autorelease vs non-autorelease object distinction is somewhat academic unless you have a very long running loop in which you're allocating many objects prior to yielding back to the run loop.
In short, autorelease objects don't affect whether objects are deallocated or not, but merely when they are deallocated. I only mention this in response to the large for loop and the contention that objects should be deallocated immediately. The precise timing of the deallocation is impacted by the presence of autorelease objects.
Regarding your rapid memory increase in your app, it's likely to be completely unrelated to your example here. The way to diagnose this is to use Instruments (as described in WWDC 2013 Fixing Memory Issues). In short, choose "Product" - "Profile" and choose the "Leaks" tool (which will grab the essential "Allocations" tool, as well), exercise the app, and then look at precisely what was allocated and not released.
Also, Xcode 8's "Debug Object Graph" tool is incredibly useful, too, and is even easier to use. It is described in WWDC 2016's Visual Debugging with Xcode. With this tool you can see a list of objects in the left panel, and when you choose one, you can see the object graph associated with that object, so you can diagnose what unresolved references you might still have:
By the way, you might try simulating a memory warning. Cocoa objects do all sorts of caching, some of which is purged when there's memory pressure.
If you turned on any memory debugging options (e.g., zombies) on your scheme, be aware that those cause additional memory growth as it captures the associated debugging information. You might want to turn off any debugging options before analyzing leaked, abandoned or cached memory.
Bottom line, if you're seeing growth of a couple of kb per iteration and none of the objects that you instantiate are showing up and you don't have any debugging options turned on, then you might not need to worry about it. Many Cocoa objects are doing sundry cacheing that is outside of our control and it's usually negligible. But if memory is growing by mb or gb every iteration (and don't worry about the first iteration, but only subsequent ones), then that's something you really need to look at carefully.

CoreData and NSManagedObject memory state

this a question I always wanted to ask.
When I am running an iOS application in Profiler looking for allocation issues, I found out that NSManagedObject stays in memory long after they have been used and displayed, and the UIViewController who recall has been deallocated. Of course when the UIViewController is allocated again, the number is not increasing, suggesting that there's no leak, and there's some kind of object reuse by CoreData.
If I have a MyManagedObject class which has been given 'mobjc' as name, then in profiler I can see an increasing number of:
MyManagedObject_mobjc_
the number may vary, and for small amount of data, for example 100 objects in sqllite, it grows to that limit and stays there.
But it also seems that sometimes during the application lifecycle the objects are deallocated, so I suppose that CoreData itself is doing some kind of memory optimizations.
It also seems that not the whole object is retained, but rather the 'fault' of it (please forgive my english :-) ) because of the small live byte size.
Even tough a lot of fault objects would also occupy memory.
But at this point I would like some confirmation:
is CoreData really managing and optimizing object in memory ?
is there anything I can do for helping my application to retain less object as possible ?
related to the point above, do I really need to take care of this issue or not ?
do you have some link, possibly by Apple, where this specific subject is explained ?
maybe it is relevant, the app I used for testing rely on ARC and iOS 5.1.
thanks
In this SO topic, Core Data Memory Management, you can find the info you are looking for.
This, instead, is the link to Apple doc on Core Data Memory Managament.
Few tips here.
First, when you deal with Core Data you deal with an object graph. To reduce memory consumption (to prune your graph) you can do a reset on the context you are using or turn objects into fauts passing NO to refreshObject:(NSManagedObject *)object mergeChanges:(BOOL)flag method. If you pass NO to that method, you can lose unsaved changes, so pay attention to it.
Furthermore, don't use Undo Management if you don't need it. It increases memory use (by default in iOS, no undo manager is created).
Hope that helps.

Problems with memory management, autorelease, permanent heap is sometimes 250+ kb on iOS

I'm really pulling my hair out on this one, it seems that I'm having severe issues with memory management on an iOS app.
Here's the case: first I load table. When the user taps a cell, it presents a complicated view. The most memory consuming about the view is that it's loading 20+ UIImages of 500x500. There are two other tabs in that view, loading a list of media (those UIImages, but then in a table) and another simple table.
When I return back to the first table view, apparently over 250 kB is still allocated on heap. I know the view is complicated, but there's no reason to keep so much memory alive. And well, guess what, when I do switch to the view a lot, eventually the app runs out of memory and gets killed.
What I tried to solve it:
Fix all Analyze issues, so according to that there are no leaks anymore.
Check all inits again for releasing, making use of autorelease where possible.
Checking all the memory leaks using Instruments -> Leaks. In a runtime of 6, I get not more than 2 or 3 leaks.
Last, Instruments -> Allocation, checking the heap. This is what bothers me, between two marked heapshots I get a difference of 250+ kB. I've looked into it, using the detailed views. I can't get my head around it: when it's pointing to one of my methods/classes, I'm pretty sure everything in there is either released or autoreleased. It's also pointing to a lot of not-mine (say QuartzCore) methods/classes.
Also, I don't understand why autorelease is not autoreleasing. I mean, it sometimes looks like an object that is marked for autoreleasing, is released way too late. I haven't created any NSAutoreleasePools myself, so is it possible that the pool is drained only when the runtime stops? How can I periodically drain the pool (even if it's not mine).
Any help is greatly appreciated.
Kind regards,
Reinder
Used this for the heap checking: http://www.friday.com/bbum/2010/10/17/when-is-a-leak-not-a-leak-using-heapshot-analysis-to-find-undesirable-memory-growth/
Are you using imageNamed to load your images - this method will keep all images cached in memory. Try initWithContentsOfFile instead.
Watch out though; initWithContentsOfFile: won't cache at all so if you use this method a lot for the same image then you should be using imageNamed:!
I think you might want to try to optimize your design first and read guides for efficent memory management. A better understaning of the components and the runtime helps more than tracking memory allocations and will make it easier to find the leaks.
First you should always use release. Only use autorelease when necessary.
Make sure you follow the guidelines for UITableView implementations and efficient management of UITableViewCells (lazy loading, cell reusing etc.).
Check if you have retain-cycles (retained view controllers won't be deallocated).
Track the deallocation of your view controllers and objects
Don't keep stuff in memory you don't need anymore.
Don't load stuff you don't need right now.

Why does garbage collection not work with threads?

Every thing works fine in my app up until I detach a thread to read audio data out of a file and process it.
Garbage Collection seems to not know anything about what happens on this thread no matter what I do. I've tried [NSThread self], [NSThread currentThread], [[NSGarbageCollector defaultCollector]collectIfNeeded] and collectExhaustivly].
I switched to NSOperation with NSOperationQueue from NSThread.
None of these suggested solutions have worked. Most recently, I switched from NSMutableArrays to
Float32* pfArray= calloc(numFloats, sizeof(Float32));
to hold my data, and used
free(pfArray);
to free that memory. This is working better, but still leaking a decent amount.
Garbage Collection does seem to start working to some extent after the "real mem" indicated in Activity Monitor hits some arbitrary number, but when it does appear to be working, it does NOT free all the memory being used. It just doesn't let it go much higher than the arbitrary threshold.
I've read that GC is the way to go, but now I'm unsure and have an almost fully written program. Any suggestions would be very helpful. Thank you!
Garbage collection indeed works on all threads of the app. You might have unwittingly kept a reference to some object rooted, thus leaking a subgraph; without the original code, however, not much can be said.
“collectIfNeeded” implies that it will not collect if collection isn't needed.
Use Instruments's Heapshot feature (part of the Allocations instrument) to find out what objects are remaining alive, and its Object Graph instrument to find out what is still holding on to the objects that you think should no longer be needed.

Does the .dispose() method do anything at all?

I was experimenting with ways to get rid of some memory leaks within my application the other day when I realized that I know virtually nothing about cleaning up my resources. I did some research, and hoped that just calling the .dispose() would solve all of my problems. We have a table in our database that contains about 65,000 records. Obviously when I fill my dataset from the data adapter, the memory usage can get pretty high. When I called the dispose method on the dataset, I was surprised to find out that NONE of the memory got released. Why did this happen? Clearing the dataset doesn't help either.
IDisposable and thus Dispose is not used to reduced memory pressure, although in some cases it might, but instead used for deterministic cleanup.
Consider this, you construct an object that maintains an active and open connection to your database server. This connection uses resources, both on your machine, and the server.
You could of course just leave the object be when you're done with it, and eventually it'll get picked up by the garbage collector, but suppose you want to make sure at least the resources gets freed, and thus the connection closed, when you're done with it. This is where IDisposable.Dispose comes into play.
It is used to clean up resources managed by the object.
It will, however, not free the managed memory allocated to the object. This is still left to the garbage collector, that will kick in at some later time to do that.
Do you actually have a memory problem, or do you just look at the memory usage in Task Manager or similar and go "that's a bit high."?
If the latter, then you should just leave it be for now. .NET will run garbage collection more often if you have less memory available, so unless you're in a situation where you get, or might suspect you will get soon, a memory overflow condition, you're probably not going to have any problems.
Let me explain what I mean by "run less often".
If you have 8GB of memory in your machine, and only have Windows and Notepad running, most of that memory will be available. When you now run your program, even if it loads minor data blocks into memory, you can keep doing that for a long time, and memory usage will steadily grow. Exactly when the GC will kick in and try to reduce your memory footprint I don't know, but I can almost guarantee you that you will wonder why it gets so high.
Let's just for the sake of the argument say that your program will eventually use 2GB of memory.
Now, if you run your program on a machine that has less memory available, GC will occur more often, and will kick in on a lower limit, which might keep the memory usage below 500MB or possibly even less.
The important part to note here is that in order for you to get an accurate picture of how much memory application actually requires, then you can't rely on Task Manager or similar ways to measure it, you need something more targetted.
Calling Dispose() will only release unmanaged resources, such as file handles, database connections, unmanaged memory, etc. It will not release garbage collected memory.
Garbage collected memory will only get released at the next collection. Usually when the application domain memory is deamed full.
I'm going to point out something here that hasn't been explicitly mentioned: calling Dispose() will only clean up (free) unmanaged resources if the developer of the component has coded it.
What I mean is this: if you suspect you have a memory leak, calling Dispose() is not going to fix it if the original developer has done a lousy job and not correctly freed up unmanaged resources. For a bit more info, check this blog post. Take note of the statement The behaviour of Dispose is defined by the developer.
Some objects will ask one or more other entities to do something on its behalf until further notice, to the detriment of other entities. If an object which did so were to disappear without informing the former entities that their services were no longer needed, those entities would continue to uselessly act on behalf of an object that no longer needed them, to the continuing detriment of other entities that would want to use them.
In many cases, for an object "George" to tell an outside entity "Joe" that its services were no longer needed, George would have to know that its services were no longer needed. There are two normal means via which that can happen in .NET, finalization and IDIsposable.
If an object overrides a method called Finalize, then when the object is created the .NET garbage collector will add it to a list of objects with registered finalizers. If the GC discovers that there exists no rooted reference to the object other than that list, the GC will remove the object from that list and add it to a strongly-rooted queue of objects which should have their Finalize method called as soon as possible. Such an object can then use its Finalize method to inform other entities that their services are no longer required.
Although finalization-based cleanup can sometimes work, there's no guarantee of timeliness. At one point during the design of .net Microsoft may have intended that finalization would be the primary cleanup method, but for a variety of reasons it cannot safely be relied upon.
The other cleanup approach, which should be the focus of one's efforts, is IDisposable. Basically, the idea behind IDisposable is simple: for every object that implements IDisposable, there should be one entity (generally either an object or a nested execution scope) which is responsible for ensuring that that object's IDisposable.Dispose method will get called sometime within the lifetime of the universe (which would imply sometime while a reference to the object still exists), and preferably as soon as code can tell that the object's services will no longer be required.
Note that IDisposable.Dispose generally promises that any outside entities which had been asked to do something on an object's behalf will be told that they no longer need to do so, but such a promise does not imply that the number of entities is non-zero. If an object hasn't asked any outside entities to do anything on its behalf, then delivering a message "all" such entities doesn't require doing anything at all. On the other hand, the fact that a Dispose method may do nothing in some cases doesn't mean that it's guaranteed never to do anything in any case, nor that failure to call it in those cases where it would do something won't have detrimental effects.