debugging memory leaks in Monotouch app - memory-leaks

I have a magazine Ipad app that dynamically adds/removes webviews from a scrollview to keep the memory usage low. For example, I have 3 view containers, and in each one I can remove existing children...
foreach (UIView subv in targetView.Subviews) {
foreach (UIView subsubv in subv.Subviews) {
subsubv.RemoveFromSuperview();
subsubv.Dispose();
}
subv.RemoveFromSuperview();
subv.Dispose();
}
and then add a new Web subview component (or scrollview with webviews) to the container.
I added the second level of children deletion, but there is probably another level in some cases. The emulator is perfect, but on the IPad, the memory usage just increases until it crashes with a low memory exception.
so.....
i) Does monotouch destroy a UIView with all nested children automatically?
ii) Instruments is not showing any memory leaks, what is the best way to debug this?
thanks.

If there are no more references to your UIView then it should get GC'd. A quick way to find out if your object was collected is to create a class that inherits from UIView (or whatever class you're using) then create a finalizer/destructor for the class. In the finalizer do a console write.
~MyView()
{
Console.WriteLine("MyView: finalized");
}
When you run your program make sure you see this being written to the console when you expect the object to be collected. In my experience, the simulator collects the objects almost as instantly as the object looses it's last reference. On the device it may not be as quick.

Related

Will "addValueChangeListener" cause memory leak?

I am using a tree within the left panel and a detail view (VerticalLayout with labels and buttons) within the right panel. Each node contains extensive data, that are saved by this way:
IndexedContainer nodeData;
.
.
.
tree.getContainerDataSource()
.getContainerProperty(itemId, "nodeData").setValue(nodeData)
The content for selected node is displayed in detail view. It can be modified using a modal window (clicking "Edit" button in detail view).
I am using nodeData.addValueChangeListener(detailView). The detail view implements function for updating its own content when the modal window saves nodeData. It works fine, but I have worries about memory leak. The right panel content (detailView) is destroyed when I select another node. Another view can be placed on the same place, because the nodeData of another node can have another structure. But nodeData container lives for long time and I can select it more times. My question is: Does it cause memory leak? Or the system is able to remove unaccessible object that are referenced only as a listener?
Thank you for telling me, if it can be problem. Any advices are appreciated.
I wouldn't worry about a memory leak in this case, as the listener is being injected into the component.
You can detect if there is a memory leak or not from the server, for instance, stopping Tomcat server will through some memory leak errors in the logs if there is a memory leak or any unreachable objects.
However, I would recommend overriding the detach() method on your tree to make sure you cleanup your resources once a view or a component is detached from the screen/view.
The problem is solved. It is possible to check out the component from listening changes, when it is orphaned:
if(!isAttached())
nodeData.removeValueChangeListener(this);

Creating views inside a worker thread

