3. What a Garbage Collector Does
• Gives you memory for your objects
• Computes which objects are reachable
• Gets rid of the rest (= garbage)
h"p://www.flickr.com/photos/tweng/2235972313/
13. Two Generations
• Generational hypothesis
Most objects die young
• Nursery
Where (small) objects are born
• Major
Where they are copied to when they mature
• Large Object Space
Part of the major generation, objects > 8Kb
Typically large arrays
14. Stop-the-World
• Nursery collection pauses are short
• Major collection pauses can take a long time
• All threads registered with the runtime are stopped
That includes the main run loop thread
• iOS animations continue to run in a separate process
15. Common Issues
• Let’s examine three common issues
• Given a trivial piece code
Find the issue
Understand the problem
Learn how to fix it
20. Using Dispose
• Call Dispose() to release ownership of a resource
• Use with large native resources, such as
Images
Sounds
• Or scarce resources, such as
Files
Sockets
• Remember, lifecycle must be manually defined
21. 01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
iOS Puzzle 2
public
class
CustomView
:
UIView
{
UIViewController
parent;
public
CustomView
(UIViewController
parent)
{
this.parent
=
parent;
}
}
public
class
Puzzle2Controller
:
UIViewController
{
public
override
void
ViewDidLoad
()
{
View.Add
(new
CustomView
(this));
}
}
22. 01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
Indirect Cycles
public
class
CustomView
:
UIView
{
UIViewController
parent;
public
CustomView
(UIViewController
parent)
{
this.parent
=
parent;
}
}
public
class
Puzzle2Controller
:
UIViewController
{
public
override
void
ViewDidLoad
()
{
View.Add
(new
CustomView
(this));
}
}
24. How those cycles happen
Indirect Cycles
C#
Puzzle2Controller
CustomView
View.Add
(...)
this.parent
=
...
Objec;ve-‐C
1
2
25. 01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
Using Weak References
public
class
CustomView
:
UIButton
{
WeakReference<UIViewController>
parent;
public
CustomView
(UIViewController
parent)
{
this.parent
=
new
WeakReference<UIViewController>
(parent);
}
}
public
class
Puzzle2Controller
:
UIViewController
{
public
override
void
ViewDidLoad
()
{
View.Add
(new
CustomView
(this));
}
}
26. Indirect cycles
• Inherited from Objective-C
• How to detect
When multiple objects point to each other
• Breaking those cycles
Use WeakReference
By disposing the parent
By explicitly nulling links
• What can trigger it under the hood
Non-wrapper subclasses of NSObject
With reference count > 2
27. 01
02
03
04
05
06
07
08
09
10
11
12
13
14
iOS Puzzle 2 (Bonus)
public
class
CustomButton
:
UIButton
{
public
CustomButton
()
{}
}
public
class
Puzzle2Controller
:
UIViewController
{
public
override
void
ViewDidLoad
()
{
var
button
=
new
CustomButton
();
View.Add
(button);
button.TouchUpInside
+=
(sender,
e)
=>
this.RemoveFromParentViewController
();
}
}
28. 01
02
03
04
05
06
07
08
09
10
11
12
13
14
Watch Your Lambdas
public
class
CustomButton
:
UIButton
{
public
CustomButton
()
{}
}
public
class
Puzzle2Controller
:
UIViewController
{
public
override
void
ViewDidLoad
()
{
var
button
=
new
CustomButton
();
View.Add
(button);
button.TouchUpInside
+=
(sender,
e)
=>
this.RemoveFromParentViewController
();
}
}
29. Measure before assuming something is wrong
Enabling GC logging on Android
A Small Detour
$adb shell setprop debug.mono.env "MONO_LOG_LEVEL=debug|MONO_LOG_MASK=gc"
D/Mono ( 5862): GC_MAJOR: (user request) pause 2.82ms, total 3.06ms,
bridge 20.96 major 608K/808K los 9K/20K
30. 01
02
03
04
05
06
07
08
09
10
11
Android Puzzle
public
class
Tweet
{}
public
class
Puzzle1
:
ListActivity
{
protected
override
void
OnCreate
(Bundle
bundle)
{
base.OnCreate
(bundle);
var
data
=
new
Tweet[]
{
tweet0,
tweet1,
tweet2,
tweet3
};
ListAdapter
=
new
ArrayAdapter
(this,
Resource.Layout.TextViewItem,
data);
}
}
31. 01
02
03
04
05
06
07
08
09
10
11
Over-Sharing
public
class
Tweet
{}
public
class
Puzzle1
:
ListActivity
{
protected
override
void
OnCreate
(Bundle
bundle)
{
base.OnCreate
(bundle);
var
data
=
new
Tweet[]
{
tweet0,
tweet1,
tweet2,
tweet3
};
ListAdapter
=
new
ArrayAdapter
(this,
Resource.Layout.TextViewItem,
data);
}
}
32. A tale of two heaps
Cross-Heap References
ArrayAdapter
ArrayList
Tweets
C#
Object
Java
Object
33. 01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
Do It All from C# Land
public
class
Tweet
{}
public
class
TweetAdapter
:
BaseAdapter<Tweet>
{
List<Tweet>
tweets;
public
override
Tweet
this[int
position]
{
get
{
return
tweets
[position];
}
}
public
override
int
Count
{
get
{
return
tweets.Count;
}
}
}
public
class
Puzzle3
:
ListActivity
{
protected
override
void
OnCreate
(Bundle
bundle)
{
var
data
=
new
List<Tweet>
()
{
tweet0,
tweet1,
tweet2,
tweet3
};
ListAdapter
=
new
TweetAdapter
(this,
data);
}
}
35. Avoid Cross-Heap References
• It’s expensive for Java to see a C# object
And vice-versa
• Performance cost of language crossing
• Higher Garbage Collector costs
• Your objects are effectively being mirrored
So using twice as much memory
36. Performance Tips
• The less you allocate, the less o!en the GC runs
• The less live data you have, the quicker the GC runs
• Small, short-lived objects are cheap
• Don’t allocate large (> 8Kb) objects that die young
• Avoid writing to reference fields
• Better: avoid having reference fields
• Don’t use free lists
39. Memory Management on iOS
• Reference Counting
• Each object has a reference count field
• Incremented when something points to it
• Decremented when something stops pointing to it
• Object is released when count equals to zero
40. Xamarin.iOS Approach
• Two classes of objects
• Wrappers
Comes from native frame
UIButton, NSString, etc
• User types
User code that extend the above
MyButton
41. Wrappers
• Retain on construction
• Release on finalization/dispose
• We don’t care if the managed object goes away
42. User Types
• Retain/Release like wrappers
• Can have custom state
• We make a gc handle (think of a static field) point to the object if
reference count > 1
• Managed object is kept around if native is interested in it
43. 01
02
03
04
05
06
07
08
09
10
11
User Types
class
MyButton
:
UIButton
{}
...
public
override
void
ViewDidLoad
()
{
var
button
=
new
MyButton
();
//ref
count
==
1,
no
gc
handle
this.View.Add
(button);
//ref
count
==
2,
gc
handle
created
...
button.RemoveFromSuperView
();
//
ref
count
==
1,
gc
handle
removed
}
44. First Rule of Finalizers:
Don’t Use Finalizers!
• They’re not guaranteed to run within any deadline
• They don’t run in a specific sequence
• They make objects live longer
• The GC doesn’t know about unmanaged resources
45. Finalizers
• Run in a dedicated finalizer thread
• Run concurrently with other threads
• Keep their objects and those referenced from there alive for
another GC round
55. Stuff You Can Do with Finalizers
(and Probably Shouldn’t)
• Resurrect their objects
• Re-register for finalization
• Non-critical vs critical finalizers
56. Nursery
Root 1 Root 2
Major Heap
keeping track of major→nursery references
The Write Barrier
57. Nursery
Root 1 Root 2
Major Heap
keeping track of major→nursery references
The Write Barrier
58. What Are Roots?
• static variables
• Stack frames in managed threads
• Finalizer queue
• References registered in unmanaged code
63. Taking care of special objects
• If it extends the Android framework, it’s a special object
• The Garbage Collector scans them in a different way
That is slower
It rescans all objects they reference
Avoid pointing to large group of objects
• Explicitly manage the lifecycle of long living objects
Static caches and collections
Helps measuring consumption