Java ‘hello world’ compiled into a static binary is just a couple of MB.
For a modern CLI app it's basically that easy. For an older or GUI WPF app it's not. It used to be fashionable in .NET to use lots of runtime reflection, dynamic runtime configuration based on config files, dynamic loading, and runtime code generation. I suppose the language was less featureful back then and people looked longingly at Enterprise Java for some reason. This type of dynamism doesn't work with AOT compilation because the compiler needs to be able to statically determine what code is required. The solution is to remove the dynamism, replace it with compile-time code generation, or annotate the reflection to tell the compiler which types will actually be used. As of .NET 6 most of the standard library has been annotated or otherwise made compatible with trimming[0]. WPF apps will probably never be able to use trimming because that's a huge amount of code and MS has like one intern working on it.
I believe the situation with Java is basically the same? You can't just build a Java 6 Spring app into a small AOT bundle, right?
[0]: https://themesof.net/roadmap?product=.NET&release=6.0&q=trim