[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)