Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Rodrigo KumperaRuntime EngineerXamarinkumpera@xamarin.comAdvancedMemory ManagementoniOS and Android
Mark ProbstRuntime EngineerXamarinmark@xamarin.comAdvancedMemory ManagementoniOS and Android
What a Garbage Collector Does• Gives you memory for your objects• Computes which objects are reachable• Gets rid of the re...
object referenceobject withreferenceone object ... … referencing ... … anotherBasic Garbage Collection
RootGarbage Collection
RootGarbage Collection
RootGarbage Collection
RootGarbage Collection
RootGarbage Collection
RootGarbage Collection
RootGarbage Collection
RootGarbage Collection
Two Generations• Generational hypothesisMost objects die young• NurseryWhere (small) objects are born• MajorWhere they are...
Stop-the-World• Nursery collection pauses are short• Major collection pauses can take a long time• All threads registered ...
Common Issues• Let’s examine three common issues• Given a trivial piece codeFind the issueUnderstand the problemLearn how ...
010203040506070809101112131415iOS Puzzle 1public	  override	  void	  ViewDidLoad	  (){	   var	  imageView	  =	  new	  UIIm...
010203040506070809101112131415Something Is Missingpublic	  override	  void	  ViewDidLoad	  (){	   var	  imageView	  =	  ne...
The garbage collector cannot see what’sbehind an innocent objectThe Illusion32	  bytes2	  MbC#	  UIImageObjC	  UIImage
0102030405060708091011121314151617Dispose Your Resourcespublic	  override	  void	  ViewDidLoad	  (){	   var	  imageView	  ...
Using Dispose• Call Dispose() to release ownership of a resource• Use with large native resources, such asImagesSounds• Or...
01020304050607080910111213141516iOS Puzzle 2public	  class	  CustomView	  :	  UIView	  {	   UIViewController	  parent;	  	...
01020304050607080910111213141516Indirect Cyclespublic	  class	  CustomView	  :	  UIView	  {	   UIViewController	  parent;	...
Cycles in Objective-C11
How those cycles happenIndirect CyclesC#Puzzle2ControllerCustomViewView.Add	  (...)this.parent	  =	  ...Objec;ve-­‐C12
01020304050607080910111213141516Using Weak Referencespublic	  class	  CustomView	  :	  UIButton	  {	   WeakReference<UIVie...
Indirect cycles• Inherited from Objective-C• How to detectWhen multiple objects point to each other• Breaking those cycles...
0102030405060708091011121314iOS Puzzle 2 (Bonus)public	  class	  CustomButton	  :	  UIButton	  {	   public	  CustomButton	...
0102030405060708091011121314Watch Your Lambdaspublic	  class	  CustomButton	  :	  UIButton	  {	   public	  CustomButton	  ...
Measure before assuming something is wrongEnabling GC logging on AndroidA Small Detour$adb shell setprop debug.mono.env "M...
0102030405060708091011Android Puzzlepublic	  class	  Tweet	  {}public	  class	  Puzzle1	  :	  ListActivity{	   protected	 ...
0102030405060708091011Over-Sharingpublic	  class	  Tweet	  {}public	  class	  Puzzle1	  :	  ListActivity{	   protected	  o...
A tale of two heapsCross-Heap ReferencesArrayAdapterArrayListTweetsC#	  ObjectJava	  Object
01020304050607080910111213141516171819Do It All from C# Landpublic	  class	  Tweet	  {}public	  class	  TweetAdapter	  :	 ...
The a!er effectCross-Heap ReferencesTweetsAdapterList<Tweet>TweetsC#	  ObjectJava	  Object
Avoid Cross-Heap References• It’s expensive for Java to see a C# objectAnd vice-versa• Performance cost of language crossi...
Performance Tips• The less you allocate, the less o!en the GC runs• The less live data you have, the quicker the GC runs• ...
Q&A
Use SGen unless you havegood reason not toSGen vs Boehm
Memory Management on iOS• Reference Counting• Each object has a reference count field• Incremented when something points t...
Xamarin.iOS Approach• Two classes of objects• WrappersComes from native frameUIButton, NSString, etc• User typesUser code ...
Wrappers• Retain on construction• Release on finalization/dispose• We don’t care if the managed object goes away
User Types• Retain/Release like wrappers• Can have custom state• We make a gc handle (think of a static field) point to th...
0102030405060708091011User Typesclass	  MyButton	  :	  UIButton	  {}...public	  override	  void	  ViewDidLoad	  (){	   var...
First Rule of Finalizers:Don’t Use Finalizers!• They’re not guaranteed to run within any deadline• They don’t run in a spe...
Finalizers• Run in a dedicated finalizer thread• Run concurrently with other threads• Keep their objects and those referen...
RoothasFinalizeran object with a finalizer must be kept aliveFinalizer Illustration
RoothasFinalizerso must all objects reachable from itFinalizer Illustration
RoothasFinalizerso must all objects reachable from itFinalizer Illustration
RoothasFinalizerthey die in the next collectionFinalizer Illustration
Rootthey die in the next collectionFinalizer Illustration
RoothasFinalizerunless ...Finalizer Illustration
RoothasFinalizerthe finalizer resurrects themFinalizer Illustration
Rootthe finalizer resurrects themFinalizer Illustration
Rootthe finalizer resurrects themFinalizer Illustration
Stuff You Can Do with Finalizers(and Probably Shouldn’t)• Resurrect their objects• Re-register for finalization• Non-critic...
NurseryRoot 1 Root 2Major Heapkeeping track of major→nursery referencesThe Write Barrier
NurseryRoot 1 Root 2Major Heapkeeping track of major→nursery referencesThe Write Barrier
What Are Roots?• static variables• Stack frames in managed threads• Finalizer queue• References registered in unmanaged code
01020304050607Android Puzzle 2public	  class	  CommitLogAdapter	  :	  BaseAdapter	  {	   Dictionary<string,	  CommitObject...
01020304050607Too Many Objectspublic	  class	  CommitLogItemAdapter	  :	  BaseAdapter	  {	   Dictionary<string,	  CommitOb...
It must inspect special objects andeverything they referenceAndroid GC InteropCommitLogAdaptercommitsCache
01020304050607Use Static Cachespublic	  class	  CommitLogItemAdapter	  :	  BaseAdapter	  {	   static	  Dictionary<string,	...
Taking care of special objects• If it extends the Android framework, it’s a special object• The Garbage Collector scans th...
Upcoming SlideShare
Loading in …5
×

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

7,841 views

Published on

Published in: Technology, Art & Photos

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

  1. 1. Rodrigo KumperaRuntime EngineerXamarinkumpera@xamarin.comAdvancedMemory ManagementoniOS and Android
  2. 2. Mark ProbstRuntime EngineerXamarinmark@xamarin.comAdvancedMemory ManagementoniOS and Android
  3. 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/
  4. 4. object referenceobject withreferenceone object ... … referencing ... … anotherBasic Garbage Collection
  5. 5. RootGarbage Collection
  6. 6. RootGarbage Collection
  7. 7. RootGarbage Collection
  8. 8. RootGarbage Collection
  9. 9. RootGarbage Collection
  10. 10. RootGarbage Collection
  11. 11. RootGarbage Collection
  12. 12. RootGarbage Collection
  13. 13. 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
  14. 14. Stop-the-World• Nursery collection pauses are short• Major collection pauses can take a long time• All threads registered with the runtime are stoppedThat includes the main run loop thread• iOS animations continue to run in a separate process
  15. 15. Common Issues• Let’s examine three common issues• Given a trivial piece codeFind the issueUnderstand the problemLearn how to fix it
  16. 16. 010203040506070809101112131415iOS 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  ();   };}
  17. 17. 010203040506070809101112131415Something 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  ();   };}
  18. 18. The garbage collector cannot see what’sbehind an innocent objectThe Illusion32  bytes2  MbC#  UIImageObjC  UIImage
  19. 19. 0102030405060708091011121314151617Dispose 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  ();   };}
  20. 20. Using Dispose• Call Dispose() to release ownership of a resource• Use with large native resources, such asImagesSounds• Or scarce resources, such asFilesSockets• Remember, lifecycle must be manually defined
  21. 21. 01020304050607080910111213141516iOS 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));   }}
  22. 22. 01020304050607080910111213141516Indirect 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));   }}
  23. 23. Cycles in Objective-C11
  24. 24. How those cycles happenIndirect CyclesC#Puzzle2ControllerCustomViewView.Add  (...)this.parent  =  ...Objec;ve-­‐C12
  25. 25. 01020304050607080910111213141516Using 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));   }}
  26. 26. Indirect cycles• Inherited from Objective-C• How to detectWhen multiple objects point to each other• Breaking those cyclesUse WeakReferenceBy disposing the parentBy explicitly nulling links• What can trigger it under the hoodNon-wrapper subclasses of NSObjectWith reference count > 2
  27. 27. 0102030405060708091011121314iOS 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. 28. 0102030405060708091011121314Watch 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  ();   }}
  29. 29. Measure before assuming something is wrongEnabling GC logging on AndroidA 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. 30. 0102030405060708091011Android 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);   }}
  31. 31. 0102030405060708091011Over-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);   }}
  32. 32. A tale of two heapsCross-Heap ReferencesArrayAdapterArrayListTweetsC#  ObjectJava  Object
  33. 33. 01020304050607080910111213141516171819Do 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);   }}
  34. 34. The a!er effectCross-Heap ReferencesTweetsAdapterList<Tweet>TweetsC#  ObjectJava  Object
  35. 35. 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 mirroredSo using twice as much memory
  36. 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
  37. 37. Q&A
  38. 38. Use SGen unless you havegood reason not toSGen vs Boehm
  39. 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. 40. Xamarin.iOS Approach• Two classes of objects• WrappersComes from native frameUIButton, NSString, etc• User typesUser code that extend the aboveMyButton
  41. 41. Wrappers• Retain on construction• Release on finalization/dispose• We don’t care if the managed object goes away
  42. 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 ifreference count > 1• Managed object is kept around if native is interested in it
  43. 43. 0102030405060708091011User 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}
  44. 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. 45. Finalizers• Run in a dedicated finalizer thread• Run concurrently with other threads• Keep their objects and those referenced from there alive foranother GC round
  46. 46. RoothasFinalizeran object with a finalizer must be kept aliveFinalizer Illustration
  47. 47. RoothasFinalizerso must all objects reachable from itFinalizer Illustration
  48. 48. RoothasFinalizerso must all objects reachable from itFinalizer Illustration
  49. 49. RoothasFinalizerthey die in the next collectionFinalizer Illustration
  50. 50. Rootthey die in the next collectionFinalizer Illustration
  51. 51. RoothasFinalizerunless ...Finalizer Illustration
  52. 52. RoothasFinalizerthe finalizer resurrects themFinalizer Illustration
  53. 53. Rootthe finalizer resurrects themFinalizer Illustration
  54. 54. Rootthe finalizer resurrects themFinalizer Illustration
  55. 55. Stuff You Can Do with Finalizers(and Probably Shouldn’t)• Resurrect their objects• Re-register for finalization• Non-critical vs critical finalizers
  56. 56. NurseryRoot 1 Root 2Major Heapkeeping track of major→nursery referencesThe Write Barrier
  57. 57. NurseryRoot 1 Root 2Major Heapkeeping track of major→nursery referencesThe Write Barrier
  58. 58. What Are Roots?• static variables• Stack frames in managed threads• Finalizer queue• References registered in unmanaged code
  59. 59. 01020304050607Android Puzzle 2public  class  CommitLogAdapter  :  BaseAdapter  {   Dictionary<string,  CommitObject>  commitCache;       CommitObject  GetCommit  (string  hash)  {     return  commitCache  [hash];   }}
  60. 60. 01020304050607Too Many Objectspublic  class  CommitLogItemAdapter  :  BaseAdapter  {   Dictionary<string,  CommitObject>  commitCache;       CommitObject  GetCommit  (string  hash)  {     return  commitCache  [hash];   }}
  61. 61. It must inspect special objects andeverything they referenceAndroid GC InteropCommitLogAdaptercommitsCache
  62. 62. 01020304050607Use Static Cachespublic  class  CommitLogItemAdapter  :  BaseAdapter  {   static  Dictionary<string,  CommitObject>  commitCache;       static  CommitObject  GetCommit  (string  hash)  {     return  commitCache  [hash];   }}
  63. 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 wayThat 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

×