advanced memory management on ios and android - mark probst and rodrigo kumpera

63
Rodrigo Kumpera Runtime Engineer Xamarin [email protected] Advanced Memory Management on iOS and Android

Upload: xamarin

Post on 18-Nov-2014

2.537 views

Category:

Technology


1 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Rodrigo KumperaRuntime [email protected]

AdvancedMemory Management

oniOS and Android

Page 2: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Mark ProbstRuntime [email protected]

AdvancedMemory Management

oniOS and Android

Page 3: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

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/

Page 4: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

object referenceobject withreference

one object ... … referencing ... … another

Basic Garbage Collection

Page 5: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Root

Garbage Collection

Page 6: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Root

Garbage Collection

Page 7: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Root

Garbage Collection

Page 8: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Root

Garbage Collection

Page 9: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Root

Garbage Collection

Page 10: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Root

Garbage Collection

Page 11: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Root

Garbage Collection

Page 12: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Root

Garbage Collection

Page 13: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Two Generations

• Generational hypothesisMost objects die young

• NurseryWhere (small) objects are born

• MajorWhere they are copied to when they mature

• Large Object SpacePart of the major generation, objects > 8KbTypically large arrays

Page 14: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

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

Page 15: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Common Issues

• Let’s examine three common issues• Given a trivial piece code

Find the issueUnderstand the problemLearn how to fix it

Page 16: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

010203040506070809101112131415

iOS Puzzle 1public  override  void  ViewDidLoad  (){   var  imageView  =  new  UIImageView  (IMG_VIEW_POSITION);   var  image  =  UIImage.FromBundle  ("grumpy-­‐cat.jpg");   imageView.Image  =  image;   View.Add  (imageView);     var  button  =  UIButton.FromType  (UIButtonType.RoundedRect);   button.Frame  =  BUTTON_POSITION;   View.Add  (button);     button.TouchUpInside  +=  (sender,  e)  =>  {     imageView.RemoveFromSuperview  ();   };}

Page 17: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

010203040506070809101112131415

Something Is Missingpublic  override  void  ViewDidLoad  (){   var  imageView  =  new  UIImageView  (IMG_VIEW_POSITION);   var  image  =  UIImage.FromBundle  ("grumpy-­‐cat.jpg");   imageView.Image  =  image;   View.Add  (imageView);     var  button  =  UIButton.FromType  (UIButtonType.RoundedRect);   button.Frame  =  BUTTON_POSITION;   View.Add  (button);     button.TouchUpInside  +=  (sender,  e)  =>  {     imageView.RemoveFromSuperview  ();   };}

Page 18: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

The garbage collector cannot see what’sbehind an innocent object

The Illusion

32  bytes

2  MbC#  UIImage

ObjC  UIImage

Page 19: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

0102030405060708091011121314151617

Dispose Your Resourcespublic  override  void  ViewDidLoad  (){   var  imageView  =  new  UIImageView  (IMG_VIEW_POSITION);   var  image  =  UIImage.FromBundle  ("grumpy-­‐cat.jpg");   imageView.Image  =  image;   View.Add  (imageView);     var  button  =  UIButton.FromType  (UIButtonType.RoundedRect);   button.Frame  =  BUTTON_POSITION;   View.Add  (button);     button.TouchUpInside  +=  (sender,  e)  =>  {     imageView.RemoveFromSuperview  ();     imageView.Dispose  ();     image.Dispose  ();   };}

Page 20: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Using Dispose

• Call Dispose() to release ownership of a resource• Use with large native resources, such as

ImagesSounds

• Or scarce resources, such asFilesSockets

• Remember, lifecycle must be manually defined

Page 21: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

01020304050607080910111213141516

iOS Puzzle 2public  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));   }}

Page 22: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

01020304050607080910111213141516

Indirect Cyclespublic  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));   }}

Page 23: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Cycles in Objective-C

1

1

Page 24: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

How those cycles happenIndirect Cycles

C#

Puzzle2Controller

CustomView

View.Add  (...)

this.parent  =  ...

Objec;ve-­‐C

1

2

Page 25: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

01020304050607080910111213141516

Using Weak Referencespublic  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));   }}

Page 26: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Indirect cycles

• Inherited from Objective-C• How to detect

When multiple objects point to each other• Breaking those cycles

