Lecture delivered in BASTA 2018, regarding Xamarin Forms memory structure and specifics when dealing with custom renderers, effects and external plugins
3. Agenda
Forms overview and scenarios
CG in nutshell
CG in Mono.NET – nursery, major, tracking
Mono CG and Java CG
• Tracking
• Cases to watch out – minor can be evil
Mono CG and IOS refcounting – framework and user pears
• Tracking
• Cases to watch out – object cycles
Ways to identify memory leaks
4. Overview of Xamarin Forms
.NET STANDARD
Core library
INFRASTRUCTURE
Xamarin Forms
Mono class libraryBase class
library
5. Forms scenarios
When building pure XF app we are shielded from the native platform
• We need to care mainly for memory in the managed word
• Most general .NET practices apply here
When should we care about Xamarin internals
• Effects
• Custom (and Fast) Renderers
• Native control embedding
• Any time our app crashes with OutOfMemoryException
7. GC overview
GC help us due to its many benefits
1 Simple to work with
2 Reduces memory Leaks
3 Deals with memory reallocation under the hood
8. GC - new object allocation
GC triggers on the current thread when there is not enough space
to satisfy new memory request
A B C
Current pointer
of free space
var D = new D();
9. GC - new object allocation
GC triggers on the current thread when there is not enough space
to satisfy new memory request
A B C D
Current pointer of free
space
var D = new D();
10. GC - reclaiming memory
GC triggers on the current thread when there is not enough space
to satisfy new memory request
A B C D
E
Current pointer
of free space
var E = new E();
11. GC overview - traverse
During collection, GC locates the roots and traverses the live
reference graph
A
B
C
D
Current thread
12. GC overview – what is root
Static property
Reference on the stack of a managed thread
Objects passed into the native platform and pinned*
13. GC – reclaiming memory
GC removes the dead objects and eventually moves rest to
reduce fragmentation and to open space for new objects
A B C D
var E = new E();
14. GC – reclaiming memory
GC removes the dead objects and eventually moves rest to
reduce fragmentation and to open space for new objects
A B D
var E = new E();
15. GC – reclaiming memory
GC removes the dead objects and eventually moves rest to
reduce fragmentation and to open space for new objects
A B D
var E = new E();
16. GC – reclaiming memory
GC removes the dead objects and eventually moves rest to
reduce fragmentation and to open space for new objects
A B D E
var E = new E();
20. Mono Generations
Nursery
• Tend to hold primarily transient or temporary objects – return values,
strings, temp objects from methods
Major
• Object that have survived collection – static or long live objects
23. Pause Time
Each time the GC runs the app pauses
The duration (pause time) it takes to do a collection depends on
• Type or generation of collection
• The size of the live graph
Our target would be to minimize pause time and the numbers of
major GC
24. GC in Mono – how to track it
Xamarin.Android – automatically in debug mode
Xamarin.IOS – requires environment variables
• MONO_LOG_LEVEL
• MONO_LOG_MASK
26. Objects in Xamarin
Some objects live in both the managed and the native world and
must be treated properly to ensure that they are freed from both
worlds.
Xamarin
Managed
Android.Graphics.
Image
IOS/Android
Native
class [UIButton]
Class
android.Graphics.
Image
UIKit.UIButtonBinding layer
35. Tips
Reduce the graph of objects living in both worlds, especially the
short lived ones. (wrap or remove them).
Avoid passing non-peer into Java (as peers are created)
Since Mono is orchestrating the peer objects, consider calling
GC.Collect when you finish using small C# object that holds big
Java one
Mono
Managed
ImageDrawable
JVM
Native
ImageDrawable
Handle
(GC Root in Android)
~20 b> 1000 kb
38. IOS – retain count
IOS uses ARC
A
B
A A A
CB C
RC = 1 RC = 2 RC = 1 RC = 0
39. Peer objects
Similar to Android, however increase the RC of a native object
instead
Mono
Managed
INativeObject
iOS
Native
iOS Object
Handle
(RC + 1)
public interface INativeObject
{
IntPtr Handle { get; }
}
40. IOS + Mono – framework peers
All ios objects live as framework peers in mono.
Mono
Managed
UIKit.UIButton
iOS
Native
[UIButton]
Handle
(RC + 1)
PeerNative
41. IOS + Mono – framework peers
All ios objects live as framework peers in mono.
• Built-in
• Stateless
• Created each time we create or access native object
• No Guaranteed that will keep one peer instance for the same native object
RC is decremented if peer is disposed or finalized
42. IOS + Mono – User peers
Also called derived objects
Mono
UIKit.UIButton
iOS
[UIButton] Handle
(RC + 1)
Mono
MyButton
43. IOS + Mono – User peers (2)
Holds managed state (iOS does not know about them)
public class MyButton : UIButton
{
string IconPath { get; set; }
}
44. IOS + Mono – User peers + GC
Holding managed reference to User Peer creates reference
cycles that cannot be broken automatically by GC
Mono
MyParentView
iOS
[UIView] Handle
(RC + 1)
Mono
MyButton
iOS
[UIButton]
(RC + 1)
Handle
(RC + 1)
Parent property
45. IOS + Mono – User peers + GC
We need to manually break the cycle
Mono
MyParentView
iOS
[UIView] Handle
(RC + 1)
Mono
MyButton
iOS
[UIButton]
(RC + 1)
Handle
(RC + 1)
Parent property
Parent=null;
Dispose()
46. Promoting User peers
Adding a state (e.g. event handler) promote framework peer to
user peer.
• We need to manually detach from the event
• Use WeakEventListener
47. IOS + Mono – User peers + GC
Xamarin.IOS keeps user peers alive to preserve state
public class CustomRenderer: EntryRenderer
{
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if(disposing && this.Control!=null)
{
this.Control.TouchDown -= OnTouchDown;
this.Control.Dispose();
}
}
}
53. UI for Xamarin
Leave dealing with these native problems to us while focusing on
your app
Save time and effort with ready to go toolkit
Covering various scenarios – 20+ components
• Grid
• Chart
• ListView
• Calendar
• DataForm
• TabView