83
A good memory is one trained to forget the trivial Where is my RAM, dude? +

Performance #1 memory

Embed Size (px)

Citation preview

Page 1: Performance #1   memory

A good memory is one trained to forget the trivial

Where is my RAM, dude?

+

Page 2: Performance #1   memory

First,

Page 3: Performance #1   memory

Yonatan LevinGoogle Developer

Expert & Android @ Gett

Idan FelixSenior Android &

Redhead Varonis

Jonathan Yarkoni

Android Developer & Advocate Ironsource

Android Academy Staff

Britt Barak

Android LeadReal

Muiriel Felix

Android Design

Page 4: Performance #1   memory

Logistics

Page 5: Performance #1   memory

https://www.facebook.com/groups/android.academy.ils/

Page 6: Performance #1   memory
Page 7: Performance #1   memory

#PerfMatters

Page 8: Performance #1   memory

What’s next?

30/5 - Felix

- How to draw right? ( Overdraw, Cliprect, Bitmaps)

13/6 - Britt

- View, Animations

Page 9: Performance #1   memory

What’s next?

4/7 - Yonatan

- Networking, JSON, Batching, Location

10/8 - Felix

- Battery & CPU

Page 10: Performance #1   memory

What’s next?

14/9 - Britt

- Threadinggg...

And…

Page 11: Performance #1   memory

31.10New course coming

Page 12: Performance #1   memory

Memory mmm…

Page 13: Performance #1   memory
Page 14: Performance #1   memory
Page 15: Performance #1   memory

OutOfMemory is just an iceberg of problems with a memory

Page 16: Performance #1   memory
Page 17: Performance #1   memory

Wow! What should I do?

Page 18: Performance #1   memory

1.Be Aware2.Learn & Understand3.Apply - Profile your code

Page 19: Performance #1   memory

I’mCollector,

Garbage Collector

Page 20: Performance #1   memory

GC

Page 21: Performance #1   memory

Goal

1.Find objects that can’t be accessed

2.Reclaim the resource from them

Page 22: Performance #1   memory

Mark & Sweeping

Page 23: Performance #1   memory

So what happens when we don’t have enough free space

for our “new Object();”?

Page 24: Performance #1   memory

Larger heap

Page 25: Performance #1   memory
Page 26: Performance #1   memory

Pitfall

Page 27: Performance #1   memory

More Garbage = Larger GC Pauses

Page 28: Performance #1   memory

Dalvik07-01 15:56:16.785: I/dalvikvm-heap(30615): Grow heap (frag case) to 38.179MB for 8294416-byte allocation

07-01 15:56:17.625: I/Choreographer(30615): Skipped 35 frames! The application may be doing too much work on its main thread.

07-01 15:56:19.035: D/dalvikvm(30615): GC_CONCURRENT freed 35838K, 43% free 51351K/89052K, paused 3ms+5ms, total 106ms

07-01 15:56:19.035: D/dalvikvm(30615): WAIT_FOR_CONCURRENT_GC blocked 96ms

Page 29: Performance #1   memory

GC ReasonGC_CONCURRENT - A concurrent GC that frees up memory as your heap begins to fill up.

GC_FOR_MALLOC - A GC caused because your app attempted to allocate memory when your heap was already full, so the system had to stop your app and reclaim memory.

GC_HPROF_DUMP_HEAP - A GC that occurs when you request to create an HPROF file to analyze your heap.

GC_EXPLICIT - An explicit GC, such as when you call gc() (which you should avoid calling and instead trust the GC to run when needed).

GC_EXTERNAL_ALLOC - This happens only on API level 10 and lower (newer versions allocate everything in the Dalvik heap). A GC for externally allocated memory (such as the pixel data stored in native memory or NIO byte buffers).

Page 30: Performance #1   memory

ART07-01 16:00:44.531: I/art(198): Explicit concurrent mark sweep GC freed 700(30KB) AllocSpace objects, 0(0B) LOS objects, 792% free, 18MB/21MB, paused 186us total 12.763ms

07-01 16:00:46.517: I/art(29197): Background partial concurrent mark sweep GC freed 74626(3MB) AllocSpace objects, 39(4MB) LOS objects, 1496% free, 25MB/32MB, paused 4.422ms total 1.371747s

07-01 16:00:48.534: I/Choreographer(29197): Skipped 30 frames! The application may be doing too much work on its main thread.

