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

Workaround memory leak in dispatchers. #119

Merged
merged 2 commits into from
Nov 10, 2017
Merged

Workaround memory leak in dispatchers. #119

merged 2 commits into from
Nov 10, 2017

Conversation

jpd236
Copy link
Collaborator

@jpd236 jpd236 commented Nov 10, 2017

Android has a long-standing known issue where local variables aren't
explicitly cleared even when they go out of scope, which can cause
their contents to leak. Since BlockingQueue#take() blocks
forever until a new item is ready, this means the last
request will remain in memory until a new request pushes it
out. Extracting a helper method is a workaround for this - see, for
example, the following CL in the Android support lib:

https://android.googlesource.com/platform/frameworks/support/+/cd07a0cfd9c9501a03c574d2d48df51c82b73e33

The following other solutions were attempted but were not sufficient:

  • Clear the variable prior to take() - optimized out because the write
    is not observable, so it has no impact on the bytecode.

  • Call poll() prior to take() - for some reason, this doesn't work
    when proguard optimization is on.

With code optimization, there's no guarantee that this will work.
However, it appears to be the best we can do and follows precedent /
advice from the ART team.

Should contain no functional changes otherwise as this is just
extracting code to a helper method, and thus should be safe for 1.1.0.

Verified against provided sample app.

Fixes #114

Android has a long-standing known issue where local variables aren't
explicitly cleared even when they go out of scope, which can cause
their contents to leak. Since BlockingQueue#take() blocks
forever until a new item is ready, this means the last
request will remain in memory until a new request pushes it
out. Extracting a helper method is a workaround for this - see, for
example, the following CL in the Android support lib:

https://android.googlesource.com/platform/frameworks/support/+/cd07a0cfd9c9501a03c574d2d48df51c82b73e33

The following other solutions were attempted but were not sufficient:

- Clear the variable prior to take() - optimized out because the write
  is not observable, so it has no impact on the bytecode.

- Call poll() prior to take() - for some reason, this doesn't work
  when proguard optimization is on.

With code optimization, there's no guarantee that this will work.
However, it appears to be the best we can do and follows precedent /
advice from the ART team.

Should contain no functional changes otherwise as this is just
extracting code to a helper method, and thus should be safe for 1.1.0.

Verified against provided sample app.

Fixes google#114
@jpd236 jpd236 requested a review from jjoslin November 10, 2017 02:39
@jpd236
Copy link
Collaborator Author

jpd236 commented Nov 10, 2017

OK - I'm reasonably confident this is the best we can do. PTAL - thanks :)

@joebowbeer
Copy link
Contributor

I glanced at the diffs and nothing pops out.

This compiler behavior violates JLS. I suggest marking the workarounds with the compiler bug's URL for tracking, so the workaround can be removed eventually.

Something like this existed at one time, but was wiped out years ago. I think this current issue is something new, and will be wiped out eventually.

@jpd236
Copy link
Collaborator Author

jpd236 commented Nov 10, 2017

From what I've gathered internally it's not actually a violation of JLS, which doesn't specify anything about when the variable in question must become inaccessible. It's not a correctness issue, only an issue with the fact that the object which is out of scope might be heavy and thus should be more aggressively pruned. So it becomes a question of whether something further down the line can make the needed optimization here, and doing so safely in all circumstances is challenging.

The bugs tracking this are internal to Google, but for our reference, the primary bug number is 33158143.

@jpd236
Copy link
Collaborator Author

jpd236 commented Nov 10, 2017

One other thing I'd like to look into before merging would be adding a consumerProguardFile with rules to "-keep,allowshrinking,allowobfuscation" both of the methods that we don't want to inline. That should offer a stronger guarantee of no inlining, even if it seems like the problem doesn't manifest with proguard on.

@jpd236 jpd236 merged commit 536c1b7 into google:master Nov 10, 2017
@jpd236 jpd236 deleted the leak branch November 10, 2017 22:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants