{"id":6543,"date":"2019-11-28T18:22:21","date_gmt":"2019-11-28T18:22:21","guid":{"rendered":"https:\/\/cheesecakelabs.com\/blog\/?p=6543\/"},"modified":"2026-08-15T08:06:21","modified_gmt":"2026-08-15T08:06:21","slug":"android-architecture-components-viewmodel-leak","status":"publish","type":"post","link":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/","title":{"rendered":"Android ViewModel leaks: why they happen and how to fix them"},"content":{"rendered":"<p><span style=\"font-weight: 400\">The <\/span><a href=\"https:\/\/developer.android.com\/jetpack\/docs\/guide\"><span style=\"font-weight: 400\">Guide to app architecture<\/span><\/a><span style=\"font-weight: 400\"> suggests using ViewModel + Repository with LiveData in order to implement the data flow from Repository to ViewModel. If you use this approach or anything similar, there&#8217;s a high chance you could be leaking ViewModel. To help you understand and fix this issue, this article will briefly explain what a memory leak is, some of its implications, what happens when a ViewModel is leaked, why it happens and how to fix it for good.<\/span><\/p>\n<p><!--more--><\/p>\n<h2><span style=\"font-weight: 400\">What is a memory leak and why is it a problem?<\/span><\/h2>\n<p><span style=\"font-weight: 400\">A memory leak happens when, for some reason, unused allocated memory cannot be released. If your application has an object that is no longer used, it should be released by the <\/span><a href=\"https:\/\/developer.android.com\/topic\/performance\/memory-overview#gc\"><span style=\"font-weight: 400\">Garbage Collector<\/span><\/a><span style=\"font-weight: 400\"> (GC) so that its allocated memory can be used again for another purpose. However, if someone has a reference to this object and never clears it, the object won\u2019t ever be garbage collected during the application lifetime, which characterizes a memory leak. But how bad is it?<\/span><\/p>\n<p><span style=\"font-weight: 400\">A limited amount of memory is provided to an application and it <\/span><a href=\"https:\/\/developer.android.com\/topic\/performance\/memory\"><span style=\"font-weight: 400\">varies depending on how much RAM the device has<\/span><\/a><span style=\"font-weight: 400\">. Having too many memory leaks can make the app deplete most or all of its memory, which could lead to:<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Lags: with too much memory used, the GC might try to reclaim memory portions through a process that will stop the main thread for <\/span><a href=\"https:\/\/techbeacon.com\/app-dev-testing\/what-you-need-know-about-android-app-memory-leaks\"><span style=\"font-weight: 400\">50ms to 100ms<\/span><\/a><span style=\"font-weight: 400\">. Since it won\u2019t be able to release the memory that was leaked, this will probably happen many times, making the app quite laggy.<\/span><\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/developer.android.com\/topic\/performance\/vitals\/anr\"><span style=\"font-weight: 400\">ANR error<\/span><\/a><span style=\"font-weight: 400\"> (Application Not Responding error): worse than lags, the app might freeze and become unusable.<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">OOM error (Out of Memory error): your app will run out of memory and then crash.<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Unexpected behavior: if an object lives longer than it should and your app isn\u2019t well structured, this object can keep doing unintended things, which can cause unexpected issues in the app.<\/span><\/li>\n<\/ul>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/media.giphy.com\/media\/10dHotK4K8R0AM\/giphy.gif\"><\/p>\n<p><span style=\"font-weight: 400\">As you can see, memory leaks can make things get really ugly. One of the most common things that beginner Android developers end up doing is passing Activity Context to another class and never removing its reference. But how about the ViewModel? You are probably thinking that you would never pass a ViewModel to another class, such as a Repository. Then how is a ViewModel leaked?<\/span><\/p>\n<h2><span style=\"font-weight: 400\">How does a ViewModel get leaked?<\/span><\/h2>\n<p><span style=\"font-weight: 400\">A ViewModel is commonly leaked when it uses a Repository to make an async network request and waits for the response, only to do something else afterwards. Based on this info, think about the scenario on Image 1 where we have a MainActivity, a SecondaryActivity + SecondaryViewModel and a Repository that performs the network request. The MainActivity starts the SecondaryActivity which then asks the Repository to perform a request and waits for the response. However, before the request is finished, you go back to the MainActivity and it gets finished when you are there.&nbsp;<\/span><\/p>\n<p>By this time, since SecondaryActivity has been destroyed, SecondaryViewModel should be dead or garbage collected, meaning it shouldn\u2019t receive the request\u2019s response. However, if you implement this flow poorly as in Image 1, the ViewModel won\u2019t be deleted and will still receive the response. Why does it happen?<\/p>\n<figure id=\"attachment_6544\" aria-describedby=\"caption-attachment-6544\" style=\"width: 782px\" class=\"wp-caption alignnone\"><img decoding=\"async\" class=\" wp-image-6544\" src=\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/sequence.png\" alt=\"sequence-viewmodel-leaking\" width=\"782\" height=\"681\" srcset=\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/sequence.png 1128w, https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/sequence-768x670.png 768w\" sizes=\"(max-width: 782px) 100vw, 782px\" \/><figcaption id=\"caption-attachment-6544\" class=\"wp-caption-text\">IMAGE 1 &#8211; Sequence Diagram of a flow that leaks a ViewModel<\/figcaption><\/figure>\n<p><span style=\"font-weight: 400\">To illustrate a similar situation, let\u2019s reproduce this flow with a simulated network request in the code below.&nbsp;<\/span><\/p>\n<p>&nbsp;<\/p>\n<pre class=\"language-swift\"><code class=\"language-swift\"><b>object <\/b><span style=\"font-weight: 400\">Repository {<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;<\/span><b>private var <\/b><b>requestCallback<\/b><span style=\"font-weight: 400\">: (() -&gt; Unit)? = <\/span><b>null<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;<\/b><i><span style=\"font-weight: 400\">\/\/ simulate a network request but in reality just sets the callback for the response<\/span><\/i>\n\n<i><span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;<\/span><\/i><b>fun <\/b><span style=\"font-weight: 400\">performFakeRequest(callback: () -&gt; Unit) {<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>requestCallback <\/b><span style=\"font-weight: 400\">= callback<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;}<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;<\/span><i><span style=\"font-weight: 400\">\/\/ simulate when the network request finishes<\/span><\/i>\n\n<i><span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;<\/span><\/i><b>fun <\/b><span style=\"font-weight: 400\">finishFakeRequest() {<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>requestCallback<\/b><span style=\"font-weight: 400\">?.invoke()<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;}<\/span>\n\n<span style=\"font-weight: 400\">}<\/span>\n\n<b>class <\/b><span style=\"font-weight: 400\">SecondaryViewModel : ViewModel() {<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;<\/span><b>fun <\/b><span style=\"font-weight: 400\">onPerformFakeRequestClicked() {<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Log.d(<\/span><b>\"SecondaryViewModel\"<\/b><span style=\"font-weight: 400\">, <\/span><b>\"performing request from view model <\/b><b>$this<\/b><b>\"<\/b><span style=\"font-weight: 400\">)<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Repository.performFakeRequest <\/span><b>{<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/b><span style=\"font-weight: 400\">Log.d(<\/span><b>\"SecondaryViewModel\"<\/b><span style=\"font-weight: 400\">, <\/span><b>\"finished request and executed callback on view model <\/b><b>$this<\/b><b>\"<\/b><span style=\"font-weight: 400\">)<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>}<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;<\/b><span style=\"font-weight: 400\">}<\/span>\n\n<span style=\"font-weight: 400\">}<\/span>\n\n<b>class <\/b><span style=\"font-weight: 400\">MainActivity : AppCompatActivity() {<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;<\/span><b>...<\/b>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;<\/span><b>private fun <\/b><span style=\"font-weight: 400\">setupView() {<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...<\/span>\n\n<b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/b><span style=\"font-weight: 400\">endFakeRequestButton.setOnClickListener <\/span><b>{<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/b><span style=\"font-weight: 400\">Repository.finishFakeRequest()<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>}<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;<\/b><span style=\"font-weight: 400\">}<\/span>\n\n<span style=\"font-weight: 400\">}<\/span><\/code><\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400\">In this small code (full code is <\/span><a href=\"https:\/\/github.com\/diegohkd\/ViewModelLeak\/tree\/master\/leak\/src\/main\/java\/example\/com\/viewmodelleak\"><span style=\"font-weight: 400\">here<\/span><\/a><span style=\"font-weight: 400\">), SecondaryViewModel asks the Repository to perform the request and wait for the response through a callback, whereas MainActivity has a button to simulate when the request ends.<\/span><\/p>\n<p><span style=\"font-weight: 400\">After reproducing the steps from Image 1, let\u2019s analyze the memory allocation using the <\/span><a href=\"https:\/\/developer.android.com\/studio\/profile\/android-profiler\"><span style=\"font-weight: 400\">Profiler Tool<\/span><\/a><span style=\"font-weight: 400\">:<\/span><\/p>\n<figure id=\"attachment_6545\" aria-describedby=\"caption-attachment-6545\" style=\"width: 989px\" class=\"wp-caption alignnone\"><img decoding=\"async\" class=\" wp-image-6545\" src=\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/SecondaryViewModel.png\" alt=\"Screenshot of Profiler Tool showing leaked SecondaryViewModel\" width=\"989\" height=\"456\" srcset=\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/SecondaryViewModel.png 1136w, https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/SecondaryViewModel-768x354.png 768w\" sizes=\"(max-width: 989px) 100vw, 989px\" \/><figcaption id=\"caption-attachment-6545\" class=\"wp-caption-text\">IMAGE 2 &#8211; Screenshot of Profiler Tool showing leaked SecondaryViewModel<\/figcaption><\/figure>\n<p><span style=\"font-weight: 400\">As you can see under the <\/span><i><span style=\"font-weight: 400\">Live Allocation-&gt;Class Name-&gt;app heap<\/span><\/i><span style=\"font-weight: 400\"> section, SecondaryViewModel is still allocated. If you click the button from MainActivity that simulates when the request finishes, you\u2019ll notice that the SecondaryViewModel callback will be triggered even though it should be dead. You can try to <\/span><a href=\"https:\/\/developer.android.com\/studio\/profile\/memory-profiler\"><span style=\"font-weight: 400\">force garbage collection<\/span><\/a><span style=\"font-weight: 400\"> but it won\u2019t work. SecondaryViewModel will never be garbage collected because it\u2019s been leaked. But how did it happen? Maybe from the SecondaryViewModel$onPerformFakeRequestClicked$1 on Image 2 you already guessed whose fault it is: the lambda expression used as the callback.&nbsp;<\/span><\/p>\n<h2><span style=\"font-weight: 400\">Callback\u2026 the villain?<\/span><\/h2>\n<p><span style=\"font-weight: 400\">There are many ways to implement the communication between ViewModel and Repository. You could <\/span><a href=\"https:\/\/developer.android.com\/reference\/android\/arch\/lifecycle\/LiveData#observeforever\"><span style=\"font-weight: 400\">observe forever<\/span><\/a><span style=\"font-weight: 400\"> a LiveData or use <\/span><a href=\"https:\/\/proandroiddev.com\/clean-easy-new-how-to-architect-your-app-part-4-livedata-transformations-f0fd9f313ec6\"><span style=\"font-weight: 400\">LiveData transformations<\/span><\/a><span style=\"font-weight: 400\"> inside the ViewModel, subscribe to observable events using <\/span><a href=\"http:\/\/reactivex.io\/\"><span style=\"font-weight: 400\">Rx<\/span><\/a><span style=\"font-weight: 400\">, use callbacks with a Listener interface or lambda expression. Either way, you have to be careful not to leak the ViewModel.&nbsp;<\/span><\/p>\n<p><span style=\"font-weight: 400\">Unless you are making a synchronous request in ViewModel, it\u2019s hard to think of how a ViewModel would receive data asynchronously without passing its reference to whoever will trigger the callback. It might not be clear to us, but that\u2019s exactly what happens when we use a callback with lambda expression or something similar.<\/span><\/p>\n<p><span style=\"font-weight: 400\">When creating a lambda expression, you can access everything that is in the scope of the enclosing class inside, right? <\/span><span style=\"font-weight: 400\">This is possible because the lambda expression has an implicit reference to the enclosing class. <\/span><span style=\"font-weight: 400\">And guess what, that\u2019s the source of our leak.<\/span><\/p>\n<p><span style=\"font-weight: 400\">When SecondaryViewModel calls performFakeRequest(callback: () -&gt; Unit), <\/span><span style=\"font-weight: 400\">an implicit reference to the ViewModel is passed to the callback<\/span><span style=\"font-weight: 400\"> and a reference to the same callback is passed to the Repository, which keeps this reference in its lifetime. Do you know what that means? Yep, the ViewModel will live until the app is terminated. Since Repository is a singleton class, it won\u2019t be garbage collected during the application lifetime. Consequently, the same will happen for the callback and the ViewModel.<\/span><\/p>\n<p><span style=\"font-weight: 400\">If the Repository wasn\u2019t a Singleton, it probably wouldn\u2019t be that bad, but it would still be a memory leak. The ViewModel reference would persist for as long as the request doesn\u2019t finish, meaning the ViewModel also might not be garbage collected when it should. This would be especially bad if instead of a one shot request, the non-singleton Repository keeps receiving some data until you cancel it. So, the right way to fix this is by clearing all ViewModel references.<\/span><\/p>\n<h2><span style=\"font-weight: 400\">How do you avoid leaking a ViewModel?<\/span><\/h2>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/media.giphy.com\/media\/l3q2MDnkLri1t7i5a\/giphy.gif\"><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400\">To avoid leaking a ViewModel, clear the reference to the callback when the request is finished, which then clears the reference to the ViewModel when the callback is garbage collected. In addition to that, when the ViewModel is no longer used, i.e. when it calls the onCleared() function, <\/span><span style=\"font-weight: 400\">any reference to it should also be cleared.<\/span><span style=\"font-weight: 400\"> Here are some suggestions on how to achieve this:<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Have a Repository function that clears the reference to the callback. The downside is when you have many callbacks co-existing in the same Repository and&nbsp;you have to clear a specific one. In this case you\u2019ll probably need to map callbacks to ViewModels somehow.<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">If you are using Rx or anything similar that can unsubscribe from a data stream, you just need to unregister from it by disposing the <\/span><a href=\"https:\/\/www.raywenderlich.com\/3983802-working-with-rxjava-disposables-in-kotlin\"><span style=\"font-weight: 400\">Disposable<\/span><\/a><span style=\"font-weight: 400\"> whenever it is necessary (e.g. when onCleared() is called).<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Similar to Rx\u2019s Disposable, you can implement your own class that is responsible for unsubscribing from a data stream and clearing callback references. For example, if you implement a request that returns Retrofit\u2019s <\/span><a href=\"https:\/\/square.github.io\/retrofit\/2.x\/retrofit\/retrofit2\/Call.html\"><span style=\"font-weight: 400\">Call<\/span><\/a><span style=\"font-weight: 400\">, this class can take care of clearing the request and callbacks like this:<\/span><\/li>\n<\/ul>\n<pre class=\"language-swift\"><code class=\"language-swift\"><b>import <\/b><span style=\"font-weight: 400\">retrofit2.Call<\/span>\n\n<b>class <\/b><span style=\"font-weight: 400\">Request&lt;<\/span><span style=\"font-weight: 400\">T<\/span><span style=\"font-weight: 400\">&gt;(<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;<\/span><b>var <\/b><b>call<\/b><span style=\"font-weight: 400\">: Call&lt;<\/span><span style=\"font-weight: 400\">T<\/span><span style=\"font-weight: 400\">&gt;,<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;<\/span><b>var <\/b><b>success<\/b><span style=\"font-weight: 400\">: ((<\/span><span style=\"font-weight: 400\">T<\/span><span style=\"font-weight: 400\">) -&gt; Unit)?,<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;<\/span><b>var <\/b><b>failure<\/b><span style=\"font-weight: 400\">: ((Throwable?) -&gt; Unit)? = <\/span><b>null<\/b>\n\n<span style=\"font-weight: 400\">) {<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;<\/span><b>fun <\/b><span style=\"font-weight: 400\">dispose() {<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>call<\/b><span style=\"font-weight: 400\">.cancel()<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>success <\/b><span style=\"font-weight: 400\">= <\/span><b>null<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/b><b>failure <\/b><span style=\"font-weight: 400\">= <\/span><b>null<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;<\/b><span style=\"font-weight: 400\">}<\/span>\n\n<span style=\"font-weight: 400\">}<\/span><\/code><\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400\">I implemented a very simple example of this Request class that fixes the ViewModel leak in our sample code. Since there isn\u2019t really any network request being performed, the Request only contains the callback. Then, when SecondaryViewModel calls onCleared(), you just need to dispose the request and that\u2019s it, the implicit ViewModel reference can be garbage collected. You can check the full code <\/span><a href=\"https:\/\/square.github.io\/retrofit\/2.x\/retrofit\/retrofit2\/Call.html\"><span style=\"font-weight: 400\">here<\/span><\/a><span style=\"font-weight: 400\">.<\/span><\/p>\n<pre class=\"language-swift\"><code class=\"language-swift\"><b>class <\/b><span style=\"font-weight: 400\">Request(<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;<\/span><b>var <\/b><b>callback<\/b><span style=\"font-weight: 400\">: (() -&gt; Unit)?<\/span>\n\n<span style=\"font-weight: 400\">) {<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;<\/span><b>fun <\/b><span style=\"font-weight: 400\">dispose() {<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>callback <\/b><span style=\"font-weight: 400\">= <\/span><b>null<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;<\/b><span style=\"font-weight: 400\">}<\/span>\n\n<span style=\"font-weight: 400\">}<\/span>\n\n<b>object <\/b><span style=\"font-weight: 400\">Repository {<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;<\/span><b>private var <\/b><b>request<\/b><span style=\"font-weight: 400\">: Request? = <\/span><b>null<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;<\/b><i><span style=\"font-weight: 400\">\/\/ simulate a network request, but in reality just sets the callback for the response<\/span><\/i>\n\n<i><span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;<\/span><\/i><b>fun <\/b><span style=\"font-weight: 400\">performFakeRequest(callback: () -&gt; Unit): Request =<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Request(callback).<\/span><i><span style=\"font-weight: 400\">apply <\/span><\/i><b>{<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/b><b>request <\/b><span style=\"font-weight: 400\">= <\/span><b>this<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/b><b>}<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;<\/b><i><span style=\"font-weight: 400\">\/\/ simulate when the network request finishes<\/span><\/i>\n\n<i><span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;<\/span><\/i><b>fun <\/b><span style=\"font-weight: 400\">finishFakeRequest() {<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>request<\/b><span style=\"font-weight: 400\">?.<\/span><b>callback<\/b><span style=\"font-weight: 400\">?.invoke()<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;}<\/span>\n\n<span style=\"font-weight: 400\">}<\/span>\n\n<b>class <\/b><span style=\"font-weight: 400\">SecondaryViewModel : ViewModel() {<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;<\/span><b>var <\/b><b>request<\/b><span style=\"font-weight: 400\">: Request? = <\/span><b>null<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;fun <\/b><span style=\"font-weight: 400\">onPerformFakeRequestClicked() {<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Log.d(<\/span><b>\"SecondaryViewModel\"<\/b><span style=\"font-weight: 400\">, <\/span><b>\"performing request from view model <\/b><b>$this<\/b><b>\"<\/b><span style=\"font-weight: 400\">)<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>request <\/b><span style=\"font-weight: 400\">= Repository.performFakeRequest <\/span><b>{<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/b><span style=\"font-weight: 400\">Log.d(<\/span><b>\"SecondaryViewModel\"<\/b><span style=\"font-weight: 400\">, <\/span><b>\"finished request and executed callback on view model <\/b><b>$this<\/b><b>\"<\/b><span style=\"font-weight: 400\">)<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>}<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;<\/b><span style=\"font-weight: 400\">}<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;<\/span><b>override fun <\/b><span style=\"font-weight: 400\">onCleared() {<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>super<\/b><span style=\"font-weight: 400\">.onCleared()<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>request<\/b><span style=\"font-weight: 400\">?.dispose()<\/span>\n\n<span style=\"font-weight: 400\">&nbsp;&nbsp;&nbsp;}<\/span>\n\n<span style=\"font-weight: 400\">}<\/span><\/code><\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400\">Last but not least, remember when I mentioned \u201cunexpected behavior\u201d as one of the consequences of a memory leak? Let\u2019s say your app needs to perform a sequence of very important operations related to saving data locally and all of them should be completed. Suppose you implement it in a way that your ViewModel sequentially asks the Repository to perform all operations. If the ViewModel suddenly calls onCleared() in the middle of this process, it shouldn\u2019t yet be garbage collected so that it can finish all operations. As stated by Jos\u00e9 Alc\u00e9rreca, this critical logic shouldn\u2019t be in the ViewModel. It should ask the Repository to do all operations only once and forget about it.<\/span><\/p>\n<p><span style=\"font-weight: 400\">\u201c<\/span><a href=\"https:\/\/medium.com\/androiddevelopers\/viewmodels-and-livedata-patterns-antipatterns-21efaef74a54\"><span style=\"font-weight: 400\">Don\u2019t put logic in the ViewModel that is critical to saving clean state or related to data. Any call you make from a ViewModel can be the last one.<\/span><\/a><span style=\"font-weight: 400\">\u201d&nbsp;<\/span><\/p>\n<p>&nbsp;<\/p>\n<h2><span style=\"font-weight: 400\">Wrapping up<\/span><\/h2>\n<p><span style=\"font-weight: 400\">Leaking ViewModel might not be as bad as leaking Activity Context, which is probably why most people are not even aware they are leaking it and why there isn\u2019t much information about it on the Internet. But in this article you have seen that it sure can cause many problems if you are not careful enough. Nowadays, most people uninstall apps after the very first bug or issue they find, and memory leaks can cause many of those.<\/span><\/p>\n<p><span style=\"font-weight: 400\">Even though this article is based on the architecture suggested by the <\/span><a href=\"https:\/\/developer.android.com\/jetpack\/docs\/guide\"><span style=\"font-weight: 400\">Guide to app architecture<\/span><\/a><span style=\"font-weight: 400\">, there are many situations that can lead to memory leaks, whether you are using MVVM, MVP, MVC, or no architecture at all. Memory leaks have nothing to do with architecture. You should try to always avoid them as they can be very harmful.<\/span><\/p>\n<p><span style=\"font-weight: 400\">I hope you find this article useful. I tried to keep it simple and leave references with further information about the subject.<\/span><\/p>\n<p><span style=\"font-weight: 400\">The sample code that I used is available at <\/span><a href=\"https:\/\/github.com\/diegohkd\/ViewModelLeak\"><span style=\"font-weight: 400\">ViewModelLeak<\/span><\/a><span style=\"font-weight: 400\">.<br \/>\n<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400\">References<\/span><\/p>\n<p><a href=\"https:\/\/android.jlelse.eu\/memory-leak-patterns-in-android-4741a7fcb570\"><span style=\"font-weight: 400\">https:\/\/android.jlelse.eu\/memory-leak-patterns-in-android-4741a7fcb570<\/span><\/a><\/p>\n<p><a href=\"https:\/\/livebook.manning.com\/book\/kotlin-in-action\/chapter-5\/1\"><span style=\"font-weight: 400\">https:\/\/livebook.manning.com\/book\/kotlin-in-action\/chapter-5\/1<\/span><\/a><\/p>\n<p><a href=\"https:\/\/www.baeldung.com\/java-8-lambda-expressions-tips\"><span style=\"font-weight: 400\">https:\/\/www.baeldung.com\/java-8-lambda-expressions-tips<\/span><\/a><\/p>\n<p><a href=\"https:\/\/medium.com\/tompee\/idiomatic-kotlin-lambdas-and-sam-constructors-fe2075965bfb\"><span style=\"font-weight: 400\">https:\/\/medium.com\/tompee\/idiomatic-kotlin-lambdas-and-sam-constructors-fe2075965bfb<\/span><\/a><\/p>\n<p><a href=\"https:\/\/proandroiddev.com\/everything-you-need-to-know-about-memory-leaks-in-android-d7a59faaf46a\"><span style=\"font-weight: 400\">https:\/\/proandroiddev.com\/everything-you-need-to-know-about-memory-leaks-in-android-d7a59faaf46a<\/span><\/a><\/p>\n<p><a href=\"https:\/\/techbeacon.com\/app-dev-testing\/what-you-need-know-about-android-app-memory-leaks\"><span style=\"font-weight: 400\">https:\/\/techbeacon.com\/app-dev-testing\/what-you-need-know-about-android-app-memory-leaks<\/span><\/a><\/p>\n<p><a href=\"https:\/\/developer.android.com\/topic\/performance\/memory\"><span style=\"font-weight: 400\">https:\/\/developer.android.com\/topic\/performance\/memory<\/span><\/a><\/p>\n<p><a href=\"https:\/\/developer.android.com\/studio\/profile\/android-profiler\"><span style=\"font-weight: 400\">https:\/\/developer.android.com\/studio\/profile\/android-profiler<\/span><\/a><\/p>\n<p><a href=\"https:\/\/developer.android.com\/studio\/profile\/memory-profiler\"><span style=\"font-weight: 400\">https:\/\/developer.android.com\/studio\/profile\/memory-profiler<\/span><\/a><\/p>\n<p><a href=\"https:\/\/developer.android.com\/reference\/android\/arch\/lifecycle\/LiveData#observeforever\"><span style=\"font-weight: 400\">https:\/\/developer.android.com\/reference\/android\/arch\/lifecycle\/LiveData#observeforever<\/span><\/a><\/p>\n<p><a href=\"https:\/\/proandroiddev.com\/clean-easy-new-how-to-architect-your-app-part-4-livedata-transformations-f0fd9f313ec6\"><span style=\"font-weight: 400\">https:\/\/proandroiddev.com\/clean-easy-new-how-to-architect-your-app-part-4-livedata-transformations-f0fd9f313ec6<\/span><\/a><\/p>\n<p><a href=\"http:\/\/reactivex.io\/\"><span style=\"font-weight: 400\">http:\/\/reactivex.io\/<\/span><\/a><\/p>\n<p><a href=\"https:\/\/www.raywenderlich.com\/3983802-working-with-rxjava-disposables-in-kotlin\"><span style=\"font-weight: 400\">https:\/\/www.raywenderlich.com\/3983802-working-with-rxjava-disposables-in-kotlin<\/span><\/a><\/p>\n<p><a href=\"https:\/\/square.github.io\/retrofit\/2.x\/retrofit\/retrofit2\/Call.html\"><span style=\"font-weight: 400\">https:\/\/square.github.io\/retrofit\/2.x\/retrofit\/retrofit2\/Call.html<\/span><\/a><\/p>\n<p><a href=\"https:\/\/medium.com\/androiddevelopers\/viewmodels-and-livedata-patterns-antipatterns-21efaef74a54\"><span style=\"font-weight: 400\">https:\/\/medium.com\/androiddevelopers\/viewmodels-and-livedata-patterns-antipatterns-21efaef74a54<\/span><\/a><\/p>\n<p><a href=\"https:\/\/developer.android.com\/topic\/libraries\/architecture\/viewmodel\"><span style=\"font-weight: 400\">https:\/\/developer.android.com\/topic\/libraries\/architecture\/viewmodel<\/span><\/a><\/p>\n<p><span style=\"font-weight: 400\">&nbsp;<\/span><\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Guide to app architecture suggests using ViewModel + Repository with LiveData in order to implement the data flow from Repository to ViewModel. If you use this approach or anything similar, there&#8217;s a high chance you could be leaking ViewModel. To help you understand and fix this issue, this article will briefly explain what a [&hellip;]<\/p>\n","protected":false},"author":65,"featured_media":6549,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_yoast_wpseo_title":"Android ViewModel memory leaks: causes and how to fix them","_yoast_wpseo_metadesc":"Learn what a memory leak costs your Android app, why your ViewModel keeps leaking through callbacks, and how to clear those references for good.","_yoast_wpseo_meta-robots-noindex":"","_yoast_wpseo_canonical":"","footnotes":"","ckl_wpml_lang":"","ckl_wpml_source_id":0,"ckl_wpml_status":""},"categories":[1425,432],"tags":[15],"class_list":["post-6543","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-product-engineering","category-engineering","tag-tag-mobile"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Android ViewModel memory leaks: causes and how to fix them<\/title>\n<meta name=\"description\" content=\"Learn what a memory leak costs your Android app, why your ViewModel keeps leaking through callbacks, and how to clear those references for good.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Android ViewModel memory leaks: causes and how to fix them\" \/>\n<meta property=\"og:description\" content=\"Learn what a memory leak costs your Android app, why your ViewModel keeps leaking through callbacks, and how to clear those references for good.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/\" \/>\n<meta property=\"og:site_name\" content=\"Cheesecake Labs\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/cheesecakelabs\" \/>\n<meta property=\"article:published_time\" content=\"2019-11-28T18:22:21+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-08-15T08:06:21+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/Android_architecture1.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2000\" \/>\n\t<meta property=\"og:image:height\" content=\"720\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Cheesecake Labs\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@cheesecakelabs\" \/>\n<meta name=\"twitter:site\" content=\"@cheesecakelabs\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/\"},\"author\":{\"name\":\"Diego Oliveira\"},\"headline\":\"Android ViewModel leaks: why they happen and how to fix them\",\"datePublished\":\"2019-11-28T18:22:21+00:00\",\"dateModified\":\"2026-08-15T08:06:21+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/\"},\"wordCount\":1789,\"publisher\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/Android_architecture1.png\",\"keywords\":[\"Mobile\"],\"articleSection\":[\"Product Engineering\",\"Engineering\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/\",\"url\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/\",\"name\":\"Android ViewModel memory leaks: causes and how to fix them\",\"isPartOf\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/Android_architecture1.png\",\"datePublished\":\"2019-11-28T18:22:21+00:00\",\"dateModified\":\"2026-08-15T08:06:21+00:00\",\"description\":\"Learn what a memory leak costs your Android app, why your ViewModel keeps leaking through callbacks, and how to clear those references for good.\",\"breadcrumb\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#primaryimage\",\"url\":\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/Android_architecture1.png\",\"contentUrl\":\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/Android_architecture1.png\",\"width\":2000,\"height\":720,\"caption\":\"Android_architecture1 | | Cheesecake Labs\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cheesecakelabs.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Android ViewModel leaks: why they happen and how to fix them\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/#website\",\"url\":\"https:\/\/cheesecakelabs.com\/blog\/\",\"name\":\"Cheesecake Labs\",\"description\":\"AI Implementation, Data and Product Engineering\",\"publisher\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cheesecakelabs.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/#organization\",\"name\":\"Cheesecake Labs\",\"alternateName\":\"Cheesecake Labs Inc\",\"url\":\"https:\/\/cheesecakelabs.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/cheesecakelabs.com\/#logo\",\"url\":\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2022\/06\/cheesecake-labs-1.png\",\"contentUrl\":\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2022\/06\/cheesecake-labs-1.png\",\"caption\":\"Cheesecake Labs\",\"inLanguage\":\"en\"},\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/cheesecakelabs.com\/#primary-image\",\"url\":\"https:\/\/ckl-website-v4-strapi-prod.s3.us-east-2.amazonaws.com\/ai_software_development_company_83fb512983.webp\",\"contentUrl\":\"https:\/\/ckl-website-v4-strapi-prod.s3.us-east-2.amazonaws.com\/ai_software_development_company_83fb512983.webp\",\"width\":1920,\"height\":1080,\"caption\":\"Cheesecake Labs \u2014 AI, Data & Blockchain software development services\",\"inLanguage\":\"en\"},\"sameAs\":[\"https:\/\/www.facebook.com\/cheesecakelabs\",\"https:\/\/x.com\/cheesecakelabs\",\"https:\/\/www.instagram.com\/cheesecakelabs\/\",\"https:\/\/www.linkedin.com\/company\/cheesecake-labs\/\",\"https:\/\/www.youtube.com\/channel\/UCdGEQ5AHJcmIlaOaI5fGGVA\",\"https:\/\/clutch.co\/profile\/cheesecake-labs\",\"https:\/\/www.behance.net\/cheesecakelabs\",\"https:\/\/dribbble.com\/cheesecakelabs\",\"https:\/\/www.designrush.com\/agency\/profile\/cheesecake-labs\",\"https:\/\/www.g2.com\/products\/cheesecake-labs\/reviews\"],\"description\":\"Cheesecake Labs is a software development studio that designs and builds custom digital products \u2014 web, mobile, and platforms \u2014 combining product design and high-performance engineering.\",\"foundingDate\":\"2013\"},{\"@type\":\"Person\",\"name\":\"Diego Oliveira\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2017\/06\/diego-300x300.jpg\",\"contentUrl\":\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2017\/06\/diego-300x300.jpg\",\"caption\":\"Diego Oliveira\"},\"description\":\"10 years of experience in Marketing and Sales in the Technology sector. My main purpose is help, support and structure efficient operations and also develop independent and multidisciplinary teams.\",\"url\":\"https:\/\/cheesecakelabs.com\/blog\/autor\/diegooliveira\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Android ViewModel memory leaks: causes and how to fix them","description":"Learn what a memory leak costs your Android app, why your ViewModel keeps leaking through callbacks, and how to clear those references for good.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/","og_locale":"en_US","og_type":"article","og_title":"Android ViewModel memory leaks: causes and how to fix them","og_description":"Learn what a memory leak costs your Android app, why your ViewModel keeps leaking through callbacks, and how to clear those references for good.","og_url":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/","og_site_name":"Cheesecake Labs","article_publisher":"https:\/\/www.facebook.com\/cheesecakelabs","article_published_time":"2019-11-28T18:22:21+00:00","article_modified_time":"2026-08-15T08:06:21+00:00","og_image":[{"width":2000,"height":720,"url":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/Android_architecture1.png","type":"image\/png"}],"author":"Cheesecake Labs","twitter_card":"summary_large_image","twitter_creator":"@cheesecakelabs","twitter_site":"@cheesecakelabs","twitter_misc":{"Written by":null,"Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#article","isPartOf":{"@id":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/"},"author":{"name":"Diego Oliveira"},"headline":"Android ViewModel leaks: why they happen and how to fix them","datePublished":"2019-11-28T18:22:21+00:00","dateModified":"2026-08-15T08:06:21+00:00","mainEntityOfPage":{"@id":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/"},"wordCount":1789,"publisher":{"@id":"https:\/\/cheesecakelabs.com\/blog\/#organization"},"image":{"@id":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#primaryimage"},"thumbnailUrl":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/Android_architecture1.png","keywords":["Mobile"],"articleSection":["Product Engineering","Engineering"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/","url":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/","name":"Android ViewModel memory leaks: causes and how to fix them","isPartOf":{"@id":"https:\/\/cheesecakelabs.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#primaryimage"},"image":{"@id":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#primaryimage"},"thumbnailUrl":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/Android_architecture1.png","datePublished":"2019-11-28T18:22:21+00:00","dateModified":"2026-08-15T08:06:21+00:00","description":"Learn what a memory leak costs your Android app, why your ViewModel keeps leaking through callbacks, and how to clear those references for good.","breadcrumb":{"@id":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#primaryimage","url":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/Android_architecture1.png","contentUrl":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/Android_architecture1.png","width":2000,"height":720,"caption":"Android_architecture1 | | Cheesecake Labs"},{"@type":"BreadcrumbList","@id":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cheesecakelabs.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Android ViewModel leaks: why they happen and how to fix them"}]},{"@type":"WebSite","@id":"https:\/\/cheesecakelabs.com\/blog\/#website","url":"https:\/\/cheesecakelabs.com\/blog\/","name":"Cheesecake Labs","description":"AI Implementation, Data and Product Engineering","publisher":{"@id":"https:\/\/cheesecakelabs.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cheesecakelabs.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cheesecakelabs.com\/blog\/#organization","name":"Cheesecake Labs","alternateName":"Cheesecake Labs Inc","url":"https:\/\/cheesecakelabs.com\/","logo":{"@type":"ImageObject","@id":"https:\/\/cheesecakelabs.com\/#logo","url":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2022\/06\/cheesecake-labs-1.png","contentUrl":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2022\/06\/cheesecake-labs-1.png","caption":"Cheesecake Labs","inLanguage":"en"},"image":{"@type":"ImageObject","@id":"https:\/\/cheesecakelabs.com\/#primary-image","url":"https:\/\/ckl-website-v4-strapi-prod.s3.us-east-2.amazonaws.com\/ai_software_development_company_83fb512983.webp","contentUrl":"https:\/\/ckl-website-v4-strapi-prod.s3.us-east-2.amazonaws.com\/ai_software_development_company_83fb512983.webp","width":1920,"height":1080,"caption":"Cheesecake Labs \u2014 AI, Data & Blockchain software development services","inLanguage":"en"},"sameAs":["https:\/\/www.facebook.com\/cheesecakelabs","https:\/\/x.com\/cheesecakelabs","https:\/\/www.instagram.com\/cheesecakelabs\/","https:\/\/www.linkedin.com\/company\/cheesecake-labs\/","https:\/\/www.youtube.com\/channel\/UCdGEQ5AHJcmIlaOaI5fGGVA","https:\/\/clutch.co\/profile\/cheesecake-labs","https:\/\/www.behance.net\/cheesecakelabs","https:\/\/dribbble.com\/cheesecakelabs","https:\/\/www.designrush.com\/agency\/profile\/cheesecake-labs","https:\/\/www.g2.com\/products\/cheesecake-labs\/reviews"],"description":"Cheesecake Labs is a software development studio that designs and builds custom digital products \u2014 web, mobile, and platforms \u2014 combining product design and high-performance engineering.","foundingDate":"2013"},{"@type":"Person","name":"Diego Oliveira","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cheesecakelabs.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2017\/06\/diego-300x300.jpg","contentUrl":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2017\/06\/diego-300x300.jpg","caption":"Diego Oliveira"},"description":"10 years of experience in Marketing and Sales in the Technology sector. My main purpose is help, support and structure efficient operations and also develop independent and multidisciplinary teams.","url":"https:\/\/cheesecakelabs.com\/blog\/autor\/diegooliveira\/"}]}},"_links":{"self":[{"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/posts\/6543","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/users\/65"}],"replies":[{"embeddable":true,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/comments?post=6543"}],"version-history":[{"count":2,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/posts\/6543\/revisions"}],"predecessor-version":[{"id":14570,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/posts\/6543\/revisions\/14570"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/media\/6549"}],"wp:attachment":[{"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/media?parent=6543"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/categories?post=6543"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/tags?post=6543"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}