Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Leak Canary report #114

Closed
rohansohonee opened this issue Dec 4, 2019 · 18 comments
Closed

Leak Canary report #114

rohansohonee opened this issue Dec 4, 2019 · 18 comments
Assignees

Comments

@rohansohonee
Copy link
Contributor

Describe the bug
The application is leaking memory when opening and closing the application.

Minimal reproduction project
Example project produces this report.

To Reproduce
Steps to reproduce the behavior:

  1. Run the example project.
  2. Open the app.
  3. Close the app using back button.
  4. Leak canary will generate a report.

Expected behavior
No memory leaks should be present in the application.

Leak Canary Report
ApplicationLeak(className=com.ryanheise.audioserviceexample.MainActivity, leakTrace=

├─ android.os.HandlerThread
│    Leaking: NO (PathClassLoader↓ is not leaking)
│    Thread name: 'LeakCanary-Heap-Dump'
│    GC Root: Thread object
│    ↓ thread HandlerThread.contextClassLoader
├─ dalvik.system.PathClassLoader
│    Leaking: NO (Object[]↓ is not leaking and A ClassLoader is never leaking)
│    ↓ PathClassLoader.runtimeInternalObjects
├─ java.lang.Object[]
│    Leaking: NO (AudioServicePlugin↓ is not leaking)
│    ↓ array Object[].[512]
├─ com.ryanheise.audioservice.AudioServicePlugin
│    Leaking: NO (a class is never leaking)
│    ↓ static AudioServicePlugin.clientHandler
│                                ~~~~~~~~~~~~~
├─ com.ryanheise.audioservice.AudioServicePlugin$ClientHandler
│    Leaking: UNKNOWN
│    ↓ AudioServicePlugin$ClientHandler.connectionCallback
│                                       ~~~~~~~~~~~~~~~~~~
├─ com.ryanheise.audioservice.AudioServicePlugin$ClientHandler$3
│    Leaking: UNKNOWN
│    Anonymous subclass of android.support.v4.media.MediaBrowserCompat$ConnectionCallback
│    ↓ AudioServicePlugin$ClientHandler$3.mConnectionCallbackInternal
│                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~
├─ android.support.v4.media.MediaBrowserCompat$MediaBrowserImplApi26
│    Leaking: UNKNOWN
│    ↓ MediaBrowserCompat$MediaBrowserImplApi26.mContext
│                                               ~~~~~~~~
╰→ com.ryanheise.audioserviceexample.MainActivity
     Leaking: YES (Activity#mDestroyed is true and ObjectWatcher was watching this)
     key = 67498789-00a3-42fe-b139-6eaddb476355
     watchDurationMillis = 5135
     retainedDurationMillis = 133
, retainedHeapByteSize=22818) 

Runtime Environment:

  • Device: [10.or G]
  • Android version: [8.1.0]

Flutter SDK version

Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, v1.9.1+hotfix.6, on Microsoft Windows [Version 10.0.18363.476], locale en-IN)
 
[√] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
[√] Android Studio (version 3.5)
[√] VS Code (version 1.40.2)
[√] Connected device (1 available)

• No issues found!

Additional Context
Leak Canary getting started

dependencies {
  // debugImplementation because LeakCanary should only run in debug builds.
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0'
}
@rohansohonee
Copy link
Contributor Author

@ryanheise
Please look into this issue as it is very serious memory leak problem.

@rohansohonee
Copy link
Contributor Author

Screenshot_20191204-141456~2
Very high memory usage when running example project.

@ryanheise
Copy link
Owner

A memory leak is only of concern in this case if new memory is being allocated each time the app is re-entered without de-allocating the old memory because otherwise the app is only allocating constant memory which the O/S can reclaim whenever it's running low on memory. So even if audio_service retains memory after Activity.onDestroy, that would not necessarily cause the very high memory usage. To determine that, you would need to provide more information. What was the memory usage on the first run of the app, and the second, and the third, and so on? If each time you back out and re-enter the app the memory usage grows, then that is of concern.

I am doubtful that a leak is causing memory usage to grow because the references that I do retain are not stored in a growing list, sot the memory usage should stay roughly constant(*), and can be cleared as needed by the O/S.

(*) At most, there will be two instances of the plugin, and each instance should have constant memory usage.

@rohansohonee
Copy link
Contributor Author

Each time I back out of the app and re-enter the app the memory usage keeps growing.
Also leak canary watches for instances being leaked. That is why I have shared the leak canary report here.

