dependency-analysis-gradle-plugin: `buildHealth` execution on CI environment leads to GC overhead OutMemoryError

plugin=0.50 branch=https://github.com/avito-tech/avito-android/tree/dependencies-analysis-0.50 gradle=6.5 agp=3.6.2 buildscan=https://scans.gradle.com/s/qbe2q7cuabqhc

Describe the bug When I execute buildHealth locally everything works fine. But every CI execution leads to OutOfMemoryError On CI build is executed in Docker. shell_script=https://github.com/avito-tech/avito-android/blob/dependencies-analysis-0.50/build.sh Everything was going bad at line ./gradlew -p subprojects build ${GRADLE_ARGS}; where $GRADLE_ARGS are simple Gradle properties

To Reproduce Because it didn’t reproduce at my local machine I tried to collect more info about GC by adding flags to JVM org.gradle.jvmargs=-Xmx2g -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:HeapDumpPath=./outputs/dumps -XX:+UseGCOverheadLimit -XX:GCTimeLimit=10 -Dfile.encoding=UTF-8 I saw effect only for -XX:GCTimeLimit=10 before adding that flag I’ve been waiting for 25 minutes while GC killing my build. Now it’s only 8 minutes

Expected behavior ./gradlew -p subprojects build ${GRADLE_ARGS}; finishes with success

Additional context I add a file with logs. But I think they are useless. Maybe you have any idea how to collect more data for investigation? github.com_avito-android_Github_PullRequest_checks_1625.log

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 18 (18 by maintainers)

Most upvoted comments

Thank you for the feedback. Nice to see some data.

I had a bit of inspiration today and found a way to cut out one of the analysis passes, which should – based on your profile – cut ~16% of the allocations. It should also use the CPU slightly less.

~I strongly suspect that I can completely collapse the duplicate analysis between 1 and 2, which would cut about 16% of memory usage in the build you sent me the profile for. So I’ll give that a try and let’s see what happens!~

I was mistaken. It’s actually an entirely different visitor for 1 and 2. Collapsing them into a single one would be challenging. No eta on this time.

I’m going to leave some notes in this comment, editing as I learn more.

For the first item, ArtifactToComponentTransformer.analyzeJar() already uses a caching strategy to prevent re-analyzing the same jar more than once. It’s a bit naive and jars may be analyzed more than once, but the count will not greatly exceed 1.

For the second item, JvmConstantMemberFinder.find() uses an identical caching strategy to the first. Important: it looks like this analysis uses the same ASM ClassVisitor as the first. So I may be reading the same jars twice, but extracting different information from them on each reading. It may be possible to collapse this into one read event per jar, reducing memory usage and processing time.

And not surprisingly, same for the third (although this case uses a different ClassVisitor than the first two).

So, the question becomes, is it possible to improve the caching strategy to reduce memory usage?

nb: all of this is the result of reading in jars and doing bytecode analysis on them.

I finally did all info described in gist I want to make some conclusions but can’t I’m new to async-profiler and it outputs and could read results If you need additional info I will gather it with pleasure