This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-02-17
Channels
- # announcements (3)
- # babashka (41)
- # beginners (118)
- # calva (4)
- # cider (22)
- # clj-kondo (4)
- # clj-on-windows (1)
- # clj-together (1)
- # clojure (164)
- # clojure-europe (46)
- # clojure-filipino (1)
- # clojure-indonesia (1)
- # clojure-my (1)
- # clojure-nl (3)
- # clojure-sg (1)
- # clojure-spec (13)
- # clojure-uk (16)
- # clojurescript (18)
- # cloverage (3)
- # conjure (5)
- # core-async (8)
- # cursive (21)
- # datomic (4)
- # deps-new (15)
- # emacs (12)
- # expound (4)
- # fulcro (45)
- # graalvm (32)
- # jobs (1)
- # malli (5)
- # nextjournal (63)
- # off-topic (27)
- # other-languages (3)
- # pathom (27)
- # proletarian (1)
- # rdf (24)
- # re-frame (10)
- # reagent (9)
- # releases (2)
- # shadow-cljs (72)
- # spacemacs (4)
- # timbre (4)
- # tools-deps (29)
- # xtdb (4)
So I'm trying to stop using --initialize-at-build-time
without arguments, but the number of classes I have to list to get things to compile is a bit silly (even with graal-build-time
). The major reason for this seems to be that sometimes type hints (especially for function return values?) seem to cause a ton of classes to be initialized. For instance, this seemingly innocent type hint forces me to list over a dozen classes: https://github.com/toyokumo/tarayo/blob/master/src/tarayo/mail/mime/multipart.clj#L13
Why do some type hints cause this problem while others seem to be fine? Is there a "best practice" for what to type-hint and what not to type-hint, to make native-image happy? And finally, is there a way to build libraries like this one without having to list dozens of classes under --initialize-at-build-time
?
Also, how can I use a library to generate data at build time without including that library in the native-image output?
@wombawomba You can just use a java package prefix in --initialize-at-build-time
I'm surprised to see type hints cause classes to be initialized at build time. Are you sure that is causing it?
It's usually stuff like this that causes the problems: https://github.com/toyokumo/tarayo/blob/f9b10b85b7bc1a188d808c3955e258916cd0b38a/src/tarayo/mail/mime/multipart/body.clj#L18 Classes that are initialized at the top level. This can be mitigated by using a delay.
But if you are sure it doesn't hurt to initialize at built time, just include the package prefix like --initialize-at-build-time=jakarta
You can even do --initialize-at-build-time=.
but this is kind of opposing what the GraalVM authors want you to do
Yeah, I've run --trace-class-initialization
to confirm that the initialization stems from the type hints.
I think jakarta might be fine to initialize fully at build time, but for some of the libs that I'm using I can't initialize do that (e.g. because they initialize a static SecureRandom instance somewhere), so it seems I'm stuck with manually listing classes
For cases where both build-time and run-time initialization work, I'm a bit unsure which to pick. What are the pros/cons of each? (I'm not exporting anything.)
GraalVM tries to encourage to initialize at run time since that is the same semantic you get in the JVM. But with Clojure this is not possible since a lot of stuff happens in static initializers: e.g. resolving things on the classpath. And this stuff cannot happen once the image is already built. Hence we are living in the situation that all Clojure-produced classes must be initialized at build time and also the classes they transitively initialize at build time.
Right, yeah, I'm aware of that. I'm curious though how initializing Java packages at build or run time will affect my build size/startup time/performance.
..or perhaps it doesn't really affect those things?
it could incur a tiny bit of startup time when initializing at run time, but other than that, no difference I think
Alright yeah, I ran some experiments and it seems like it doesn't really matter.
Any pointers on how to reduce native-image build size?
@wombawomba: • use the least amount of dependencies possible • don't use dynamic requires/requiring-resolve/resolve: only use those at the top level
• Don't use clojure.pprint (babashka contains a patch for it which reduces native image significantly)
@borkdude thanks, that helped 🙂
it seems like graalvm 22 and static linking with musl helped a bit too
I was able to shave off ~200 mb from the final image
now I just need to figure out why the image heap size is ~130 mb when I'm only packing 20 mb of resources...
phew, okay, https://upx.github.io/ helped me reduce the size by another 75%
note that upx will reduce size, but will also add to startup time - may or may not matter
if you are using docker, then that already uses compression so you might not need upx in that case
yeah there's a slight increase in startup time, but it isn't a problem
@wombawomba Great progress. What are you making , if you want to share?