├─ com.ryanheise.audioservice.AudioServicePlugin$ClientHandler
Leaking: UNKNOWN
│ ↓ AudioServicePlugin$ClientHandler.connectionCallback
│ ~~~~~~~~~~~~~~~~~~
├─ com.ryanheise.audioservice.AudioServicePlugin$ClientHandler$3
Leaking: UNKNOWN
│ Anonymous subclass of android.support.v4.media.MediaBrowserCompat$ConnectionCallback
│ ↓ AudioServicePlugin$ClientHandler$3.mConnectionCallbackInternal
│ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
├─ android.support.v4.media.MediaBrowserCompat$MediaBrowserImplApi26
Leaking: UNKNOWN
│ ↓ MediaBrowserCompat$MediaBrowserImplApi26.mContext
│ ~~~~~~~~
╰→ com.ryanheise.audioserviceexample.MainActivity
Leaking: YES(Activity#mDestroyed is true and ObjectWatcher was watching this)
key = 67498789-00a3-42fe-b139-6eaddb476355
watchDurationMillis = 5135
retainedDurationMillis = 133
, retainedHeapByteSize=22818)

This issue is very important because the leak occurs without even starting your audioservice.
Just opening the app and closing it makes leak canary fire up.

My understanding of the report is that ClientHandler class is leaking.

@rohansohonee
Copy link
Contributor Author

The memory usage growth that I monitored was as follows:
(used flutter profile command for monitoring memory usage)

  1. 10-20 MB
  2. Then 50-80 MB
  3. Then 120-190 MB
  4. 190-250 MB
    and so on..

I manually also ran GC using the dev tools. GC was also unable to purge all the memory.
Before running GC: 246 MB memory usage
After running GC: 193 MB memory usage
And running GC again and again made no difference.

Finally the maximum usage reached a whopping 2.7 GB when viewed on android device, which was very alarming.

@ryanheise
Copy link
Owner

I agree this is alarming. I'll investigate today.

@ryanheise
Copy link
Owner

@rohansohonee I was unable to reproduce the runaway memory problem. On my phone (Pixel 3a), the memory remained constant after repeatedly backing out and re-entering the app. However I addressed the memory leaks reported by LeakCanary, so please let me know if it solves the issue for you.

@rohansohonee
Copy link
Contributor Author

Ok Will be checking now

@rohansohonee
Copy link
Contributor Author

19942 bytes retained

