spring-attic / spring-native

Spring Native is now superseded by Spring Boot 3 official native support
https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html
Apache License 2.0
2.74k stars 355 forks source link

"code too large" error happens if the application has too many Spring beans #1659

Closed mrchengxin closed 2 years ago

mrchengxin commented 2 years ago

Background: I'm trying to use Spring Native for a big Java application, which has more than 2,000 Spring beans. When I try to build a native image, the "code too large" error happened.

Exception log:

[ERROR] COMPILATION ERROR : 
[INFO] -------------------------------------------------------------
[ERROR] /demo/target/generated-runtime-sources/spring-aot/src/main/java/org/springframework/aot/ContextBootstrapInitializer.java:[107,15] code too large
[INFO] 1 error
[INFO] -------------------------------------------------------------
[ERROR] 
org.apache.maven.plugin.MojoExecutionException: Unable to execute mojo
    at org.twdata.maven.mojoexecutor.MojoExecutor.executeMojo (MojoExecutor.java:122)
    at org.springframework.aot.maven.GenerateMojo.compileGeneratedSources (GenerateMojo.java:153)
    at org.springframework.aot.maven.GenerateMojo.execute (GenerateMojo.java:134)
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:137)
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute (MojoExecutor.java:301)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:211)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:165)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:157)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:121)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:81)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:56)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:127)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:294)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:192)
    at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:105)
    at org.apache.maven.cli.MavenCli.execute (MavenCli.java:960)
    at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:293)
    at org.apache.maven.cli.MavenCli.main (MavenCli.java:196)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:566)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:282)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:225)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:406)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:347)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:566)
    at org.apache.maven.wrapper.BootstrapMainStarter.start (BootstrapMainStarter.java:47)
    at org.apache.maven.wrapper.WrapperExecutor.execute (WrapperExecutor.java:156)
    at org.apache.maven.wrapper.MavenWrapperMain.main (MavenWrapperMain.java:72)
Caused by: org.apache.maven.plugin.compiler.CompilationFailureException: Compilation failure
/demo/target/generated-runtime-sources/spring-aot/src/main/java/org/springframework/aot/ContextBootstrapInitializer.java:[107,15] code too large

    at org.apache.maven.plugin.compiler.AbstractCompilerMojo.execute (AbstractCompilerMojo.java:1310)
    at org.apache.maven.plugin.compiler.CompilerMojo.execute (CompilerMojo.java:198)
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:137)
    at org.twdata.maven.mojoexecutor.MojoExecutor.executeMojo (MojoExecutor.java:120)
    at org.springframework.aot.maven.GenerateMojo.compileGeneratedSources (GenerateMojo.java:153)
    at org.springframework.aot.maven.GenerateMojo.execute (GenerateMojo.java:134)
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:137)
    at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute (MojoExecutor.java:301)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:211)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:165)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:157)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:121)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:81)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:56)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:127)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:294)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:192)
    at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:105)
    at org.apache.maven.cli.MavenCli.execute (MavenCli.java:960)
    at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:293)
    at org.apache.maven.cli.MavenCli.main (MavenCli.java:196)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:566)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:282)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:225)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:406)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:347)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:566)
    at org.apache.maven.wrapper.BootstrapMainStarter.start (BootstrapMainStarter.java:47)
    at org.apache.maven.wrapper.WrapperExecutor.execute (WrapperExecutor.java:156)
    at org.apache.maven.wrapper.MavenWrapperMain.main (MavenWrapperMain.java:72)
[ERROR] [org.twdata.maven.mojoexecutor.MojoExecutor.executeMojo(MojoExecutor.java:122), org.springframework.aot.maven.GenerateMojo.compileGeneratedSources(GenerateMojo.java:153), org.springframework.aot.maven.GenerateMojo.execute(GenerateMojo.java:134), org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:137), org.apache.maven.lifecycle.internal.MojoExecutor.doExecute(MojoExecutor.java:301), org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:211), org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:165), org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:157), org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:121), org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:81), org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:56), org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:127), org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:294), org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:192), org.apache.maven.DefaultMaven.execute(DefaultMaven.java:105), org.apache.maven.cli.MavenCli.execute(MavenCli.java:960), org.apache.maven.cli.MavenCli.doMain(MavenCli.java:293), org.apache.maven.cli.MavenCli.main(MavenCli.java:196), java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method), java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62), java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43), java.base/java.lang.reflect.Method.invoke(Method.java:566), org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:282), org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:225), org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:406), org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:347), java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method), java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62), java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43), java.base/java.lang.reflect.Method.invoke(Method.java:566), org.apache.maven.wrapper.BootstrapMainStarter.start(BootstrapMainStarter.java:47), org.apache.maven.wrapper.WrapperExecutor.execute(WrapperExecutor.java:156), org.apache.maven.wrapper.MavenWrapperMain.main(MavenWrapperMain.java:72)]

Sample project: demo.zip

Environment info: OS: Ubuntu 22.04 LTS JDK: GraalVM 22.1.0.r11 Maven: 3.8.6 Spring Boot: 2.7.0 Spring Native: 0.12.0

How to reproduce: Open the demo project, and execute this command:

./mvnw -Pnative clean package

or

./mvnw -Pagent clean package

Root cause: The generated org.springframework.aot.ContextBootstrapInitializer#initialize(GenericApplicationContext context) is too large.

Tried solutions: I've tried to split the initialize method into small methods by this small fix: https://github.com/spring-projects-experimental/spring-native/compare/main...mrchengxin:spring-native:patch-1

And it works.

Expectations: Could you look into this issue and fix it?


Probably applying Spring Native to such a big project with 2,000+ beans is not a good practice. But the experiment result turned out to be promising, for example, we found that memory consumption is reduced by 90% and the startup time is reduced to seconds compared to minutes. If you're interested in the result, I'm very glad to share it.

sdeleuze commented 2 years ago

Interesting, happy to learn more about the data points yeah, I will have a deeper look to the repro.

snicoll commented 2 years ago

For the record, Spring Framework 6 uses a different approach where generated code is rather co-located with their configuration infrastructure or target type. Unless those 2000 beans are in the same package space, this should fix the issue for you.

@mrchengxin would be interested to give Spring Framework 6 a try? The latest milestone is Spring Boot 3.0.0-M4, but we're testing our smoke tests against the snapshot at the moment.

mrchengxin commented 2 years ago

@snicoll Thank you for involving. It sounds promising. I would like to have a try with Spring Framework 6. Which version of Spring Native I should use along with Spring Boot 3.0.0-M4?

snicoll commented 2 years ago

You don't need Spring Native at all with Spring Boot 3. I can see you are using Maven. Here is some initial doc, it's pretty light at this point. This repository provides a working example.

We're really be interested to get your feedback on your project based on Spring Boot 3.

sdeleuze commented 2 years ago

I am closing this issue as it is expected to be handled by Spring Boot 3.

@mrchengxin If you have an issue with the upgrade please comment here, we will try to help.