07-01 16:00:48.566: I/art(29197): Background sticky concurrent mark sweep GC freed 70319(3MB) AllocSpace objects, 59(5MB) LOS objects, 825% free, 49MB/56MB, paused 6.139ms total 52.868ms

Page 31: Performance #1   memory

GC ReasonConcurrent - A concurrent GC which does not suspend app threads. This GC runs in a background thread and does not prevent allocations.

Alloc - The GC was initiated because your app attempted to allocate memory when your heap was already full. In this case, the garbage collection occurred in the allocating thread.

Explicit - The garbage collection was explicitly requested by an app, for instance, by calling gc() or gc().

NativeAlloc - The collection was caused by native memory pressure from native allocations such as Bitmaps or RenderScript allocation objects

Page 32: Performance #1   memory

Why i care about memory

Page 33: Performance #1   memory

60FPS!!!

Page 34: Performance #1   memory

But How Does It Work?

Page 35: Performance #1   memory

But How Does It Work?

SmoothMotion

60

No Difference

60+

Flip Book

12

Movies

Frames Per Second

Fluid Motion

24+effects

Page 36: Performance #1   memory

We Have A Winner!

SmoothMotion

60

No Difference

60+

Flip Book

12

Movies

Frames Per Second

Fluid Motion

24+effects

SmoothMotion

60

Page 37: Performance #1   memory

60 FPS

60 Frames / Second = Frame / 16.666 Millisecond

Page 38: Performance #1   memory
Page 39: Performance #1   memory

Okey, OkeyI got the idea.What’s next?

Page 40: Performance #1   memory

Reasons

1.A lot of allocations

2.Memory leaks

3.Not designing for performance

Page 41: Performance #1   memory

Memory churnpublic void startAllocations(View view) {

AndroidAcademy object;

for (int i = 0; i < 1000; i++) {

object = new AndroidAcademy();

}

}

Page 42: Performance #1   memory
Page 43: Performance #1   memory

A Volume hit the GC

Page 44: Performance #1   memory

When usually avoid it happen

1.onDraw()

2.String concatenation on buffer reading from stream

3.Inner loops allocation

Page 45: Performance #1   memory

Good Practice

1.Allocate outside of the loop

2.Consider reuse of the object

3.Pool of objects

4.To think does it really necessary?

Page 46: Performance #1   memory

Memory Leakpublic class MainActivity extends AppCompatActivity {

private static Drawable sBackground;

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView textView = (TextView) findViewById(R.id.text); textView.setText("Leak fun!");

if (sBackground == null) { sBackground = ContextCompat.getDrawable(this,android.R.drawable.ic_menu_zoom); } textView.setBackgroundDrawable(sBackground); }

}

Page 47: Performance #1   memory
Page 48: Performance #1   memory

Detecting Memory Issues

Page 49: Performance #1   memory
Page 50: Performance #1   memory

GC now!Dump Java

Heap

Start Allocation Tracker

Page 51: Performance #1   memory
Page 52: Performance #1   memory
Page 53: Performance #1   memory

LeakCanary

Page 54: Performance #1   memory
Page 55: Performance #1   memory

How to?public class GetTaxiDriverBoxApp extends Application { protected RefWatcher mRefWatcher;

public void onCreate() { super.onCreate(); mRefWatcher = installLeakCanary(); }

protected RefWatcher installLeakCanary() { return RefWatcher.DISABLED;}

}

Page 56: Performance #1   memory

In Debug Onlypublic class DebugApplication extends GetTaxiDriverBoxApp {

@Override protected RefWatcher installLeakCanary() { mRefWatcher = LeakCanary.install(this, LeakSlackUploadService.class, AndroidExcludedRefs.createAppDefaults().build()); return mRefWatcher; }}

Page 57: Performance #1   memory

When should be killed - watched it@Overridepublic void onDetach() { super.onDetach();

GetTaxiDriverBoxApp.getRefWatcher(getContext()).watch(this);

}

Page 58: Performance #1   memory

Let’s dive into some examples

Page 59: Performance #1   memory

Memory Leak Life Example Google Maps* GC ROOT thread java.lang.Thread.<Java Local> (named 'AsyncTask #5')* references android.os.AsyncTask$3.this$0 (anonymous class extends java.util.concurrent.FutureTask)* references com.google.maps.android.clustering.ClusterManager$ClusterTask.this$0* references com.google.maps.android.clustering.ClusterManager.mRenderer* references com.gettaxi.dbx_lib.features.heatmap.FixedBucketsResizingDrawableClusterRenderer.mIconGenerator* references com.google.maps.android.ui.IconGenerator.mContext* leaks com.gettaxi.dbx.android.activities.MainActivity instance