I have a requirement to generate a bitmap out of an EditText and then perform some manipulations on it.
My main concern is not to call View.buildDrawingCache() method on the UI thread and possibly block it, especially when talking about large screens (i.e. Nexus 10) since the EditText will occupy about 80% of the available screen size.
I execute Runnables inside a ThreadPoolExecutor, those will inflate dummy views on a worker thread and set all the required attributes to them, then simply call buildDrawingCache() & getDrawingCache() to generate a bitmap.
This works perfect on some devices yet recently I have encountered a few devices that crash with the following message:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
I understand why this happens, as some phones must have modified implementation for EditText that creates a Handler and thus requires Looper.prepare() to be called first.
From what I've read online there is no issue with calling Looper.prepare() inside a worker thread though some stated it is highly unrecommended yet I could not find a reason for that.
Other than that, most posts related to this issue state you are not supposed to inflate views inside a background thread, probably due to the following from Android's official documentation (Processes and Threads):
"Do not access the Android UI toolkit from outside the UI thread"
What is the recommended approach to dealing with this problem?
Is there any harm in calling build/get drawingcache from the main thread? (performance-wise)
Will calling Looper.prepare() inside my worker thread solve this problem?
EDIT
Just to elaborate on my specific requirement, I have a user-interface consisting of an ImageView and a custom EditText on top of it, the EditText can change it's font and color according to the user selection, it can be zoomed in/out using "pinch to zoom" gesture and can also be dragged around to allow the user to reposition it on top of the image.
Eventually what I do is create a dummy view inside my worker thread using the exact same values (width, height, position) it currently has on the UI and then generate it's drawingcache, the original image's bitmap is decoded again from a local file.
Once the two bitmaps are ready I merge them into a single bitmap for future use.
So to put it simple, is there anything wrong with executing the following code (from within a background thread):
Call Looper.prepare()
Create a new view with application context, call measure() & layout() manually and then build+get drawingcache from it, i.e.:
Looper.prepare();
EditText view = new EditText(appContext);
view.setText("some text");
view.setLayoutParams(layoutParams);
view.measure(
View.MeasureSpec.makeMeasureSpec(targetWidth, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(targetHeight, View.MeasureSpec.EXACTLY));
view.layout(0, 0, targetWidth, targetHeight);
view.buildDrawingCache();
Bitmap bitmap = view.getDrawingCache();
How does this apply to the restriction with not accessing the Android UI toolkit from outside the UI thread, what could possibly go wrong?
In your case, you can do it of course, but be carefull only reading values from UI data, to avoid synchronizations bug.
Also you should not recreate the EditText from the background thread, it will be more efficient to directly access the already existant one instead:
Looper.prepare();
myEditText.setDrawingCacheEnabled(true);
Bitmap bitmap = myEditText.getDrawingCache();
If your question is : why it is not recommanded by android guidelines, here is a good SO answer to your question.
Calling View.buildDrawingCache() calls Bitmap.nativeCreate which can be a large allocation, so yes, it can be potentially harmful to run on main thread. I don't see a problem with calling Looper.prepare() in your background thread. However, it's unclear what you are trying to achieve and there may be a better solution to your problem.
The reason you are not supposed to the UI toolkit from other threads is that it is not written to be thread safe it is written under the assumption that only one thread runs it. This means it's really hard to tell what can go wrong, the bad effects, if any, will mostly happen in an un-repeatable due to specific timing of threads.
Your description of what you are trying to do it not too clear. In your case, I would just allocate a large bitmap, and draw text into it. Why are you using the EditText in the first place ? It seems like a kind of a hack, and hacks tend to break eventually.
Why View.buildDrawingCache()? What about using View.draw(Canvas canvas) to manually render to a Canvas backed by a Bitmap? Method seems simple enough to not cause problems on background threads.
EditText edit = (EditText)findViewById(R.id.edit);
edit.buildDrawingCache();
ImageView img = (ImageView)findViewById(R.id.test);
img.setImageBitmap(edit.getDrawingCache());
Lalit when you try to build the cache in the onCreate method, the drawing hasn't happened yet so the drawingCache should have nothing. Either put the buildDrawingChache method in the onClick method. Or use the following code in onCreate.
ViewTreeObserver vto = editText.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
editText.buildDrawingCache();
}
});
I also encountered this error a few times already:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
my solution:
new Thread(new Runnable(){
#Override
public void run(){
//add implementations that DOES NOT AFFECT the UI here
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run(){
//manage your edittext and Other UIs here
}
});
}
}).start();
just create a handler inside your worker thread to apply data changes to your UI

Memory leak on calling forceLayout

I have a ListView object which calls the forceLayout() method to re-render itself after its css property display is set to none. The reason i'm calling the above method is because I have a master-detail list of many listviews with only one listview visible at a time. After each list item in the master-detail list is selected the corresponding listview will be displayed.
Everytime the forceLayout() method is called, the memory increases by around 10mb and is not reclaimed even after the app is suspended. Is it a bug or am I not supposed to be calling the method frequently?
Btw, I'm running Visual Studio RC 2012 on Release Preview.
Thanks in advance.
A ton of memory leaks were fixed between Release Preview and RTM, so my first suggestion would be to grab the RTM bits and try that.
Failing that, it's probably an issue with your code holding on to references to your data or the rendered DOM elements, but without seeing your code it's hard to say what it might be.

