How to avoid memory leak in context.getSystemServi

2019-06-20 16:37发布

问题:

I've found a memory leak in Android 5.x Camera2 API which I also reported. The problem is when you use Android Lollipop device that has Camera2 API implemented in LEGACY mode. On such devices, calling context.getSystemService(Context.CAMERA_SERVICE) causes context to be retained so that it won't be garbage collected.

If this context is your activity that is started multiple times, you can end up with hanging references to dozens of instances of your activity that are never garbage collected.

The issue appears to happen only on Lollipop devices that have Camera2 API implemented in LEGACY mode (e.g. HTC One M8, Samsung Galaxy S4), while it does not happen on Samsung Galaxy S6 which implements Camera2 API in FULL mode.

To demonstrate the issue, I've created a small demo app. The app contains two activities: first that contains a button which calls the second activity. The second activity obtains the CameraManager and queries the level of Camera2 API support for first back-facing camera and returns the result to first activity. If you run the app on device that implements Camera2 API in LEGACY mode, after tapping the button 98 times, causing GC and then dumping HPROF you will see exactly 98 live instances of Main2Activity, like this http://www.pohrani.com/f/1H/gs/4EFlHKoj/sgs4.png

If you do the same on device that implements Camera2 API in FULL mode, you will see 0 live instances of Main2Activity, like this http://www.pohrani.com/f/2q/bV/4srUZIJL/sgs6.png

Is there a way to workaround this leak?

One might ask why am I doing this? At our company, we are developing barcode and OCR scanning solutions and also a famous PhotoMath app. So we have a scan activity that controls the camera and scanning process. While starting up, activity checks if device supports Camera2 API in either FULL or LIMITED mode and attempts to use it for better performance, whilst if Camera2 API is in LEGACY mode, then we prefer using camera management using old camera API, as we do on pre-Lollipop devices.

Because of the mentioned memory leak, whenever a client that has integrated our SDK into its app starts the scan activity, performs a scan and obtains the result, one instance of scan activity will be leaked due to the bug. If client scans a lot, this can eat up a more than 20 MB of memory - a serious issue!

So if someone knows how to make a workaround for this issue, I'll be eternally grateful!

回答1:

Is there a way to workaround this leak?

You could call getSystemService() on the Application singleton. So, instead of:

getSystemService(CAMERA_SERVICE)

you would use:

getApplicationContext().getSystemService(CAMERA_SERVICE)

If your assessment is correct, then this will cause those additional references to be to the existing Application singleton, which is always around in your process. It's effectively "pre-leaked", and you cannot leak it further by having more references to it.



回答2:

This is an Android bug that was fixed in L MR1.

Basically, the CameraManager retained a reference to the context it was created with, and then connected to the camera service. This connection kept the camera manager instance alive indefinitely, and therefore also kept the Context alive indefinitely.

This was fixed in L MR1 to correctly allow CameraManager objects to be reclaimed when no longer referenced.