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

SI-8946: Fixes memory leak when using reflection #4148

Merged
merged 1 commit into from
Nov 26, 2014

Conversation

timcharper
Copy link
Contributor

References to Threads would be retained long after their termination if
reflection is used in them. This led to a steady, long memory leak in
applications using reflection in thread pools.

Closes https://issues.scala-lang.org/browse/SI-8946

Mentioning @retronym @xeno-by

@scala-jenkins scala-jenkins added this to the 2.11.5 milestone Nov 23, 2014
@timcharper
Copy link
Contributor Author

Not sure why this failed after rebase; should've been harmless. Investigating.

@timcharper
Copy link
Contributor Author

I'd forgotten to build against JDK6 locally :( My bad. I'll get this fixed soon.

@timcharper
Copy link
Contributor Author

this should build against JDK6:

References to Threads would be retained long after their termination if
reflection is used in them. This led to a steady, long memory leak in
applications using reflection in thread pools.
@timcharper
Copy link
Contributor Author

@retronym @xeno-by the tests are now passing for this pull request.

@timcharper timcharper changed the title Fixes memory leak when using reflection SI-8946: Fixes memory leak when using reflection Nov 24, 2014
@adriaanm
Copy link
Contributor

Thanks!

@xeno-by
Copy link
Contributor

xeno-by commented Nov 26, 2014

LGTM. @retronym WDYT?

@retronym
Copy link
Member

I had to look at this a few times to convince myself that it is okay.

But I can now see that the fact we're running on thread which is the key to the map prevents usual problems with concurrent map mutation and with a weak reference being collected between the containsKey and get calls. So LGTM, too.

Thanks again, @timcharper!

retronym added a commit that referenced this pull request Nov 26, 2014
SI-8946: Fixes memory leak when using reflection
@retronym retronym merged commit b1b5b90 into scala:2.11.x Nov 26, 2014
@adriaanm
Copy link
Contributor

Unfortunately the assert is failing on jenkins (under load, likely):

fail - run/t8946.scala  [non-zero exit code]% scalac t8946.scala
% /home/jenkins/apps/jdk1.6.0_33-x64/jre/bin/java \
  -classpath \
  /localhome/jenkins/c/workspace/scala-checkin/test/files/run/t8946-run.obj:/localhome/jenkins/c/workspace/scala-checkin/build/pack/lib/scala-library.jar:/localhome/jenkins/c/workspace/scala-checkin/build/pack/lib/scala-reflect.jar:/localhome/jenkins/c/workspace/scala-checkin/build/pack/lib/scala-compiler.jar:/localhome/jenkins/c/workspace/scala-checkin/build/pack/lib/scalap.jar:/localhome/jenkins/c/workspace/scala-checkin/build/pack/lib/scala-actors.jar:/localhome/jenkins/c/workspace/scala-checkin/m2repo/org/scala-lang/modules/scala-partest_2.11/1.0.1/scala-partest_2.11-1.0.1.jar:/localhome/jenkins/c/workspace/scala-checkin/m2repo/org/apache/ant/ant/1.8.4/ant-1.8.4.jar:/localhome/jenkins/c/workspace/scala-checkin/m2repo/org/apache/ant/ant-launcher/1.8.4/ant-launcher-1.8.4.jar:/localhome/jenkins/c/workspace/scala-checkin/m2repo/com/googlecode/java-diff-utils/diffutils/1.3.0/diffutils-1.3.0.jar:/localhome/jenkins/c/workspace/scala-checkin/m2repo/org/scala-sbt/test-interface/1.0/test-interface-1.0.jar:/localhome/jenkins/c/workspace/scala-checkin/m2repo/org/scala-lang/modules/scala-xml_2.11/1.0.2/scala-xml_2.11-1.0.2.jar:/localhome/jenkins/c/workspace/scala-checkin/m2repo/org/scala-lang/modules/scala-parser-combinators_2.11/1.0.2/scala-parser-combinators_2.11-1.0.2.jar:/localhome/jenkins/c/workspace/scala-checkin/m2repo/org/scalacheck/scalacheck_2.11/1.11.4/scalacheck_2.11-1.11.4.jar:/localhome/jenkins/c/workspace/scala-checkin/build/pack/lib/scala-partest-extras.jar:/localhome/jenkins/c/workspace/scala-checkin/build/pack/lib/scala-partest-javaagent.jar:/localhome/jenkins/c/workspace/scala-checkin/test/files/lib/annotations.jar:/localhome/jenkins/c/workspace/scala-checkin/test/files/lib/enums.jar:/localhome/jenkins/c/workspace/scala-checkin/test/files/lib/genericNest.jar:/localhome/jenkins/c/workspace/scala-checkin/test/files/lib/jsoup-1.3.1.jar:/localhome/jenkins/c/workspace/scala-checkin/test/files/lib/macro210.jar:/localhome/jenkins/c/workspace/scala-checkin/test/files/lib/methvsfield.jar:/localhome/jenkins/c/workspace/scala-checkin/test/files/lib/nest.jar \
  -Dfile.encoding=UTF-8 \
  -Djava.library.path=/localhome/jenkins/c/workspace/scala-checkin/test/files/run \
  -Dpartest.output=/localhome/jenkins/c/workspace/scala-checkin/test/files/run/t8946-run.obj \
  -Dpartest.lib=/localhome/jenkins/c/workspace/scala-checkin/build/pack/lib/scala-library.jar \
  -Dpartest.reflect=/localhome/jenkins/c/workspace/scala-checkin/build/pack/lib/scala-reflect.jar \
  -Dpartest.cwd=/localhome/jenkins/c/workspace/scala-checkin/test/files/run \
  -Dpartest.test-path=/localhome/jenkins/c/workspace/scala-checkin/test/files/run/t8946.scala \
  -Dpartest.testname=t8946 \
  -Djavacmd=/home/jenkins/apps/jdk1.6.0_33-x64/jre/bin/java \
  -Djavaccmd=javac \
  -Duser.language=en \
  -Duser.country=US \
  scala.tools.nsc.MainGenericRunner \
  -usejavacp \
  Test \
  jvm > t8946-run.log
