“How is it possible that your app, written in a managed programming language, has memory issues? After all, isn’t it the Garbage Collector’s job to release unused or unreachable objects to free up memory? Clearly you are not paging your data, hence the large memory footprint of your app.” If you think the above it true, then you are more than welcome to join Pieter’s session about finding and fixing memory issues in Xamarin.iOS, Xamarin.Android and Xamarin.Forms! Our expert PIETER will investigate and try to find the culprits of different memory leaks, and their motives. This often requires a deep dive into some Xamarin/iOS/Android internals, and spans a lot wider than your basic and typical TableView or ListView optimizations. After this session you should have a good understanding of how and why some types of memory leaks arise, how to search for them and -most importantly- how to avoid them in the future.
2. About the speaker
Pieter Nijs
Senior .NET Consultant & Competence Lead Mobile
@ Ordina Belgium
Microsoft MVP Windows Development
MADN Board Member
Blog.PieEatingNinjas.be
@nijspieter
10. # 1 Get to know our enemy (in general)
What is a memory leak?
Objects that are not being removed from memory, although
they are not needed any more (not accessible from code).
11. # 1 Get to know our enemy (in general)
What about our ‘secret’ weapon, the GC?
12. # 1 Get to know our enemy (in general)
What about our ‘secret’ weapon, the GC?
• Us protecting our enemies
• Framework
• Coding
• Managed Mono memory is
only part of the entire memory
13. # 1 Get to know our enemy (in general)
The main ’problem’ with Xamarin
• 2 different types of memory
Mono
runtime
14. # 1 Get to know our enemy (in general)
The main ’problem’ with Xamarin
• 2 different types of memory è objects living in both ‘worlds’
UIButton button = new UIButton(); [UIButton]
UIButton
22. Get to know our enemy: Xamarin iOS
General .NET pitfall we already know
Delegates and events live as long as the publisher
è Problem when publisher lives longer than
subscriber
è NSNotificationCenter
è Except: events/actions set via Storyboard
23. Get to know our enemy: Xamarin iOS
How does iOS manages memory?
Reference counting
24. Get to know our enemy: Xamarin iOS
Reference counting
Instantiate:
[[Person alloc] init] è retain count 1
Copy reference:
Person2 = [Person retain] è retain count 2
Release:
[Person release] è retain count 1
[Person2 release] è retain count 0
25. Get to know our enemy: Xamarin iOS
Peer objects
26. Get to know our enemy: Xamarin iOS
Peer objects
Managed wrappers around native iOS objects when accessed
by Xamarin.iOS runtime.
27. Get to know our enemy: Xamarin iOS
Native iOS
Native object
[UIButton]
Native memory
Allocated by iOS
Reference count
Xamarin.iOS
Peer object
UIKit.UIButton
Managed memory
Allocated by X.iOS
Collected by GC
IntPtr Handle
Peer objects
28. Get to know our enemy: Xamarin iOS
Peer objects
Native iOS
[[UIButton alloc] init]
[b release]
[b release]
Xamarin.iOS
var b = new UIButton(…);
b = null;
b.Dispose();
Release in finalizer,
using IntPtr
29. Get to know our enemy: Xamarin iOS
Framework peers User peers
Peer objects
30. Get to know our enemy: Xamarin iOS
Framework peers
• Built-in types around iOS objects
• Stateless
è call native objects to get or set values
è Lots of optimizations by the framework
• Created first time C# code accesses the object
• Can be created on the fly
• …
31. Get to know our enemy: Xamarin iOS
User peers
• Custom type deriving from Framework peer
• Have state!
public class MyCustomView : UIKit.UIView
{
public string Text
{
get;
set;
}
}
32. Get to know our enemy: Xamarin iOS
User peers
• Have state!
èiOS doesn’t know about this additional state
èNeeds to be kept alive on managed side
33. Get to know our enemy: Xamarin iOS
User peers
• Have state!
View.AddSubview(new MyCustomView()
{ Text = "Hello Techorama" });
…
if (View.Subviews.Last() is MyCustomView mcv)
Debug.WriteLine(mcv.Text);
34. Get to know our enemy: Xamarin iOS
User peers - State
• Xamarin.iOS ROOTS User peer objects that have no managed
references
MyCustomView UIView
35. Get to know our enemy: Xamarin iOS
User peers - State
• Xamarin.iOS ROOTS User peer objects that have no managed
references
MyCustomView UIView
GC Handle
36. Get to know our enemy: Xamarin iOS
User peers - State
• GC will look at the retain count of native object
• Retain Count > 1
è Object is alive and kicking
• Retain Count == 1 && no references to managed object
è Managed object can be collected
37. Get to know our enemy: Xamarin iOS
User peers - State
Strong references added by the framework, outside
our code, can easily create a reference cycle
that can NOT be broken by the GC!
39. Get to know our enemy: Xamarin iOS
User peers - Reference cycle
When we navigate back, we get this:
GalleryImageView UIView
GC Handle
GalleryViewController UIViewController Retain count = 1
Retain count = 2
40. Get to know our enemy: Xamarin iOS
User peers - Reference cycle
When we navigate back, we get this:
GalleryImageView UIView
GC Handle
GalleryViewController UIViewController Retain count = 1
Retain count = 2
Managed reference is still here!!
41. Get to know our enemy: Xamarin iOS
User peers - Reference cycle
We need to break the cycle when navigating back:
GalleryImageView UIView
GC Handle
GalleryViewController UIViewController Retain count = 1
Retain count = 1
_galleryViewController = null;
Retain count = 2
42. Get to know our enemy: Xamarin iOS
Peer promotion
Framework peer being promoted to User peer
_galleryButton = new UIButton();
…
_galleryButton.TouchUpInside += GalleryButton_TouchUpInside;
è Adding state
è Reference cycles!
43. Get to know our enemy: Xamarin iOS
Peer promotion
Unsubscribe from events (or use storyboard actions)
public override void ViewDidAppear(bool animated)
{
EvilButton.TouchUpInside += EvilButton_TouchUpInside;
}
public override void ViewDidDisappear(bool animated)
{
EvilButton.TouchUpInside -= EvilButton_TouchUpInside;
}
45. Xamarin iOS Battleplan
è Unsubscribe from delegates and events!
Except for events/actions set via Storyboard
è Call Dispose() on big peers when needed
(instead of waiting for GC)
è Manually break reference cycles
50. Get to know our enemy: Xamarin Android
How does Android manages memory?
GC
51. Get to know our enemy: Xamarin Android
Garbage Collectors
Both GCs need to work together
52. Get to know our enemy: Xamarin Android
Remember Peer objects ?
53. Get to know our enemy: Xamarin Android
Peer objects
Native Android / Java
Native object
Java.Lang.Object
JVM memory
Allocated by JVM
Collected by
Android GC
Xamarin.Android
Peer object
IJavaObject
Managed memory
Allocated by X.Droid
Collected by GC
IntPtr Handle JNI Handle
Treated as GC root by JVM
54. Get to know our enemy: Xamarin Android
Garbage Collection
‘Normal’ managed objects
è ‘Classic’ GC
Peer Objects
è JNI Handle replaced with Weak Handle
è Force Android / Java GC
è Weak reference != Alive? è Collect
55. Get to know our enemy: Xamarin Android
Garbage Collection
Good and bad news…
The good: No reference cycle problems as in iOS
The bad: GC takes longer & higher memory pressure
56. Get to know our enemy: Xamarin Android
Watch out for bitmaps
Typically large objects, but not on the Mono-side
è Possible no Mono GC will occur
è Android GC can’t collect due to active JNI handle
57. Get to know our enemy: Xamarin Android
Boxing
ArrayAdapter is a Framework peer, using java.util.ArrayList
internally, holding references to Java objects
è C# objects are boxed to create Java representation
59. Xamarin Android Battleplan
è Improve GC performance
• Dispose peer objects & set references to null
• Periodically trigger a GC
è Watch out for Bitmaps, use Dispose/using
è Avoid using ArrayAdapter, use BaseAdapter instead
è Experiment with ‘new’ GC Bridges
61. Xamarin Android Battleplan
GC Bridge
Default: ‘Old’ bridge
Alternatives: ‘New’ and ’Tarjan’
Newer implementations != silver bullet
è Experimental, but might work for you
è Try it out and test!
66. Get to know our enemy: Xamarin Forms
Can be both easiest and hardest enemy!
General .NET pitfall we already know:
Delegates and events
Xamarin Forms, ‘knows’ about previous platform-specific
pitfalls and takes them into account.
BUT all iOS & Droid specific caveats are relevant on effects
and renderers!