[HN Gopher] JVM Anatomy Quarks
       ___________________________________________________________________
        
       JVM Anatomy Quarks
        
       Author : lichtenberger
       Score  : 130 points
       Date   : 2024-11-10 16:09 UTC (6 hours ago)
        
 (HTM) web link (shipilev.net)
 (TXT) w3m dump (shipilev.net)
        
       | quotemstr wrote:
       | https://shipilev.net/jvm/anatomy-quarks/17-trust-nonstatic-f...
       | is a damned shame. User code misses out on an important
       | optimization available only to system-provided classes because
       | certain frameworks have abused JNI and reflection to mutate final
       | fields, which by all rights should be immutable.
       | 
       | Platforms, especially compilers and runtimes, need to be
       | absolutely strict in _enforcing_ semantic restrictions so as to
       | preserve optimization opportunities for the future.
        
         | J-Kuhn wrote:
         | Yes, but you would be surprised how many people want to change
         | static final fields for various reasons - be it testing, or
         | other things.
         | 
         | When telling those that it doesn't work, and that it can not
         | work without violating the semantics of the JVM, they will wave
         | their hand and say "look, it does work here". And it looks
         | like, yes, if the stars align in that specific constellation,
         | it may work.
        
           | hinkley wrote:
           | Also a part of why Singletons are the black sheep of the
           | Patterns family. They're nasty during bootstrapping and hell
           | during functional testing.
        
         | bobnamob wrote:
         | I'll be the first to admit that I've written the evil three
         | liner to "un-final", mutate, re-final a member off in some long
         | forgotten internal library to dodge a gnarly refactor.
         | 
         | I do wish that I couldn't have done so, shrug, business needs
        
         | blibble wrote:
         | the System.out thing is Java itself
         | 
         | https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.htm...
         | 
         | given this it's not surprising others thought it was acceptable
         | also
        
         | deepsun wrote:
         | I wonder if it thanks to some people blindly following
         | Effective Java book that made a sin by saying "final all the
         | things". So now we cannot easily mock final classes in tests.
         | And mocking tools have to resort to bytecode manipulation to
         | mock the final classes.
         | 
         | E.g. Effective Java is a requirement inside Google, so even
         | public GDrive APIs have final classes. External APIs is exactly
         | the thing you'd want to mock.
        
           | lupire wrote:
           | If you are rigorous as to make classes final, you should also
           | be rigorous to never provide a non-interface as an
           | Application Programming _Interface_.
           | 
           | Google uses mocks and fakes implementations of interfaces,
           | and provides dependency injection frameworks for managing
           | these (Guice and Dagger).
        
             | stickfigure wrote:
             | I once worked with a guy who obsessively made interfaces
             | for every java class. Even domain objects. He was extremely
             | proud of this.
             | 
             | It was garbage.
        
           | adamnew123456 wrote:
           | I would say less overuse of final, more underuse of
           | interfaces. If everything takes/returns/stores values by
           | interfaces (excluding data containers with no behavior) then
           | you don't need to "jailbreak" any class to mock it.
           | 
           | Of course you get code bloat defining interfaces for
           | everything you intend to implement once, and you have to
           | enforce these rules, but this is something that could be made
           | easier. Not in Java, but imagine a language where:
           | 
           | - Concrete classes can only be used in new, or in some
           | platform provided DI container.
           | 
           | - Methods can only accept interface types and return
           | interface types.
           | 
           | - Fields are private only, all public/protected is via
           | properties (or getters/setters, it just has to be declarable
           | in an interface)
           | 
           | - You have a ".interface" syntax (akin to ".class" but for
           | types) that refers to the public members of a class without
           | tying you to the concrete class itself. You can use this as a
           | shorthand instead of declaring separate interfaces for
           | everything.
           | 
           | Eg.
           | 
           | ```
           | 
           | final class GDrive { ... }
           | 
           | public Download file(GDrive.interface drive) { ... }
           | 
           | class MockDrive implements GDrive.interface { ... }
           | 
           | ```
           | 
           | The closest I can think of is a hypothetical typed variant of
           | NewSpeak, but maybe something like this exists already?
        
             | lupire wrote:
             | You just invented Java AutoValue and Kotlin Data Classes.
             | 
             | https://github.com/google/auto/blob/main/value/userguide/in
             | d...
        
             | cyberax wrote:
             | > I would say less overuse of final, more underuse of
             | interfaces.
             | 
             | Interfaces with one implementation are terrible. They just
             | clutter everything and make the code navigation a pain, so
             | it's good that people are avoiding them.
             | 
             | Perhaps a special "test-only" mode that allows to patch
             | finals is a better idea.
        
           | PaulHoule wrote:
           | The trouble is final is not final in Java, private is not
           | private. I mean you can lock it down but you usually won't.
           | With reflective access, breaking the rules is one method call
           | away. Common Java libraries such as meta factories (Spring,
           | Guice), serializers (Jackson, GSON) and test frameworks
           | regularly cheated in the JDK 8 era and a lot of us are
           | running with protections for the stdlib turned off.
           | 
           | Normally a JDK 21 would let you get private/final reflective
           | access to your own "module" but not to the stdlib modules but
           | so many libraries want private access to stdlib objects such
           | as all the various date and time objects.
        
           | mrkeen wrote:
           | > So now we cannot easily mock final classes in tests
           | 
           | > And mocking tools have to resort to bytecode manipulation
           | to mock the final classes
           | 
           | Well which is it? Presumably you use said mocking tool
           | anyway, so it's not your effort that's being expended.
           | 
           | "Final all the things" really doesn't go far enough. There is
           | little point substituting a mutable hashmap for a "final"
           | mutable hashmap, when the actual solution is for the standard
           | library to ship proper immutable collection classes.
           | 
           | In any case, I prefer to avoid mockito anyway, so it's a non-
           | issue for me. Just do plain ol' dependency injection by
           | passing in dependencies into constructors.
        
             | hinkley wrote:
             | As I mentioned in another reply, Josh Bloch experimented
             | with a different type structure for the Collections API
             | that could have yielded read only collections but he
             | thought it was too confusing and went back to... this.
             | 
             | And I've never forgiven him for it
        
           | hinkley wrote:
           | I do not like Bloch, and see him as the architect of some of
           | Java's woes. Generics didn't have to be so stupid. Gilad
           | Bracha (who did a lot of the work on generics) quit the
           | moment they were done to go try something very different -
           | gradual typing. I hope he's keeping an eye on what Elixir is
           | trying, because Set Theoretic Typing has the potential to be
           | big, and it can be applied gradually.
           | 
           | I can no longer recall exactly what Bloch said, I may have to
           | search through some of by old writing to find it, but at one
           | point he admitted he didn't really understand type theory
           | when he designed the collections API. And while I appreciate
           | the honesty, and the reason (he was trying to illustrate that
           | this stuff is still too hard if "even he" didn't get it), I
           | think it paints him rather worse.
           | 
           | But I already knew that about him from working with that code
           | for years and understanding LSP, which he clearly did not.
           | 
           | I don't know _why_ they thought he should be the one writing
           | about how to use Java effectively when he was materially
           | responsible for it being harder to use, but I'm not going to
           | give him any money to reward him. And there are other places
           | to get the same education. "Refactoring" should be a
           | cornerstone of every education, for much the same reason
           | learning to fall without hurting yourself is the first thing
           | some martial arts teach you. Learn to clean up before you
           | learn to make messes.
           | 
           | He said at one point that he had thought of a different way
           | to decompose the interfaces for collections that had less
           | need for variance, with read and write separated, but he
           | thought there were too many interfaces and they would confuse
           | people. But when I tried the same experiment (I spent years
           | thinking about writing my own language)... the thing is when
           | you're only consuming a collection, a lot of the types have
           | the same semantics, so they don't need separate read
           | interfaces, and the variance declarations are much simpler.
           | It's only when you manipulate them that you run into trouble
           | with Liskov, and things that are structurally similar have
           | different contracts. The difference in type count to achieve
           | parity with Collections was maybe 20% more, not double. So to
           | this day I don't know what he's talking about.
           | 
           | Most APIs should only consume collections from callers, so
           | friction against mutation in your interface is actually a
           | good thing.
        
             | roadbuster wrote:
             | > the thing is when you're only consuming a collection,
             | > a lot of the types have the same semantics, so they
             | > don't need separate read interfaces                   >
             | Most APIs should only consume collections from callers
             | 
             | I'm having trouble understanding what you mean by
             | "consuming a collection." Can you expand?
        
               | thfuran wrote:
               | Read-only use of an externally provided collection,
               | presumably.
        
         | rusk wrote:
         | Field modifiers are a semantic constraint not a security
         | constraint. It is right and proper that you should be able to
         | bypass them with the appropriate backflips.
         | 
         | The main issue is safety cause you might modify something that
         | isn't modifiable and cause a SEGV and that is precisely the
         | concern access modifiers are meant to address.
        
           | blibble wrote:
           | they certainly were a security constraint back in the day
           | before Java gave up on trying to use the type system for
           | security
           | 
           | e.g. SecurityManager for applets will not let you
           | setAccessible(true) on private fields of system classes
        
         | elric wrote:
         | IIRC illegal access can be locked down and be controlled in a
         | fine grained manner with the add-opens and illegal-access flags
         | on newer JVMs.
        
         | pron wrote:
         | As part of our "integrity by default" strategy [1] we're
         | changing that. There will be a JEP about it soon.
         | 
         | The idea is that because not much code actually needs to mutate
         | finals (and even if it does, that operation is already limited
         | today to classes in the code's own modules or ones explicitly
         | "open" to it), the application will need to grant a permission
         | to a module that wants to mutate finals, similar to how we've
         | recently done things with native calls and unsafe memory
         | access.
         | 
         | [1]: https://openjdk.org/jeps/8305968
        
       | exabrial wrote:
       | I love the "size" of these posts. Kinda neat to just read through
       | one in a few mins and maybe run the bench locally.
        
       | azinman2 wrote:
       | I've basically forgotten about Java. It would never occur to me
       | to start a new project in it. Am I the only one? It feels like
       | I'd reach for python if I want fast development and flexibility,
       | Go if I want to handle a bunch of I/O concurrency in a garbage
       | collected way, Swift if I want a nice language that's compiled
       | and balanced, or Rust if I want performance and safety in a
       | compiled language. Those are just my personal leanings. I know
       | kotlin has made Java more ergonomic, and yet....
        
         | mpenet wrote:
         | There are plenty of very ergonomic languages on the JVM (for
         | instance clojure).
         | 
         | I wouldn't dismiss the JVM as a whole, it is a marvel of
         | engineering and is evolving quickly nowadays (see loom, panama,
         | leyden, etc...).
        
         | pianoben wrote:
         | I think it's not _just_ you, but certainly not everyone. Kotlin
         | with Java 21+ is my go-to choice for an I /O-bound service, or
         | really _any_ service. It 's just so ergonomic, and with virtual
         | threads the code can be as simple and efficient as Go - while
         | also taking advantage of possibly the best and largest library
         | ecosystem in the world.
         | 
         | I'm not knocking Go or Python - if those are your preferred
         | tools, they're more than adequate. Java, however, isn't nearly
         | as irrelevant as you may perceive.
        
         | izacus wrote:
         | For posters like you I always wonder: why are you posting? Why
         | did you even click on the article if you dislike Java so much?
         | 
         | Is this posturing? Do you feel cool? Why did you come here and
         | bloviate over something as silly as a language choice?
        
           | azinman2 wrote:
           | Are you the reason I got downvoted? I was very surprised by
           | that.
           | 
           | I've spent years writing Java and later Scala, in academia
           | and later production. I've always followed to see how the JVM
           | and the language/ecosystem has progressed. And now I don't
           | use it at all. Is it really that odd to take a temperature on
           | a site filled with other tech folks? I don't understand why
           | you took it so negatively and use words like bloviate, or
           | attack me as just posturing to look cool (how does one look
           | cool on a geeky Internet forum?). One of the HN tenants is to
           | "converse curiously," which is exactly my mindset when I
           | wrote my comment. And if you look at the other replies, it
           | seems others took it that way as well with healthy
           | discussion.
        
         | cyberax wrote:
         | > Am I the only one?
         | 
         | Probably not. Java had stagnated for quite a while, entirely
         | missing the lightweight threading and/or async/await revolution
         | of the last decade. The JVM ergonomics also just sucks, a lot
         | of apps _still_ have to use -Xmx switches to allocate the RAM,
         | as if we're still using a freaking Macintosh System 6!
         | 
         | On the other hand, it's a very mature ecosystem with plenty of
         | established battle-tested libraries.
        
           | azinman2 wrote:
           | Omg I forgot about that. So there's no way to say just grab
           | the memory you need and that's that?
        
             | NovaX wrote:
             | That's in the works, where it adapts from 16mb to terabyte
             | heaps. The current GCs have a max, with lazy allocation and
             | ability to release back to the system periodically, but are
             | not as system aware.
             | 
             | 1. https://openjdk.org/jeps/8329758
             | 
             | 2. https://m.youtube.com/watch?v=wcENUyuzMNM&embeds_referri
             | ng_e...
        
         | dcminter wrote:
         | You're not the only one I'm sure, but sounds like you don't
         | need it. Its major strengths are:
         | 
         | * Bottomless resource of developers with Java experience
         | 
         | * Vast array of existing libraries many of which are enterprise
         | focused
         | 
         | * Managing very large codebases with many contributors is
         | straightforward
         | 
         | * Standard VM that's very solid (decades of development),
         | reasonably fast, and supported on essentially all platforms.
         | 
         | It doesn't have quite the stranglehold (even in Enterprise)
         | that it had in perhaps the early 2000s and it's the
         | archetypical "blub" language, but it's a perfectly reasonable
         | language to choose if you're expecting Enterprise scale and
         | pure performance is less valuable to you than scaling out with
         | large numbers of developers.
         | 
         | I like Rust, but it's Java that puts bread on my table.
        
         | ivan_gammel wrote:
         | In addition to other answers: Java absolutely does have the
         | qualities necessary for startups, so using it for new projects
         | makes sense.
         | 
         | 1. Modern frameworks and AI assistance can help ramping up a
         | decent backend in days. Solo tech co-founder needs to know only
         | Java or Kotlin and some frontend stack to build MVP quickly and
         | will spend such amount of time on non-coding tasks where
         | language features will be irrelevant. Swift can be the second
         | language if you go mobile-native.
         | 
         | 2. Scaling isn't the problem you are going to have for quite a
         | while. It is quite likely, that problem of scaling the team
         | will come first and any performance bottlenecks will be
         | noticeable much later. Java is good for large teams.
         | 
         | That said, from business perspective, if you want larger talent
         | pool, fast delivery cycles and something that may remain as
         | your core stack in the long term - Java or Kotlin is probably
         | the best choice. If you want fancy tech as a perk to attract
         | certain cohort of developers or you have that rare business
         | case, you can choose Go or Rust. Python is popular in academia
         | and bootcamps, but TBH I struggle to see the business value of
         | it for generic backends.
        
       | lsuresh wrote:
       | Happy to see this gem shared here. I've learnt a lot about the
       | JVM going through these.
       | 
       | This article about the "stack allocation" misnomer in Java in
       | particular is one of my favorites:
       | https://shipilev.net/jvm/anatomy-quarks/18-scalar-replacemen....
       | What the JVM really does is escape analysis + scalar replacement.
        
       | lukeh wrote:
       | Tangential: Apple has a new Swift Java bridge which is pretty
       | cool, supporting both JNI and Panama. I've been porting it to
       | Android this past week.
       | 
       | https://github.com/swiftlang/swift-java
        
       ___________________________________________________________________
       (page generated 2024-11-10 23:00 UTC)