java.lang.AssertionError: assertion failed: 1 threads were retained; expected 0.
        at Test$.main(t8946.scala:27)

@som-snytt
Copy link
Contributor

I haven't looked at this failure, but I have a different approach at

#4145

to checking that a ref is collectable. There is no guarantee that refs are cleared in a particular order, so it's safer to wait on the ref queue. In fact, the allocator could check the ref and short-circuit the wait: the docs say the ref is enqueued maybe later, after the ref is cleared.

I added a collectable assertion, which maybe should use the queue instead of the optimistic approach used here.

#4133

@timcharper
Copy link
Contributor Author

Som-snytt - interesting approach. I'll review and incorporate your ideas, and will send another patch here soon (tonight). Sorry@adriaanm for the trouble!

@timcharper
Copy link
Contributor Author

@som-snytt it looks like you are doing a very similar approach to mine for checking collectability; and, it looks like the tests in your examples may be prone to the same error as I am seeing.

I think a retry mechanism will work best. Also, if after a set amount of retries, one or more threads are garbage collector, I think victory can be declared

@timcharper
Copy link
Contributor Author

Please see the following patch @som-snytt @adriaanm SpinGo@286538b

@som-snytt
Copy link
Contributor

I like your idea of giving the gc many references and only caring if one is cleared, since that shows that they are all collectable in principle.

In fact, given a generator of a (new) reference, the test rig can invoke the generator any number of times, which can only increase our chances of noticing a collection.

I used your strategy (of checking for cleared ref after an arbitrary ref was cleared) in the first commit here:

#4133

but the second commit uses that other strategy of just blocking on the queue while a thread generates garbage. The thread stops generating garbage if the heap expands or the reference is cleared.

The docs say that when a ref is cleared, enqueuing happens at the same time or later. So there is room for further improvement: if the allocator notices the ref is cleared, it should notify the test thread right away rather than let it wait for enqueuing. But that is very refined.

I tried to avoid arbitrary sleeps and retries, since tuning that is hard. Tuning a timeout is fairly simple, though it suffers from long waits for failure. Eventually, I'd want a long wait for Jenkins and a short wait when I run unit tests locally.

@som-snytt
Copy link
Contributor

I hope a fix can get merged by someone not cooking a turkey today. Even just relaxing the assert, n < 16 instead of n == 0. @adriaanm @retronym

@retronym
Copy link
Member

I would suggest moving the test case from run to disabled.

Then you can eat your fill of turkey before reasoning about garbage collection as dessert.

@retronym
Copy link
Member

#4170

@som-snytt
Copy link
Contributor

There's a test at

som-snytt@7029bb7

#4133

The tests pass, but maybe I'll ask Jenkins nicely to retry a few times, just to confirm.

griggt added a commit to griggt/dotty that referenced this pull request Sep 25, 2020
In the spirit of scala/scala#4148, which fixed SI-8731.

The consensus seems to be that the intent of the @switch annotation is to
warn when the generated bytecode may be poor, not merely if the compiler
elects to not emit a tableswitch/lookupswitch.

Note that the case threshold for determining whether a switch is emitted
is implementation dependent, and currently varies between scalac and dotc.

Also note that this implementation will not warn in instances where a
switch will never be emitted (e.g. because the match is on a non-integral
type) but the number of cases is below the warning threshold. This behavior
is consistent with scalac, but may be surprising to the user if another
case is added later and a warning suddenly appears.

Not all spurious @switch warnings are addressed by this commit, see
issue scala#5070 for an example.
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.

6 participants