├─ android.speech.tts.TextToSpeech$Connection$1
│    Leaking: UNKNOWN
│    Anonymous subclass of android.speech.tts.ITextToSpeechCallback$Stub
│    GC Root: Global variable in native code
│    ↓ TextToSpeech$Connection$1.this$1
│                                ~~~~~~
├─ android.speech.tts.TextToSpeech$Connection
│    Leaking: UNKNOWN
│    ↓ TextToSpeech$Connection.this$0
│                              ~~~~~~
├─ android.speech.tts.TextToSpeech
│    Leaking: UNKNOWN
│    ↓ TextToSpeech.mUtteranceProgressListener
│                   ~~~~~~~~~~~~~~~~~~~~~~~~~~
├─ com.tundralabs.fluttertts.FlutterTtsPlugin$1
│    Leaking: UNKNOWN
│    Anonymous subclass of android.speech.tts.UtteranceProgressListener
│    ↓ FlutterTtsPlugin$1.this$0
│                         ~~~~~~
├─ com.tundralabs.fluttertts.FlutterTtsPlugin
│    Leaking: UNKNOWN
│    ↓ FlutterTtsPlugin.channel
│                       ~~~~~~~
├─ io.flutter.plugin.common.MethodChannel
│    Leaking: UNKNOWN
│    ↓ MethodChannel.messenger
│                    ~~~~~~~~~
├─ io.flutter.view.FlutterNativeView
│    Leaking: UNKNOWN
│    ↓ FlutterNativeView.mPluginRegistry
│                        ~~~~~~~~~~~~~~~
├─ io.flutter.app.FlutterPluginRegistry
│    Leaking: UNKNOWN
│    ↓ FlutterPluginRegistry.mActivity
│                            ~~~~~~~~~
╰→ com.ryanheise.audioserviceexample.MainActivity
     Leaking: YES (Activity#mDestroyed is true and ObjectWatcher was watching this)
     key = 375c2e12-1f21-435b-8b41-92de4621e72c
     watchDurationMillis = 5124
     retainedDurationMillis = 124

METADATA

Build.VERSION.SDK_INT: 27
Build.MANUFACTURER: 10or
LeakCanary version: 2.0
App process name: com.ryanheise.audioserviceexample
Analysis duration: 12927 ms 

@rohansohonee
Copy link
Contributor Author

@ryanheise

Now it seems the leak is coming from TTS plugin

@ryanheise
Copy link
Owner

Since that's a separate plugin, would you say the issue with audio_service is resolved?

@rohansohonee
Copy link
Contributor Author

Yes

@ryanheise
Copy link
Owner

Great, good to know and thanks for reporting.

@rohansohonee
Copy link
Contributor Author

Hi @ryanheise

Can we reopen this issue. The latest commit Support Flutter 1.12's new v2 embedding model is leaking memory. Here is the stack trace that leak canary generated:

┬───
│ GC Root: Global variable in native code
│
├─ io.flutter.embedding.engine.FlutterJNI class
│    Leaking: NO (a class is never leaking)
│    ↓ static FlutterJNI.asyncWaitForVsyncDelegate
│                        ~~~~~~~~~~~~~~~~~~~~~~~~~
├─ io.flutter.view.VsyncWaiter$1 instance
│    Leaking: UNKNOWN
│    Anonymous class implementing io.flutter.embedding.engine.FlutterJNI$AsyncWaitForVsyncDelegate
│    ↓ VsyncWaiter$1.this$0
│                    ~~~~~~
├─ io.flutter.view.VsyncWaiter instance
│    Leaking: UNKNOWN
│    ↓ VsyncWaiter.windowManager
│                  ~~~~~~~~~~~~~
├─ android.view.WindowManagerImpl instance
│    Leaking: UNKNOWN
│    ↓ WindowManagerImpl.mContext
│                        ~~~~~~~~
├─ android.app.ContextImpl instance
│    Leaking: UNKNOWN
│    ↓ ContextImpl.mAutofillClient
│                  ~~~~~~~~~~~~~~~
╰→ com.ryanheise.audioserviceexample.MainActivity instance
     Leaking: YES (ObjectWatcher was watching this because com.ryanheise.audioserviceexample.MainActivity received Activity#onDestroy() callback and Activity#mDestroyed is true)
     key = 188e9832-ade8-4c58-8094-5545e59ae865
     watchDurationMillis = 5204
     retainedDurationMillis = 199

METADATA

Build.VERSION.SDK_INT: 27
Build.MANUFACTURER: 10or
LeakCanary version: 2.1
App process name: com.ryanheise.audioserviceexample
Analysis duration: 12492 ms 

Do have a look at this.

@ryanheise
Copy link
Owner

ryanheise commented Jan 28, 2020

If this is related to the new v2 implementation I just committed, I've still left that issue open so it would be best to post all issues related to that commit in that issue (i.e. #145).

@rohansohonee
Copy link
Contributor Author

@ryanheise
version 0.10.0

┬───│ GC Root: Local variable in native code│
├─ android.os.HandlerThread instance
│    Leaking: NO (PathClassLoader↓ is not leaking)
│    Thread name: 'LeakCanary-Heap-Dump'
│    ↓ HandlerThread.contextClassLoader
├─ dalvik.system.PathClassLoader instance
│    Leaking: NO (AudioServicePlugin↓ is not leaking and A ClassLoader is never leaking)
│    ↓ PathClassLoader.runtimeInternalObjects
├─ java.lang.Object[] array
│    Leaking: NO (AudioServicePlugin↓ is not leaking)
│    ↓ Object[].[532]
├─ com.ryanheise.audioservice.AudioServicePlugin class
│    Leaking: NO (a class is never leaking)
│    ↓ static AudioServicePlugin.mainClientHandler
│                                ~~~~~~~~~~~~~~~~~
├─ com.ryanheise.audioservice.AudioServicePlugin$ClientHandler instance
│    Leaking: UNKNOWN
│    ↓ AudioServicePlugin$ClientHandler.connectionCallback
│                                       ~~~~~~~~~~~~~~~~~~
├─ com.ryanheise.audioservice.AudioServicePlugin$ClientHandler$3 instance
│    Leaking: UNKNOWN
│    Anonymous subclass of android.support.v4.media.MediaBrowserCompat$ConnectionCallback
│    ↓ AudioServicePlugin$ClientHandler$3.mConnectionCallbackInternal
│                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~
├─ android.support.v4.media.MediaBrowserCompat$MediaBrowserImplApi26 instance
│    Leaking: UNKNOWN
│    ↓ MediaBrowserCompat$MediaBrowserImplApi26.mContext
│                                               ~~~~~~~~
╰→ com.ryanheise.audioserviceexample.MainActivity instance
     Leaking: YES (ObjectWatcher was watching this because com.ryanheise.audioserviceexample.MainActivity received Activity#onDestroy() callback and Activity#mDestroyed is true)
     key = 7ea03580-2db2-43b5-b542-73f6617253e7
     watchDurationMillis = 5141
     retainedDurationMillis = 140

METADATA
Build.VERSION.SDK_INT: 27Build.MANUFACTURER: 10or
LeakCanary version: 2.3
App process name: com.ryanheise.audioserviceexample
Analysis duration: 12590 ms

@ryanheise
Copy link
Owner

Thanks, I have fixed it in the latest commit (aside from the leak that comes from the flutter_tts plugin).

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs, or use StackOverflow if you need help with audio_service.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 19, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants