memory leaks on android
TRANSCRIPT
Memory Leaks
SOFTWARE ENGINEER | AUTHOR | CONSULTANTOmri Erez
@innovationMaze | https://medium.com/@OomriErez
WHAT YOU DON’T KNOW WILL DRIVE YOU CRAZY
About Me
Memory Leak
A scenario in which our application persistently retains an object’s memory, even after it is not needed anymore.
Component Life Cycle
Activity Life Cycle
App Life Cycle
Service Life Cycle
References TreeGC
Root
O2
O3 O4
Source: https://flic.kr/p/q2TrC7
GC Roots
Static variables
& functions
References on a stack
JNI references
& objects
A Memory LeakGC
Root
O2
O3
O4
Source: https://flic.kr/p/52jkCJ
Source: https://flic.kr/p/6H4wxP
What you don’t know WILL drive you crazy
Scenario 1
private static View mLeakingView; private byte[] mBytes=new byte[1000000]; @Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity); mLeakingView = findViewById(R.id.mainView); mLeakingView.setOnClickListener(view -> finish()); MyStringHelper.getInstance().setActivity(this); Log.d(TAG,MyStringHelper.getInstance().getString(R.string.app_name)); }
Static References
Static Referencespublic final class MyStringHelper { private final static MyStringHelper INSTANCE = new MyStringHelper(); private Context mActivityContext; public static MyStringHelper getInstance() {return INSTANCE;} public void setActivity(Context context) {mActivityContext=context;} public String getString(int id) { return mActivityContext.getString(id); } private MyStringHelper() { if (INSTANCE != null) { throw new IllegalStateException("Already instantiated"); } }}
The Source of the Leak
•Static reference to a view
•Static instance of MyStringHelper
Their lifecycle is longer than the activity lifecycle
A Memory Leak
GC RootmLeakingView
Activity
GC RootMyStringHelper
Solution?
Solution :|
@Overrideprotected void onDestroy() { mLeakingView=null; MyStringHelper.getInstance()
.setActivityContext(null); super.onDestroy(); }
Solution :)
•Avoid static references especially to Android components like activities, services and views
•Use Application context instead of the Activity context
Scenario 2
Anonymous Classes//Anonymous classprivate final Handler mLeakyHandler = new Handler() { @Override public void handleMessage(Message msg) { } }; @Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity); mView.setOnClickListener(view -> finish()); mLeakyHandler.sendEmptyMessageDelayed(MESSAGE_ID,20000);}
A Memory Leak
Looper ActivitymLeakyHandler
Source: https://flic.kr/p/6H4wxP
Solution :|
@Overrideprotected void onDestroy() { mLeakyHandler.removeMessages(MESSAGE_ID); super.onDestroy(); }
Solution :)
•When using the Handler class, make sure its static
Scenario 3
Non Static Inner Classes
private class MyRunnable implements Runnable { @Override public void run() { Log.d(TAG, "I am running in activity:" + InnerClassLeakingActivity.this.getComponentName()); } }
Solution :)
•Avoid non-static inner classes
•Use WeakReference if needed
Solution :)private static class MyRunnable implements Runnable{ WeakReference<InnerClassLeakingActivity> mWeakActivity; public MyRunnable(InnerClassLeakingActivity activity) { this.mWeakActivity = new WeakReference<>(activity); } @Override public void run() { final InnerClassLeakingActivity activity=mWeakActivity.get(); if (activity!=null) Log.d(TAG, String.format("I am running in activity:%s" ,activity.getComponentName()));
}}
Tools for Automatic Detection
StrictMode
•Here to help find developers mistakes and bring it to our attention
•Very easy to use
StrictMode :)
if (BuildConfig.DEBUG) { StrictMode.VmPolicy vmPolicy=new StrictMode.VmPolicy.Builder() .detectActivityLeaks()
.detectLeakedSqlLiteObjects() .detectLeakedClosableObjects() .penaltyLog() .build(); StrictMode.setVmPolicy(vmPolicy); }
Leak Canary
•Created by Pierre-Yves Ricau (Square Inc)
•Very easy to use
•Tracks objects for memory leaks