Use WeakReferenceBy disposing the parentBy explicitly nulling links

• What can trigger it under the hoodNon-wrapper subclasses of NSObjectWith reference count > 2

Page 27: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

0102030405060708091011121314

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  ();   }}

Page 28: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

0102030405060708091011121314

Watch Your Lambdaspublic  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  ();   }}

Page 29: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Measure before assuming something is wrongEnabling 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

Page 30: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

0102030405060708091011

Android Puzzlepublic  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);   }}

Page 31: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

0102030405060708091011

Over-Sharingpublic  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);   }}

Page 32: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

A tale of two heapsCross-Heap References

ArrayAdapter

ArrayList

Tweets

C#  Object

Java  Object

Page 33: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

01020304050607080910111213141516171819

Do It All from C# Landpublic  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);   }}

Page 34: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

The a!er effectCross-Heap References

TweetsAdapter

List<Tweet>

Tweets

C#  Object

Java  Object

Page 35: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Avoid Cross-Heap References

• It’s expensive for Java to see a C# objectAnd vice-versa

• Performance cost of language crossing• Higher Garbage Collector costs• Your objects are effectively being mirrored

So using twice as much memory

Page 36: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

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

Page 37: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Q&A

Page 38: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Use SGen unless you havegood reason not to

SGen vs Boehm

Page 39: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

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

Page 40: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Xamarin.iOS Approach

• Two classes of objects• Wrappers

Comes from native frameUIButton, NSString, etc

• User typesUser code that extend the aboveMyButton

Page 41: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Wrappers

• Retain on construction• Release on finalization/dispose• We don’t care if the managed object goes away

Page 42: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

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

Page 43: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

0102030405060708091011

User Typesclass  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

}

Page 44: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

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

Page 45: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

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

Page 46: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Roothas

Finalizer

an object with a finalizer must be kept aliveFinalizer Illustration

Page 47: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Roothas

Finalizer

so must all objects reachable from itFinalizer Illustration

Page 48: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Roothas

Finalizer

so must all objects reachable from itFinalizer Illustration

Page 49: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Roothas

Finalizer

they die in the next collectionFinalizer Illustration

Page 50: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Root

they die in the next collectionFinalizer Illustration

Page 51: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Roothas

Finalizer

unless ...Finalizer Illustration

Page 52: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Roothas

Finalizer

the finalizer resurrects themFinalizer Illustration

Page 53: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Root

the finalizer resurrects themFinalizer Illustration

Page 54: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Root

the finalizer resurrects themFinalizer Illustration

Page 55: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Stuff You Can Do with Finalizers(and Probably Shouldn’t)

• Resurrect their objects• Re-register for finalization• Non-critical vs critical finalizers

Page 56: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Nursery

Root 1 Root 2

Major Heap

keeping track of major→nursery referencesThe Write Barrier

Page 57: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

Nursery

Root 1 Root 2

Major Heap

keeping track of major→nursery referencesThe Write Barrier

Page 58: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

What Are Roots?

• static variables

• Stack frames in managed threads• Finalizer queue• References registered in unmanaged code

Page 59: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

01020304050607

Android Puzzle 2public  class  CommitLogAdapter  :  BaseAdapter  {   Dictionary<string,  CommitObject>  commitCache;       CommitObject  GetCommit  (string  hash)  {     return  commitCache  [hash];   }}

Page 60: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

01020304050607

Too Many Objectspublic  class  CommitLogItemAdapter  :  BaseAdapter  {   Dictionary<string,  CommitObject>  commitCache;       CommitObject  GetCommit  (string  hash)  {     return  commitCache  [hash];   }}

Page 61: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

It must inspect special objects andeverything they reference

Android GC Interop

CommitLogAdapter

commitsCache

Page 62: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

01020304050607

Use Static Cachespublic  class  CommitLogItemAdapter  :  BaseAdapter  {   static  Dictionary<string,  CommitObject>  commitCache;       static  CommitObject  GetCommit  (string  hash)  {     return  commitCache  [hash];   }}

Page 63: Advanced Memory Management on iOS and Android - Mark Probst and Rodrigo Kumpera

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 slowerIt rescans all objects they referenceAvoid pointing to large group of objects

• Explicitly manage the lifecycle of long living objectsStatic caches and collectionsHelps measuring consumption