Upgrade to Pro — share decks privately, control downloads, hide ads and more …

A Brief History of Memory Leaks - Droidcon Berl...

A Brief History of Memory Leaks - Droidcon Berlin 2022

Memory and threading in is some of the most difficult and complicated topics in Android. This talk introduces what memory leaks are, and why they’re so hard to find. Taken from excerpts of the newly published O'Reilly book "Programming Android with Kotlin: Achieving Structured Concurrency with Coroutines", this talk take a light-hearted approach at some of the colorful developments Android has gone through the years. From “Why did AsyncTask get deprecated?” to resource leaks in background threads, we look at memory leaks in Android as we’ve uncovered them through the years. To understand quirks of memory leaks in Android, join us for some historical context, a little empathy, and a kinder view of open-source development.

mvndy_hd

July 08, 2022
Tweet

More Decks by mvndy_hd

Other Decks in Technology

Transcript

  1. Timeline JetBrains: Coroutines are like light-weight threads 2017 Roman Elizarov

    [JB]: Coroutines in Practice 2018 Roman Elizarov [JB]: Structured Concurrency 2019 @mvndy_hd #programmingandroidwithkotlin
  2. Timeline JetBrains: Coroutines are light-weight threads Roman Elizarov [JB]: Coroutines

    in Practice 2017 2018 Roman Elizarov: Structured Concurrency Android Developer Blog: Avoiding memory leaks 2009 @mvndy_hd
  3. Timeline JetBrains: Coroutines are light-weight threads Roman Elizarov [JB]: Coroutines

    in Practice 2016 KT-1622: Coroutine does not clear internal state Fixed Kotlin 1.4 2017 2018 Roman Elizarov: Structured Concurrency JetBrains: Coroutines are light-weight threads Android Developer Blog: Avoiding memory leaks 2009 @mvndy_hd
  4. Memory leaks are everywhere! They're in your apps. They're in

    your IDEs. They're in your phones. We're going to creating them. And we're going to keep finding them and fixing them.
  5. What is a memory leak? • When the heap holds

    to allocated memory longer than necessary ◦ Leaking references for scheduled work and concurrency • When an object is allocated in memory but is unreachable for the running program ◦ Passing Android components outside of Main threading • Can be found in your own code or 3rd party libraries or OS @mvndy_hd #programmingandroidwithkotlin
  6. Why do memory leaks matter? • Application quality depends on

    user experience • Android has very limited resources in hardware capacity ◦ ~15% of Android users use Lollipop and below on their devices¹ • Memory leaks can cause "jank", OOM, and other strange behaviors ¹ Bradshaw, Kyle. “Google Kills Android Distribution Numbers on the Web - 9to5Google.” 9To5Google @mvndy_hd #programmingandroidwithkotlin
  7. Heap + Garbage collection (GC) • Every application has its

    own process and heap ◦ If your app reaches heap capacity and tries to allocate for more memory, it will receive an OOM • GC - frees allocated memory back to heap ◦ Finds data objects in a program that cannot be reached in the future ◦ Reclaim the resources used by those objects • Objects eligible for GC: ◦ Nullifying the object reference ◦ Re-assigning the object reference ◦ Object created inside method @mvndy_hd #programmingandroidwithkotlin
  8. Limitations of the Android threading system • Interacts and manipulates

    UI widgets and views • Allows Android components in the application to interact with the Android OS • All components run within the same process instantiates on the UI thread • Storing references to Android components in background threads can cause leaks @mvndy_hd #programmingandroidwithkotlin
  9. Explicit leaks Look for references to Android Context/Activity/Fragment/View components accidentally

    stored or injected into: • static objects • background threads • RecyclerViews!!!!! @mvndy_hd #programmingandroidwithkotlin
  10. Implicit Memory Leaks • Harder to see due to implicit

    referencing - use LeakCanary to catch these • Be wary of third-party libraries that may reference the use of an Android resource @mvndy_hd #programmingandroidwithkotlin
  11. Timeline JetBrains: Coroutines are light-weight threads Roman Elizarov [JB]: Coroutines

    in Practice 2016 KT-1622: Coroutine does not clear internal state Fixed Kotlin 1.4 2017 2018 Roman Elizarov: Structured Concurrency Google: AsyncTask deprecated 2019 Android Developer Blog: Avoiding memory leaks 2009 @mvndy_hd
  12. Implicit Memory Leaks: Async Tasks class MainActivity : Activity {

    /*removed for brevity */ inner class SomeAsyncTask: AsyncTask<Void, Void, String> { override fun doInBackground( ) { /*removed for brevity */ } override fun onPostExecute(results: String): String { /* removed for brevity */ } } @mvndy_hd #programmingandroidwithkotlin
  13. Implicit Memory Leaks: Fragment Views • Be wary of third-party

    libraries that may reference the use of an Android resource • The Fragment's View (but not the Fragment itself) is destroyed when a Fragment is put on the backstack² ◦ Developers are expected to clear refs for views in Fragment::onDestroyView ¹ “Issue 145468285: Memory Leak with Nested Fragments and Data Binding.” Issuetracker.google.com, Google, 2 Dec. 2019, 09:01am PT, issuetracker.google.com/issues/145468285#comment6.
  14. Review • Memory leaks occur when the heap holds to

    allocated memory longer than necessary or references become unreachable • Explicit Memory Leaks ◦ Avoid passing Android components outside of Main threading • Implicit Memory Leaks ◦ Leaking references for scheduled work @mvndy_hd #programmingandroidwithkotlin
  15. Why is concurrency hard to manage? • Callbacks rapidly become

    complex + hard to maintain without thoughtful design • Must account for listening to lifecycle events • Requires a thread-safe way of interrupting threads @mvndy_hd #programmingandroidwithkotlin
  16. In a hiking app, onHikesFetched might gather the following data

    • Total distance • Length of last hike in both time + distance • current weather • favorite hikes @mvndy_hd #programmingandroidwithkotlin
  17. Executing heavier work with ThreadPoolExecutor • Tasks can be broken

    down into threads • A ThreadPoolExecutor spins up a number of threads and tosses blocks of work amongst them to execute @mvndy_hd #programmingandroidwithkotlin
  18. Pros of implementation: • Thread-safe way since only one thread

    owns the state • Relative ease of callbacks Cons: • Using a thread pool can be overkill @mvndy_hd #programmingandroidwithkotlin
  19. • Okay but what about memory leaks? ◦ We've addressed

    a lot of potential areas already • But we have missed one consideration - cancellation @mvndy_hd #programmingandroidwithkotlin
  20. Limitations of the Android threading model • Devs cannot control

    when a thread finishes ◦ A thread finishes when the work is complete ◦ It cannot finish if an injected resource lives a long time • Execution flow hard to control ◦ If one fails, the others are still running @mvndy_hd #programmingandroidwithkotlin
  21. Limitations of the Android threading model (cont...) • In threads,

    exceptions are not propagated • Android designers made the choice to crash the app if there is an unhandled UncaughtExceptionHandler as a choice to make it fail-fast ◦ - Good for Android, back for app developers who want to keep customers @mvndy_hd #programmingandroidwithkotlin
  22. • Coroutines does not require data primitives like Handlers to

    pass data • Safe cancellation mechanisms encouraged in coroutines Good news - cancellation mechanism is easier in coroutines @mvndy_hd
  23. • Fetching weather could involve multiple APIs, depending on the

    nature of the data • Wind, temperature data fetched separately from different sources Implementing onHikesFetched with coroutines @mvndy_hd #programmingandroidwithkotlin
  24. • To prevent leaked coroutines - implement coroutines that are

    able to automatically cancel dangling background threads to reduce risk of memory leak Implementing onHikesFetched with coroutines @mvndy_hd #programmingandroidwithkotlin
  25. What kind of exception are there? • Exposed exceptions ◦

    The only kinds of exceptions which can be handled by client code try/catch • Unhandled exceptions ◦ Typically makes your application crash (or at least, recovering from them might leave the app in an undetermined state) @mvndy_hd #programmingandroidwithkotlin
  26. Unhandled Exceptions by registering a custom CoroutineExceptionHandler (CEH) • Stops

    exception propagation • Avoids a program crash #programmingandroidwithkotlin
  27. No leaks, and waits for completion before cleanup - expresses

    uncaught exceptions Register CEH to: ◦ launch must be coroutine root builder ◦ a scope ◦ a supervisorScope child Unhandled Exceptions by registering a custom CoroutineExceptionHandler (CEH)
  28. Avoiding potential Memory Leaks • Watch how you interact with

    the Android API Framework • Follow the constraints of the Android threading model • Profile your code with AS or LeakCanary • Ask yourself "could I be interacting with a hardware component"? ◦ Databases, networking, Android graphics, location, etc etc @mvndy_hd #programmingandroidwithkotlin
  29. Avoiding potential Memory Leaks • Pay special care to static

    references, since they are stored longest in heap ◦ Never store Android components like Activity, Service, View, etc. • Do not reference Activity/View/Fragments/Context and other Android components outside the UI thread • Use Glide if you can, but if you have to work with bitmaps, make sure to recycle/nullify • Avoid the use of persistent services whenever possible @mvndy_hd #programmingandroidwithkotlin
  30. Interested in hearing more? • Android threading model • Thread

    safety strategies • Thread cancellation mechanisms • Structured concurrency • Coroutines • Channels • Flows @mvndy_hd