Page 60: Performance #1   memory

Memory Leak Life Example

Page 61: Performance #1   memory

Some of them are really hard to spot

Page 62: Performance #1   memory

Memory Leak - Example* GC ROOT thread com.squareup.picasso.Dispatcher.DispatcherThread.<Java Local>* references android.os.Message.obj* references com.example.MyActivity$MyDialogClickListener.this$0* leaks com.example.MyActivity.MainActivity instance

Page 63: Performance #1   memory

Handler.javapublic final Message obtainMessage(int what, Object obj) { return Message.obtain(this, what, obj);}

public void handleMessage(Message msg) { switch (msg.what) { case WHAT_ORDER_UPDATES: { //do something break; }}

Page 64: Performance #1   memory

Simple Dialognew AlertDialog.Builder(this) .setPositiveButton("Baguette", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { MyActivity.this.makeBread(); } }).show();

Page 65: Performance #1   memory

.class of OnClickListenerclass MyActivity$0 implements DialogInterface.OnClickListener { final MyActivity this$0; MyActivity$0(MyActivity this$0) { this.this$0 = this$0; } @Override public void onClick(DialogInterface dialog, int which) { this$0.makeBread(); }}

new AlertDialog.Builder(this) .setPositiveButton("Baguette", new MyActivity$0(this)); .show();

Page 66: Performance #1   memory

AlertController.javapublic void setButton(int whichButton, CharSequence text, DialogInterface.OnClickListener listener, Message msg) { if (msg == null && listener != null) { msg = mHandler.obtainMessage(whichButton, listener); } switch (whichButton) { case DialogInterface.BUTTON_POSITIVE: mButtonPositiveText = text; mButtonPositiveMessage = msg; break; //...

Page 67: Performance #1   memory

private final View.OnClickListener mButtonHandler = new View.OnClickListener() { @Override public void onClick(View v) { final Message m; if (v == mButtonPositive && mButtonPositiveMessage != null) { m = Message.obtain(mButtonPositiveMessage); }

//...some other code if (m != null) { m.sendToTarget(); } // Post a message so we dismiss after the above handlers are executed. mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialogInterface) .sendToTarget(); }};

Page 68: Performance #1   memory

Fix?

Page 69: Performance #1   memory

Check the square blogWrapper for clickListener and clear it on detach

Or

Send idle messages to clean app the messages

https://corner.squareup.com/2015/08/a-small-leak.html

Page 70: Performance #1   memory

Good Practices

Page 71: Performance #1   memory

Do not keep long-lived references to a context-activitypublic static Context mContext;

public NoLifeCycleClass(Activity myActivity) {

mContext = (Context) myActivity;}

Page 72: Performance #1   memory

Try using the context-application instead of a context-activity

StringUtilsUI.doSomeLongRunningTask(getApplicationContext());

Page 73: Performance #1   memory

Avoid non-static inner classespublic class DialogCountdown extends BaseDialogFragment { private class CountDownHandler extends Handler { //do some work }}

Page 74: Performance #1   memory

Avoid non-static inner classes private static class CountDownHandler extends Handler {

private final WeakReference<DialogCountdown> mDialogCountdownWeakReference;

public CountDownHandler(DialogCountdown dialogCountdown) { super(); mDialogCountdownWeakReference = new WeakReference<>(dialogCountdown); }

public void handleMessage(Message msg) { if(mDialogCountdownWeakReference.get()!=null) { mDialogCountdownWeakReference.get().onCountDown(); } } }

Page 75: Performance #1   memory

Clean/Stop all your handlers, animation listeners onDestroy();protected void onStop() { super.onStop(); mHandler.clearAllMessages(); unregisterReceivers(); heatMapsDone(); if (mServiceBound) { mServiceBound = false; Services.unbindFromRidesService(this, this); } if (mMapStateMachine != null) { mMapStateMachine.stop(); mMapStateMachine = null; }}

Page 76: Performance #1   memory

Last world...

Page 77: Performance #1   memory
Page 78: Performance #1   memory

Today

Page 79: Performance #1   memory

With Proguard

Page 80: Performance #1   memory

Jack

Page 81: Performance #1   memory

Jill

Page 82: Performance #1   memory

http://trickyandroid.com/the-dark-world-of-jack-and-jill/

Page 83: Performance #1   memory