ReleaseDesignerOutlets deallocated outlets not being reallocated

I have the following scenario:
UIViewControllerA pushes UIViewControllerB on to the navigation stack via a storyboard segue;
UIViewControllerB is now currently onscreen and I simulate a memory warning via the iOS Simulator.
UIViewControllerA.DidReceiveMemoryWarning() gets invoked as well as UIViewControllerA.ViewDidUnload().
In method UIViewControllerA.ViewDidUnload() I call ReleaseDesignerOutlets() as recommended. UIViewControllerA designer outlets are now deallocated and null.
I hit the "back" button on the navigation bar which dismisses UIViewControllerB and causes UIViewControllerA.ViewDidLoad() to be invoked.
At this point I would expect the designer outlets to be initialised but they are still null and my application crashes due to null pointer exception.
Have I fundamentally missed something? I am using the Monotouch 5.4.0.
EDIT
I found this post which describes the solution to problem to be the same as what I discovered, that is, don't release the designer outlets in ViewDidUnload().
Update: It's finally crash free! (or at least it seems to be)
For future reference, it looks like the big lock-up was due to my disposing of my UIWebView outlet in the ViewDidUnload event. The UIWebView is defined in the xib, and not added as a subview in code.
When memory was running low, the view unloaded and removed the UIWebView outlet. Subsequently, the ViewDidLoad event would fire again, as the app recovered (!?), but it would lock-up. I expected the UIWebView to be reinstated in when the view reloaded (i.e. the xib reloaded).
Is this not the case? I thought it was good practise to dispose of outlets in the ViewDidUnload event?
As soon as I commented out the "webView.Dispose(); webView = null;" line of code, it's been working as expected.
What a day!
EDIT
Further debugging reveals that SOME designer outlets are getting reallocated such as UIImageView and UIScrollView, others such as UITextField and UIBarButtonItem are not.

UIView.BackgroundColor leaking memory if image is used?

Preamble:
The view controller's view I'm talking about is used in a UISplitViewController as the details view. It gets replaced if the user navigates using the UINavigationController of the split view. When hitting "Back", a new instance of my problematic view controller is created and gets assigned to the split view controllers array of controllers.
Everytime my view shows up, I see an 8MB block being allocated in Instruments. Navigating forth and back for a while will eventually crash the app.
In detail:
In my class overriding UIViewController I have a:
private UIColor oBgColor = null;
in ViewDidLoad():
this.oBgColor = UIColor.FromPatternImage ( UIImage.FromFile ( "Data/Images/background.jpg" ) );
// This line here causes memory to go up by 8MB!
this.View.BackgroundColor = this.oBgColor;
in ViewDidDisappear():
// These lines should sucessfully get rid of the allocated 8MB chunk.
this.View.BackgroundColor = UIColor.Clear;
this.oBgColor.Dispose();
this.View.Dispose();
Code reaches ViewDidDisappear() as expected, view is gone.
The 8MB block is allocated ONLY if I assign this.oBgColor to the view's BackgroundColor. Leave that line out and all works (but I don't have a background).
Each time the view gets shown, I see another 8MB getting wasted.
What's going on here? Is BackgroundImage known to be leaking?
Interesting sidenote: if I add a UIImageView as a subview and use its image instead of assign to the view BackgroundColor property, memory hardly goes up at all and gets properly released.
I wrote a sample based on your code, and while memory usage does go up when it is assigned, there is no leak (I toggled the image on and off 100 times, and memory didn't change).
The reason it goes up is the BackgroundImage property is backed by a copy #property in Objective-C, which means that the entire object is copied on assignment, so if you have a huge pattern image, and then assign it, you will for a brief period have 2 copies of the image in memory.
The reason you see the memory increase is as I explained to you on IRC, MonoTouch does not have a compacting GC in v3.

Resources