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

Plugin uses a lot of memory in large multi module build #322

Closed
Thrillpool opened this issue Apr 26, 2022 · 16 comments
Closed

Plugin uses a lot of memory in large multi module build #322

Thrillpool opened this issue Apr 26, 2022 · 16 comments

Comments

@Thrillpool
Copy link

We have a project with about 400 modules. To build the project requires us to set org.gradle.jvmargs=-Xmx8g.

To me this was very surprising, so I did some digging and found that this plugin is responsible for about 6gb of the memory usage uncollectable. Actually If we have mavenExclusions enabled it goes up to more like 12gb, I disabled this a while ago.

It seems like this change is the main offender #225 as there is seemingly a cache per module, and furthermore, the cache for a module isn't freed when gradle is done with that module. Presumably because another module might want to use that modules cache?

Reverting to 1.0.6.RELEASE I saw a good improvement in memory usage, it still uses quite a lot of memory, more than anything else. But a significant improvement.

I wonder if anything can be done about this? Perhaps making it possible to opt out of the cache?

@jeantil
Copy link

jeantil commented May 12, 2022

Hello,

I discovered this issue while diagnosing an enormous heap usage in our ~380 modules build which mostly manifests during intellij sync or running the DependencyReportTask on all our subprojects (I was trying to determine if the issue came from IJ's plugin, gradle or one of our plugins).

The heap dump reported loads of io.spring.gradle.dependencymanagement.internal objects with high retained space (the screenshot only shows the first page but look at the scroll indicator, I scrolled down and 90% of it is more of the io.spring.gradle.dependencymanagement.internal objects)
image

@wilkinsona
Copy link
Contributor

@Thrillpool @jeantil It would be interesting to know how much, if any, effect disabling support for Maven-style exclusion semantics has on the memory usage that you're seeing.

@jeantil
Copy link

jeantil commented May 12, 2022

I am running the following tests on our mono repo right now:

  • using a gradle daemon with Xmx4g

  • using a task called allDep that runs the dependency report task on all submodules

     tasks.register<DependencyReportTask>("allDeps")
    
  • starting from a warmed up daemon each time (I don't use the no-daemon flag to preserve the process to capture the memory and cpu profile but I kill the daemon after capturing the graphs)
    with both 1.0.6 and 1.0.11, try to complete the task with and withou maven exclusions

  • 1.0.6 without exclusions completes in 1m51s
    image

  • 1.0.6 with exclusions failed with OOM (first screenshot at about 5 min since i felt the initial ramp up would be lost because of gc overhead, second screenshot is when I saw the first OOM notice in the console)
    image
    image

  • 1.0.11 without exclusions completes in 1min51s
    image

  • 1.0.11 with exclusions failed with OOM (first screenshot at 5min, second on the first OOM notice in the
    image
    image

  • 1.0.11 with exclusions + 8g heap completed in 3min27
    image

From these runs, it looks like enabling maven exclusions doubles the consumed heap ... we will investigate if disabling the exclusions still leads to a valid build for us but it sounds like the feature is excessively memory hungry.

@jeantil
Copy link

jeantil commented May 17, 2022

Disabling maven exclusions has not led to any obvious errors for now, unfortunately fully validating for our 340 modules is going to take a while.

@Thrillpool
Copy link
Author

Thrillpool commented May 17, 2022

@wilkinsona I actually already have mavenExclusions disabled. Without that noone can build the repo.

@jeantil I didn't do too much validation when I disabled it. Practically speaking I think all that can go wrong is that going forward you use a formerly excluded module transitively, and then consumers of your project who might be applying mavenExclusions would exclude the module and so get runtime issues.

For the time being I have plans to swap over most of the modules from using this plugin to just using the enforcedPlatform gradle provides direct, and leave some key modules that really do want this plugin as having it. The generated poms are identical, so upstream consumers should have no problem.

@wilkinsona
Copy link
Contributor

@Thrillpool That's surprising as the vast majority of Maven model building performed by this plugin is in support of applying Maven-style exclusions. Are you importing a significant number of different Maven boms or boms with a large inheritance hierarchy?

@spring-projects-issues
Copy link

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

@Thrillpool
Copy link
Author

Hey sorry for my delay in responding. I wouldn't say I was importing a significant number of boms. We basically import a bom we define, which is one level deep, the spring-boot-dependencies bom and some other cloud spring bom. These spring boms are admittedly quite nested.

I've since switched over our build to use the gradle platform, which wasn't entirely trivial because the details differ a little (e.g. how multiple boms defining a version are handled). And have been happy with the improvement.

@wilkinsona
Copy link
Contributor

Thanks, @Thrillpool.

I wouldn't say I was importing a significant number of boms. We basically import a bom we define, which is one level deep, the spring-boot-dependencies bom and some other cloud spring bom. These spring boms are admittedly quite nested.

Unfortunately, that leaves me at a loss to explain the memory usage that you were seeing.

I've since switched over our build to use the gradle platform

Unless you need this plugin's support for overriding the version properties in a bom, using Gradle's built-in platform support is a good choice in my opinion.

@wilkinsona
Copy link
Contributor

wilkinsona commented Jun 1, 2022

I have experimented with limiting the cache to 25 entries. The good news is that it has minimal effect on the performance of the build that was supplied with #225. The bad news is that I don't know if this will address the problem reported here as the exact cause isn't 100% clear to me, particularly as memory usage is apparently very high even with Maven exclusions disabled.

@Thrillpool @jeantil Can either of you please provide a Gradle build that simulates the behaviour that you've described?

@jeantil
Copy link

jeantil commented Jun 3, 2022

hello @wilkinsona sorry for the delay, I haven't been able to create a shareable build that replicates the behaviour (I tried in a minimal build but the issue is probably compounded by the project size and I don't really have enough time to create a fake 340 modules build :) ) Unfortunately our main build is not open source.
However if you are able to provide a jar or to tell me how to reproduce your experiment, I'll inject it in our build and report back on the relevant scenarii

@wilkinsona
Copy link
Contributor

wilkinsona commented Jun 7, 2022

Thanks, @jeantil. I think have recreated the problem with a little bit of code that generates a 400 module build in which each module imports Spring Boot's spring-boot-dependencies bom and has Maven-style exclusions disabled. With 1.0.11.RELEASE and a 1G heap, the build fails after 9m 55s and a lot of GC thrash. It succeeds with a 1.5G heap in 37s and with a 2G heap in 29s. With the changes that I'll push shortly, the build succeeds with a 0.75G heap in 28s.

@wilkinsona
Copy link
Contributor

The changes are now available in 1.0.12.BUILD-SNAPSHOT that can be resolved from https://repo.spring.io/plugins-snapshot-local. Please give the snapshot a try and let me know how you get on.

@jeantil
Copy link

jeantil commented Jun 8, 2022

I repeated my experiment with 1.0.12.BUILD-SNAPSHOT this morning.

  • 1.0.12.BUILD-snapshot without maven exclusions completes in 1min33s (Xmx4g, max heap usage 2.5g)

  • 1.0.12.BUILD-snapshot with maven exclusions completes in 2m51s (Xmx4g, max heap usage 2.6g )

so on 1.0.12.BUILD-SNAPSHOT, as before, maven exclusions make the build slower but the heap usage compared to previous versions is much much better.

Thanks a lot for the patch !

@wilkinsona
Copy link
Contributor

Thanks for giving it a try, @jeantil.

@Thrillpool
Copy link
Author

Hey sorry I have been busy. I tried our repo (pre my platform/enforcedPlatform changes) with the patch. It works great, there is now little difference in memory usage between the plugin and without.

Thanks! This will be helpful for other parts of our codebase where I haven't removed this plugin.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants