[HN Gopher] Jacobin: A more than minimal JVM written in Go
___________________________________________________________________
Jacobin: A more than minimal JVM written in Go
Author : yla92
Score : 258 points
Date : 2023-08-24 10:59 UTC (12 hours ago)
(HTM) web link (jacobin.org)
(TXT) w3m dump (jacobin.org)
| bullen wrote:
| So Go GC + Java GC?
| nonane wrote:
| No. It's using Go's built in GC:
| https://github.com/platypusguy/jacobin#garbage-collection
| karmakaze wrote:
| The Go GC could _be_ the Java GC.
| perbu wrote:
| Then it would be hard to write this in Go. Can't see how
| you'd be able to hook into the Go GC in an application
| written in Go.
| mbreese wrote:
| You couldn't necessarily "hook into" it from the Java side,
| as it would be an opaque interface. But the Go side would
| make the Java GC process "just work".
| nightowl_games wrote:
| From TFA:
|
| > An important factor in reducing the size of the codebase
| and executable is that Jacobin relies on Go's built-in
| memory management to perform garbage collection, and so it
| contains no GC code.
|
| I don't see how you can't see it.
| RetroTechie wrote:
| I can see how using Go's GC reduces the codebase.
|
| But I don't see how that reduces binary size.
|
| Compiled Go programs include a Go runtime in their
| binary, correct? (statically linked).
|
| So if Go program uses Go's builtin GC, that GC wouldn't
| be part of the program code, but _would_ be part of the
| runtime that it 's packed with in the binary, right?
|
| In essence replacing [GC in Go program part of the
| binary] with [GC in Go runtime part of the binary].
|
| I mean, I can see the advantage(s) there. But reducing
| binary size doesn't seem to be one of them. Unless you
| count [Go program using its own GC + the one in Go
| runtime] vs. [ _only_ the one in Go runtime].
|
| It's still a (read: _at least_ 1) GC included in the
| binary.
| skitter wrote:
| You're right, but that's what's meant - using the Go GC
| vs also implementing a custom one.
| karmakaze wrote:
| Every Java reference to an object can be a reference to the
| allocated Go object for that object. The Go objects are the
| Java objects with no extra indirection. The Go object would
| be bytes with each reference field being a struct with a
| pointer. There should be a low level use these bytes for
| 'allocating' this object.
| perbu wrote:
| Thanks. That makes a lot of sense and isn't as
| complicated as I thought it would be.
| perbu wrote:
| From the project page: GC is handled by the golang runtime,
| which has its own GC
|
| This is pretty cool, imo.
| pjmlp wrote:
| Go GC is already written in Go.
| paulddraper wrote:
| It'd be hard _not_ to hook into the Go GC in an application
| written in Go...
| who-shot-jr wrote:
| Inception GC
| lemper wrote:
| only go's gc. there's no gc code, if I read correctly.
| kernal wrote:
| >The goal is to provide a more-than-minimal implementation of the
| JVM that can run most class files and JARs
|
| As with everything, the final 10% is 90% of the work.
| andrewbinstock wrote:
| Lead dev here. It's been an amazing amount of work. I'd change
| the above adage to: Every 10% is 90% of the work!
| steno132 wrote:
| I once said that "just as there's no founder like Elon, there's
| no Java expert like Andrew (project author)"
|
| Andrew's Java skills are at a different level. I only briefly met
| him once, but he used to be a core contributor to Oracle's Java
| magazine. 80% of the "Unix greybeard" type advanced Java
| knowledge I have is due to his articles.
|
| Happy to see he's still building. Thanks Andrew!
| [deleted]
| usrnm wrote:
| So, where is Andrew's x/twitter?
| leetrout wrote:
| https://x.com/platypusguy?s=21&t=emEe0Pw2GM5BS7u2A27-pQ
| agumonkey wrote:
| seems his blogspot is also full of stuff
| http://binstock.blogspot.com/
| [deleted]
| andrewbinstock wrote:
| Thank you for such kind words!
| AtlasBarfed wrote:
| Ok why are you doing this? To see if some end around to
| another language GC is faster?
| mdaniel wrote:
| While they seem to be "git commit && exit", there are sibling
| repos that are interesting, too:
|
| https://github.com/platypusguy/jacobin-dart (A JVM written in
| Dart)
|
| https://github.com/platypusguy/jacobin-swift (Jacobin JVM written
| in Swift)
| andrewbinstock wrote:
| Author here: Yeah, those were first attempts in other
| languages.
| baryphonic wrote:
| I had no idea classes were allowed in a Jacobin system
| paulddraper wrote:
| Good job sir, now please leave
| [deleted]
| joeatwork wrote:
| _slow clap_
| yaqubroli wrote:
| Initially I was very confused; why would a socialist magazine
| have an article about the JVM?
| lambda_garden wrote:
| They use Haskell in any case [0]
|
| [0] https://jacobin.com/2022/05/jacobin-is-looking-for-a-
| program...
| andrewbinstock wrote:
| Author here: Long ago, when I got the domain, the project was
| going to be JAva COmpiler to BINary = Jacobin. And as the
| Jacobins were revolutionaries, it sort of fit the intended
| project. I don't recall whether Jacobin Magazine was in
| publication then or not TBH.
| ninkendo wrote:
| > An important factor in reducing the size of the codebase and
| executable is that Jacobin relies on Go's built-in memory
| management to perform garbage collection, and so it contains no
| GC code.
|
| This breaks my brain thinking about it. A lot of what the JVM
| does is interpreting/JITing bytecode and ensuring it
| links/executes correctly, and writing that logic itself in Go is
| one thing. But how does Go's GC help you garbage collect objects
| in the JVM you're implementing?
|
| For example, you have objects in the JVM heap, tracked by the
| code you're writing in Go. You need to do a GC run. How does the
| Go GC know about the objects you're managing in the JVM? Do you
| just... write wrapper objects in Go around each of them, and code
| up a destructor so that freeing the Go object frees the tracked
| JVM object? How do you inform the Go VM about which pointers
| exist to your JVM objects?
|
| I realize I'm in way out of my depth here, and only have a
| "user"'s understanding of what GC's do, having never implemented
| one myself, but it seems crazy to me that Go's GC can Just Work
| with a VM you're writing in Go itself.
| paulddraper wrote:
| > it seems crazy to me that Go's GC can Just Work with a VM
| you're writing in Go itself.
|
| Far from it, it is more natural to do that than anything else.
|
| Simplified example: type Array struct {
| items *any[] } type Object struct {
| fields map[string]*any }
|
| These are the JVM values, and when the references to them
| disappear, the JVM values they reference can be GC'd as well.
| 38 wrote:
| your example is not valid Go code:
|
| syntax error: unexpected ], expected type argument list
|
| and its also just poor style in general. "any" is already a
| pointer, so you would rarely design a pointer to any.
| example:
|
| https://godocs.io/encoding/json#Marshal
| jsd1982 wrote:
| Since the VM controls allocation of Java objects, just
| implement the VM to allocate the Java objects into Go's heap
| using Go's native allocator thereby allowing the native Go GC
| to clean those up when they become unreferenced.
| ninkendo wrote:
| How would one allocate Java objects using Go's allocator, as
| a program written in Go? Does go provide such primitives?
|
| Naively, something like: // Called when the
| hosted JVM code wants to allocate func
| makeJVMObject() (jObj, error) { var obj =
| new(jObj) // on go's heap // do stuff
| return obj, nil }
|
| would make sense, except how do we keep track of who's
| referencing it? JVM objects have fields which tell the GC how
| to crawl the object tree in the mark phase (and so do Go
| objects), but how do we make the Go GC aware of the fields
| the JVM knows about? A map maybe?
|
| Hmm, I guess a map could work... the jObj struct could have a
| map of fields it knows about, keys being the field name and
| values being where they point to...
|
| Now that I think of it this probably must be how all GC's
| work, they can't rely on static information to know the
| fields of each type they've compiled, it's gotta be something
| like a map somewhere.
|
| I guess I may have answered my own question here.
| paulddraper wrote:
| Yep :)
|
| In fact, I would go so far as to say that it's harder to
| implement those _without_ Go 's GC just working.
|
| https://news.ycombinator.com/edit?id=37254746
| felixge wrote:
| I suspect every JVM heap alloc is implemented by doing an alloc
| in Go. The JVM references to the object are pointers in the Go
| VM. So no special magic is needed. When the Go VM stops
| referencing an object, the Go GC will collect it.
| NovemberWhiskey wrote:
| Java leans much more heavily on its GC than Go does so it
| will be interesting to see whether that's really an approach
| that works.
| cvoss wrote:
| Not too familiar with Go, but my first instinct is how will
| it handle non-vanilla references, of the weak, soft, or
| phantom variety?
| starlevel003 wrote:
| Given how primitive the Go GC is, I doubt it'll work at
| all.
| paulddraper wrote:
| ?
|
| Go has a complete GC. It's not like Go relies on
| reference counting.
|
| The biggest problem is that it's non-moving, so
| fragmentation is an issue. But that's true of many
| languages, e.g. C/C++.
| mananaysiempre wrote:
| A "complete"--i.e. functioning--tracing GC is a weekend
| project. (Mark-sweep, mark-compact, or stop-and-copy,
| take your pick.) Perhaps not as simple as basic
| unoptimized reference counting, but still not hard.
|
| The hard part, the one that has occupied JVM engineers
| for almost three decades now, comes afterwards: when you
| try to make things not freeze when memory is low, or when
| you have multiple threads mutating the same heap, or
| ultimately when you're adapting the GC to the particulars
| of your language. (E.g. Haskell has an awesome concurrent
| GC that'd work like crap for Java, because it assumes
| tons of really short-lived, really small garbage and
| almost no mutation. The other way around is also bound to
| be problematic--I don't know how the Scala people do it.)
|
| So a GC being tracing and not refcounting is not really a
| useful benchmark. And Go's GC is undeniably less advanced
| than OpenJDK's, simply because almost every other GC is.
| It can still suit Go's purposes, but it does mean running
| Java on top of it is bound to yield interesting results.
|
| (And can we please stop pretending C and C++ are in any
| way close as languages? Even if the latter reuses some
| parts from the former's runtime.)
| suresk wrote:
| Can you elaborate more on this?
|
| > E.g. Haskell has an awesome concurrent GC that'd work
| like crap for Java, because it assumes tons of really
| short-lived, really small garbage and almost no mutation.
| The other way around is also bound to be problematic--I
| don't know how the Scala people do it
|
| I don't know a ton about Haskell's GC, but at surface
| level it seems very similar to several of the JVM GC
| implementations - a generational GC with a concept of a
| nursery. Java GC is very heavily designed around the weak
| generational hypothesis (ie, most objects don't live
| long) and very much optimizes for short-lived object
| lifecycles, so most GC implementations have at least a
| few nursery-type areas before anything gets to the main
| heap where GC is incredibly cheap, plus some stuff ends
| up getting allocated on the stack in some cases.
|
| The only big difference is that in Haskell there are
| probably some optimizations you can do if most of your
| structures are immutable since nothing in an older
| generation can refer to something in the nursery. But it
| isn't super clear to me that alone makes a big enough
| difference?
| aardvark179 wrote:
| It will work, bat since it's not a moving GC you may end
| up with a lot of heap fragmentation, and as I don't think
| it is generational it may get into a state where it stops
| collecting or has quite long pause times (can't remember
| if it limits its pause times).
| suresk wrote:
| It is kind of interesting to look at some of the
| differences in GC approach in the JVM vs Go - the
| different goals, different tradeoffs, different
| approaches, etc. Go's is definitely simpler in that there
| is a single implementation, it doesn't have nearly as
| many tuning knobs, and it is focused on one things vs the
| JVM GC implementations that give you a lot of control
| (whether that is good or not..) over tuning knobs and it
| is a pretty explicit goal to support the different GC-
| related use cases (ie, low-latency vs long-running jobs
| where you only care about throughput).
|
| One of the things I really like about Go is that a lot of
| the designs and decisions, along with their rationales,
| are pretty well documented. Here are the GC docs, for
| example - https://go.dev/doc/gc-guide.
|
| For example, Go doesn't move data on the heap around so
| to combat fragmentation, it breaks the heap up into a
| bunch of different arenas based on fixed sizes. So a 900
| byte object goes into the 1K arena, etc. This wastes some
| heap space, but saves the overhead and complexity with
| moving data around.
| dhconnelly wrote:
| Also interesting: https://go.dev/blog/ismmkeynote
| fleventynine wrote:
| It will "work". It won't be as fast and precise as the
| JVM.
| remus wrote:
| I don't think primitive is a good description of the go
| gc. It's definitely got different design constraints (Vs
| the various java gc, for example), particularly around
| the philosophy of minimising knobs, but within those
| constraints it's pretty highly optimised.
| omoikane wrote:
| Does this mean that code running inside Jacobin might be
| vulnerable to memory exhaustion issues[1], whereas in JVM
| they might have gotten an OutOfMemoryError instead because
| JVM heap size is fixed at startup time?
|
| [1] For example https://pkg.go.dev/vuln/GO-2023-1704
| justinclift wrote:
| Interesting idea. That sounds like this could run java
| programs with only the memory they actually need, instead
| of dividing a server up into pieces just because a java
| program "might" some day, possibly, maybe, ever need that
| much.
|
| Which tends to be cargo culted into "use these arguments
| when running java programs", thus a "hello world" responder
| gets allocated 128GB of ram.
| nazgulsenpai wrote:
| "I have spent the last eight months researching the JVM--reading
| the docs and articles and doing exploratory coding in various
| languages with which to write the Jacobin JVM."[0]
|
| I wish I had the discipline to decide I wanted to do create a
| long-term pet project, spend months researching what kind of
| project and which programming language to implement it in, and to
| still be motivated enough to keep actively updating it 2 years
| later. Also, the code comments are educational, and the write-ups
| are inspiring. Great stuff.
|
| [0] from http://binstock.blogspot.com/2021/08/a-whole-new-
| project-jvm... linked from article
| toasted-subs wrote:
| I'd love to make a highly integrated webserver. At this point
| it seems like spending tons of time getting caught up on what
| other people have done, why they have done it. Trying different
| techniques to see what actually seems to work. Where
| abstractions need to be and what provides the nicest transition
| between accessing different portions of stack. At this point
| I'm getting paid to work in tons of languages and integrating
| different stacks.
|
| I wish somebody would unify these things but I'm running out of
| time in the day .
|
| People that are able to commit time to projects like that are
| truly amazing.
| smokel wrote:
| Discipline gets a lot of attention around here.
|
| My suggestion is to find something that piques your interest
| for a long enough time. Thought around the "Flow" concept [1]
| suggests to pick something that is just within reach of your
| capabilities.
|
| And perhaps try meditation to exercise building up discipline.
|
| It also helps to ask yourself several times per day: "What
| would be the smartest thing to do now?", until it becomes a
| habit. You will then accidentally think that phrase, which
| gives some freedom to override other (possibly) bad habits.
|
| [1] https://en.m.wikipedia.org/wiki/Flow_(psychology)
| andai wrote:
| Can anyone comment on the intersection of discipline and
| flow? I've always seen it as, flow is when you're doing
| something you enjoy, and discipline is when you force
| yourself to do something whether you enjoy it or not. But
| I've often found that just putting in enough time (about
| 45min-1hr for me) will trigger a flow state, or "evaporate"
| most resistance I had to a task.
|
| So paradoxically I found that it's easier to work for 2 hours
| than for just 1, since the whole first hour is just getting
| settled in. This seems to go against the pomodoro stuff
| (working in 25 minute increments).
| seabrookmx wrote:
| I think it's important to note these approaches aren't one
| size fits all.
|
| What you're saying rings true for me: it's easier to work
| for 2 hours vs. 1, and the pomodoro method absolutely has
| not worked for me the few times I've tried.
|
| I have friends and coworkers who are the opposite though.
| Their barrier to the "flow state" is much lower than mine
| but they seem to fizzle out faster.
|
| IMO it's all about experimenting with different approaches
| and finding blend one is to your taste.
| agumonkey wrote:
| All of these have one problem, they're fickle. Intrinsic
| motivation, flow.. things most (myself included) seek and
| love, are not guaranteed.
|
| The experience of adjusting difficulty and investment (you
| feel tired, you have emergencies.. do less, but do something,
| have rest days), going slow and steady versus inspiration
| driven is key IMO.
|
| What is hard is accepting the near invisible small steps that
| don't seem to bring you closer to your goal.. that's where
| motivation dies.
| slashdev wrote:
| Motivation is fickle, discipline is about doing the action
| when you're not motivated and don't want to.
|
| Motivation comes and goes. Discipline shows up for work
| every day, rain or shine.
|
| I've worked on long term projects, and it's the discipline
| that lets you power through those tasks you don't want to
| do. It's required to finish anything of substance.
|
| One thing that's really helped me build discipline is the
| gym. It's hard, I usually don't want to go. But even on the
| days I don't feel it, I go and get a workout in, even if
| it's not a great workout. Even if I just go for a walk in
| the park or on a treadmill some days.
|
| Once you do something consistently enough, it becomes
| habit, and develops its own inertia, to the point where it
| can become harder not to do it. I think habit and
| consistency are important in maintaining discipline.
| bowsamic wrote:
| Discipline is just another word for motivation. You are
| motivated to start, and are motivated to keep going. The
| only reason you call it discipline is to give yourself
| the illusion that you can't lose it, but you can
| agumonkey wrote:
| And the experience I was mentioning is well summed up by
| "develops its own inertia". You become fine tuned at
| adjusting activity to keep inertia in some range, avoid
| the zero which would cost you long time to recover that
| inertia, but don't over work yourself. Cause a life of
| overworking is dreadful, the key is finding the sweet
| spot. I even think we're build for that, the right kind
| and amount of stimulation keeps life interesting.
| kaba0 wrote:
| May not be the best advice, but a BSc/MSc thesis (or phd for
| the even more masochists) may give one just the right amount of
| pressure to be able to go through a bigger project. At least it
| did help me.
| [deleted]
| abledon wrote:
| have you tried 'redbull' ?
| ShamelessC wrote:
| Reading the sibling responses so far, my only advice is to not
| take advice from random strangers on the internet about this
| sort of thing. You will just get bad takes tainted by
| survivorship bias.
| [deleted]
| sbdaman wrote:
| Interesting name choice
| crickey wrote:
| So the garbage collected language is written in a garbage
| collected language? How does that effect the system?
| ternaryoperator wrote:
| Lead dev here. A significant portion of the JDK and the JVM is
| written in Java. There is much that happens in the JVM were gc
| is a good thing. Classes are constantly being instantiated and
| then discarded. Having those discarded classes GC'd is a
| benefit indeed.
| gabereiser wrote:
| Ummm, excuse me, but where the f&$k has this been hiding? I've
| been looking for ways to extend my go applications with scripting
| support. I started with Lua (worked ) then Python (worked but
| hacky) then javascript using otto [1]. However it lacks ES6
| support so having pretty OOP js code is a non-starter. I would
| love to have Java as a runtime that can be executed from
| goroutines.
|
| [1] https://github.com/robertkrimen/otto
| hinkley wrote:
| What didn't you like about Lua for your problem domain? It's
| used quite a lot in computer games, and even nginx supports it.
| gabereiser wrote:
| I'm familiar with it from my many years using it in game
| engines (and implementing it in them). My problem is it's
| fluid by nature. The concern of validation and interface
| semantics are dependent on the implementation, rather than as
| an engine driving the process. I could expose some functions
| and write a bunch of docs on which ones to call first, or I
| could switch to a more OO approach. I chose the latter.
|
| If I want to script behavior, Lua is great. Python as well.
| But I'm not scripting behavior - I'm providing an engine.
| rainingmonkey wrote:
| What was your experience with Lua if it worked? What were the
| disadvantages that kept you looking for other extension
| languages?
| gabereiser wrote:
| Advantages, Lua is old and simple so implementing it was a
| breeze. Disadvantage, I kept running into scenarios where oop
| made more sense and extending an interface. I could have done
| that through convention, but it's not for me. I need to
| "enforce" the interface more than just a try catch. That led
| me to Python. Which also worked and suited that need for
| interface contracts but it introduced new problems. How can I
| enforce memory guards and sandboxing and execute parallel
| Python contexts? I ran into road blocks and gave Otto a try
| as it's written in go, I would have more luck making the
| necessary changes if I needed a feature.
| donio wrote:
| Not the parent but there are several high quality native
| (meaning no CGO) Lua implementations for Go and it's a great
| choice if you want an embedded scripting language:
|
| https://github.com/yuin/gopher-lua
|
| https://github.com/Shopify/go-lua
|
| Unless you specifically need a JVM either of these will be a
| much more practical and mature choice for embedded scripting.
|
| Alternatively if you prefer JS then Otto is a good choice:
| https://github.com/robertkrimen/otto
| gabereiser wrote:
| I used Shopify's go-lua. It was the simplest to integrate.
| Otto is what I'm on now but with a "lot" of shims to get
| ES6 classes to work.
| sproketboy wrote:
| [dead]
| evacchi wrote:
| I am a fan of the Jacobin project! For your uses, you may also
| want to consider wazero [1], a pure-go WebAssembly runtime.
| Full disclosure: I am on the team :)
|
| [1]: https://wazero.io/
| gabereiser wrote:
| I'm doing distributed backends, not webassembly, how does
| wazero fit into that scenario?
| evacchi wrote:
| I don't know what kind of use case you have in mind
| specifically, but wherever you would use a scripting
| engine, you can embed a small Wasm runtime instead and let
| your end-users pick their favorite* language
|
| (*) as long as there is a toolchain that supports using it
| on Wasm :P
| sam_on_the_web wrote:
| Have you had a look at Starlark? Its a python-like scripting
| language built specifically for embedding into applications.
| Originally it was written in Java to be used in Bazel but its
| been re-implemented in go and rust and has found use in a bunch
| of other places.
|
| https://github.com/google/starlark-go
| gabereiser wrote:
| Yeah, it's cool but doesn't fit my use case. I'm not
| scripting configuration. I'm scripting functionality. I need
| to be able to enforce interfaces and signatures easily all
| while having near-go-like performance.
|
| I'll play around with it some more and see if I can't get
| some OOP pydantic-like stuff going with it. Otherwise it's a
| non-starter.
| skitter wrote:
| If you want near-go-like performance, Jacobin doesn't help
| you either.
| gabereiser wrote:
| I can handle <10 orders of magnitude performance loss, I
| can't handle 100+ as I get with Lua and other scripting
| engines in go.
| verdverm wrote:
| CUE is another interesting language to use from within Go, and
| is rather natural, given CUE is implemented in Go, but you can
| also do way more cool things with CUE via the Go API.
|
| We're using CUE to validate and transform data, as input to
| code gen, the basis for a DAG task engine, and more
|
| https://cuelang.org |
| https://pkg.go.dev/cuelang.org/go@v0.6.0/cue |
| https://cuetorials.com/go-api (learn about CUE)
|
| https://github.com/hofstadter-io/hof (where we are doing these
| things)
| gabereiser wrote:
| Not remotely close to my use case. Data filtering and
| extraction seems to be its niche, a problem you're solving
| using it. My use case is different.
| verdverm wrote:
| indeed, I've been looking for something more scripty to use
| from Go programs as well. Curious about the Gp+LUA, but
| haven't had the time to try it
|
| Generally, this subthread became several comments about
| using lang X in Go
| rbanffy wrote:
| For a second I wondered why the Jacobin would publish a story
| about a JVM...
| [deleted]
| richieartoul wrote:
| This is so cool. Can't wait to go digging around through the code
| this weekend. Good luck with the project!
| richieartoul wrote:
| I know the link stresses that correctness and code clarity are
| the primary goals of this project, but I'm curious if
| performance is a goal as well? Do you hope that people will run
| "real" workloads with this or use it to embed other software in
| their Go applications?
| andrewbinstock wrote:
| Author here: Right now we're entirely focused on getting
| parity of functionality so that anything that runs on the
| Hotspot JVM runs similarly on Jacobin. There's still a lot of
| work to do to get there. However, once we do get there, we'll
| start working on performance.
| riku_iki wrote:
| what is the reason for this project to be started? What is
| the end goal? Why current JVM is not good enough?..
| andrewbinstock wrote:
| Thanks for asking. I've always thought of the JVM as
| magical technlogy--I'm certainly not alone in that view.
| But in trying to learn more about it, I was greatly
| frustrated by the difficulty of reading the code base.
|
| As you likely know, the Hotspot JVM is open source. But
| reading the code is very difficult, in part because it
| grew organically and in part because of its unusual
| design, in which many actions are buried deep at the end
| of a long series of function calls involving unexpected
| classes and unusual methods, etc.
|
| This led me to thinking there would be value in a JVM
| written as a single cohesive codebase. And given that
| there is a 300+ page JVM specification and a reference
| implementation, I thought to myself, how long could this
| take? Two years later, and with help from two major
| contributors, we're still finding out! ;-)
|
| Eventually, we hope, it will be a fun/interesting
| experience for users to pop open their Go IDE and watch a
| Java program execute--which is why we're intent on making
| sure it's written in 100% go.
|
| In a larger context, Jacobin might eventually be useful
| as an embeddable JVM.
| layer8 wrote:
| Since they emphasize cohesiveness and clear code, the goal
| seems to be more on the educational side. It doesn't look
| like they'd want to implement JIT bytecode compilation.
| meisel wrote:
| Just curious, what's the purpose of this? Are there any
| production use cases it's targeting, or is it just for fun?
| andrewbinstock wrote:
| Lead dev here. I gave a detailed reply earlier. Grep for "I've
| always thought of the JVM as magical technlogy" on this page
| and you'll find it.
| butterisgood wrote:
| I can't help but wonder if this means Java might work on Plan
| 9/9front with this.
|
| But I'm also not sure that's something I'll ever need.
| tombert wrote:
| I would be very curious to side by side performance benchmarks
| between this, GraalVM, and vanilla JDK. My gut tells me (with no
| data to back this us) that the vanilla JVM will inch ahead once
| it's paid the cost of starting up but I would be interested to
| see how wrong I am.
| jsiepkes wrote:
| It looks like a cool research project. But realistically I
| would be surprised if it could beat OpenJDK in any benchmark
| (though I don't think that's the purpose of the project).
|
| Writing something which is compatible with a specification is
| one thing, making it performant is another thing. For example
| it's not that hard to create a webserver from scratch but
| making it performant takes quite some effort.
| tgv wrote:
| I see it more as an niche opportunity to integrate bits of Java
| code in Go applications. The start-up costs can be "paid" when
| the app starts. Performance will be bad, but it might save some
| rewrites (provided they can get things like db connections
| going).
| tombert wrote:
| Yeah that makes some sense; there's probably millions of
| lines of Java code out there where performance doesn't really
| matter, and so aren't worth rewriting. Being able to embed a
| JVM just to run those from Go projects isn't a bad idea.
|
| Tangential, but a long time ago, I wanted to reuse a node.js
| slug library in .NET. I thought about trying to port it at
| first, but then I realized that this actual job didn't need
| to be terribly fast, so I instead embedded the Jurassic JS
| library into my code, and was able to load in the slug
| library that way directly). It wasn't especially fast (but
| actually faster than I thought it would be!), but it was
| certainly fast enough, and I didn't have to worry about not
| having feature parity.
| kaba0 wrote:
| Why not just do traditional IPC, or even a batch process
| starting up a JVM from time to time when needed?
| tgv wrote:
| There are always other solutions. This one might just
| work in some cases. As I wrote: it's (probably) niche.
| paulddraper wrote:
| Exactly.
|
| The cool thing is that since this uses the Go garbage
| collector, you can create a very nice Go-native interface to
| that JVM code.
| andrewbinstock wrote:
| Lead dev here. We've run a couple of benchmarks internally just
| for kicks. To create a fair comparison, you have to run the
| Hotspot JVM with the -Xint flag, which says interpret only.
| Right now our performance is anywhere from 15-25% of the speed
| of Hotspot with -Xint on small benchmarks. We figure that the
| use of go alone creates some important portion of that overhead
| when compared with the C++ of the Hotspot JVM. We're guessing
| that a well-optimized Jacobin interpreter will eventually get
| to 50-60% of the Hotspot's -Xint speed.
|
| But we first want to get feature parity, before pivoting to
| performance. When we have feature parity, we'll run the
| Computer Language Benchmarks and post the results. That'll be
| fun to see!
| kaba0 wrote:
| Do you (can you even have in go?) have a direct threaded
| interpreter? That alone may give a 2x performance difference
| last time I checked, or is that no longer the case?
| wahern wrote:
| I don't think you can even implement indirect threading in
| Go. Go supports neither computed goto nor mutually
| recursive tail-call optimization (only self-recursive).
|
| Jump tables for switch statements was only implemented last
| year. If you squint that's close to indirect threading, but
| still with at least one unnecessary conditional per op.
|
| For the curious, here's their giant switch: https://github.
| com/platypusguy/jacobin/blob/c508ec50f55ef381... In
| practice compilers have always been finicky when it comes
| to coaxing them to emit jump tables from switch statements,
| and I bet this is especially truly for Go.
| suresk wrote:
| Yeah, I think any bytecode interpreter ends up with a
| giant switch in the critical path at some point :)
|
| Around the time that change was made to Go, Andrew and I
| were looking at this and wondering how big of a
| performance hit it was and if there were a better way to
| structure that. I had a hunch that the compiler should be
| smart enough to not compile that as a switch/giant if
| block, and a quick trip to a disassembler showed it using
| binary search. This commit: https://github.com/golang/go/
| commit/1ba96d8c0909eca59e28c048... added the jump table
| and has some nice analysis on where it makes sense to do
| binary search vs jump tables.
|
| As far as I can tell, with certain restrictions (that are
| fine in this case), it is pretty reliable at optimizing
| giant if/else blocks and switches.
| tombert wrote:
| That's fair; I thought maybe the use of the Go GC might make
| the results a bit more interesting.
| kaba0 wrote:
| Go has a much more primitive GC, so I wouldn't expect a
| positive result from that itself.
| jillesvangurp wrote:
| Clearly this is not going to be a high performance thing. I'd
| be surprised if it is even close.
|
| It seems the goals for this are purely academic and about
| figuring out how Java works. They'll probably just do a simple
| interpreter and not a JIT compiler. That would be good enough
| for a POC. Additionally, they already indicated that they'll
| use Go's garbage collector, which won't be setting speed
| records with this either. And Java's typical usage of memory
| might actually stress it out a bit. Then there is the standard
| library which is going to need plenty of support for things
| like Threads, IO, various synchronization primitives and locks,
| etc. Doing that in Go is going to be a bit interesting but
| probably doable. Alternatively, they might just interface with
| native code directly and bypass the Go ecosystem. They might
| even reuse some things from openjdk for that. Speaking of
| which, native code and JNI would need to be implemented anyway.
| layer8 wrote:
| The startup cost is dominated by class loading, which Jacobin
| will have to do more or less the same as OpenJDK [*]. GraalVM
| of course profits from AOT compilation.
|
| [*] until the latter will implement condensers:
| https://openjdk.org/projects/leyden/notes/03-toward-condense...
| ptx wrote:
| Didn't they introduce some new format with Java 9 and jlink
| to store modules more efficiently? Why hasn't that fixed
| class loading performance?
| layer8 wrote:
| The issue isn't the format. Class loading includes bytecode
| verification (verifying the type soundness of whatever code
| the class files contain), and then executing the
| initialization code defined by each class (static code
| blocks etc.).
|
| The optimizations in JDK 9 are for reducing the file size
| of the JDK (in terms of number of classes) when distributed
| with a standalone application, but that by itself doesn't
| significantly affect startup time I believe, because
| classes are lazy-loaded only as required in any case.
| mike_hearn wrote:
| The jimage format does optimize startup a bit because it
| builds a perfect hashtable to go from class name to
| classfile bytes. On the classical classpath that requires
| an O(N) scan of the JARs in the app.
| erik_seaberg wrote:
| Does a JVM really need to re-verify the standard library
| it ships with?
| layer8 wrote:
| It's actually the default to not do that
| (-Xverify:remote). So I was probably wrong about bytecode
| verification being a relevant part of startup time,
| unless you count the application itself.
| mike_hearn wrote:
| It improves it a bit, and AppCDS improves it a lot (~30%
| startup win), but neither feature is widely used as it
| changes deployment workflows.
| papercrane wrote:
| The modules system is for better encapsulation and the
| ability to produce a JVM without features you don't need.
|
| Faster start up time is in the works with projects like
| coordinated restore at checkpoint which will let you start
| a JVM up in an already "warmed" state.
|
| https://github.com/CRaC/docs
| ptx wrote:
| If I understand the README correctly, CRaC in an abstract
| API that delegates to an implementation (CRIU) which is
| Linux-specific, described in its README as "the never-
| ending story, because we have to always keep up with the
| Linux kernel".
|
| So this wouldn't help if I'm deploying a GUI app for
| Windows, for example.
| papercrane wrote:
| Correct, it might, someday, become part of Java SE. If
| that ever happens I would expect it working on every
| platform Java supports would be a prerequisite for that.
|
| Right now it's primarily useful for reducing start up
| times in AWS Lambdas and auto-scaling workloads.
| suresk wrote:
| I'm not the author, but I have contributed a few things to the
| project over the past year. The performance isn't anywhere
| close to the vanilla JVM - several times slower at best. It is
| entirely interpreted and there aren't any of the optimizations
| that have made their way into the JVM over the decades it has
| been around.
|
| It has been a fun project to play around with for someone like
| me who thinks this kind of stuff is fun and interesting but
| will probably never get a chance to work on it full time. Cool
| to see it noticed, though!
| twic wrote:
| This JVM has no JIT, so OpenJDK and Graal will rather more than
| inch past it. It's not even a given that it will start up
| faster, since i don't believe it has been optimised nearly as
| hard.
| pg_1234 wrote:
| You'd probably get better results writing a Go compiler in
| Java ...
| adra wrote:
| Well, you'd probably get pretty good perf with graalvm
| truffle, but certainly the building blocks of golang
| shouldn't be that problematic to implement in java given
| that the go language model is more straight-forward IMHO. I
| wouldn't assume it'd beat pure golang compilation but who
| knows.
| cfiggers wrote:
| Wrote a Go compiler in Java, used it to compile this JVM
| implementation written in Go
| paulddraper wrote:
| Great, now just use this JVM to run your compiler :)
| seanw444 wrote:
| Ouroboros
| KRAKRISMOTT wrote:
| Lambda the ultimate
| rizky05 wrote:
| [dead]
| peter_d_sherman wrote:
| Very cool!
|
| Hey, you know a feature which I would love to see (and maybe it's
| already in there) -- would be the ability to "orchestrate" Java
| code. In other words, to be able to add external event hooks from
| functions/procedures/methods -- at runtime...
|
| These event hooks, when encountered, would be able to do such
| things as:
|
| * Print/pipe a debug message to an external program or log or log
| viewer;
|
| * The debug printer should contain the function/procedure/method
| name, parameter names, and parameter values automatically (there
| should be functionality to have them in both the invocation and
| after the function/procedure/method's code is complete, prior to
| returning to the caller, so two possible places per
| function/procedure/method...);
|
| * The ability to selectively turn on/off such events at runtime;
|
| * The ability to add additional code which could be evaluated for
| every such event (also at runtime), and make the determination if
| the event should be processed or skipped;
|
| * The ability to do all of the above programmatically, via API...
|
| * Some sort of GUI which automatically imports all
| function/procedure/method names of a running Java program at
| runtime, then gives the user the ability to track/log whichever
| ones they want, by simply selecting them to a secondary listbox
| of tracked function/procedure/methods...
|
| Now, maybe some or all of that -- is already baked in there. I
| didn't look at the codebase long enough to know...
|
| But if it is in there, that's awesome! And if one or more of
| those features are missing, then maybe a future version
| maintainer or forked version maintainer might be persuaded to add
| them in there...
|
| (Side note: It would be nice to have the above functionality for
| all programming languages!)
|
| Anyway, as said previously, looks very cool!
| andrewbinstock wrote:
| Lead dev here. Some very cool ideas in there. Will copy/paste
| this into our task tracker on YouTrack for further
| consideration. Much appreciated.
| mike_hearn wrote:
| That sounds like what the Java world calls aspect-oriented
| programming.
|
| Also, GraalVM/Truffle has that sort of ability today, supported
| for any Truffle language:
|
| https://www.graalvm.org/latest/tools/graalvm-insight/
| whalesalad wrote:
| Will it run clojure?
| philipov wrote:
| I heard the project repository suffers from a detached head.
| keb_ wrote:
| heh
| lopatin wrote:
| I wonder if it uses Go routines as the virtual threads
| implementation.
| jerf wrote:
| If I am reading the code correctly, and as a parachute code
| reviewer I may very well not be, I am not sure this supports
| threading at all, or at least, not user-level threading.
|
| I can find references to a thread module and it clearly creates
| a "main" thread, but I actually can't find anywhere where it
| creates anything other than a main thread, nor do I see any
| code for task switching manually/cooperatively, or anything
| else like that. There's a global thread table but I can only
| find test code manipulating it. And the main core VM loop
| doesn't seem to have any concurrency in it to me.
|
| Corrections welcome; posted fully in the spirit of Cunningham's
| Law as I'm curious too.
| andrewbinstock wrote:
| Author here. No threading currently. Once we have all the
| bytecodes executing, we will perforce be obliged to add
| threading. Starting with the heavier threading that Java pre-
| Loom uses.
| returningfory2 wrote:
| Can you just use green threads (goroutines) from the get-
| go? Or, by Hyrum's law, is the fact that legacy Java
| threads are really OS threads part of Java's de facto
| public API?
| jerf wrote:
| You have to distinguish between the implementation and
| the interface from the Java point of view. In Go, you'd
| use goroutines. Even if you want to use an OS thread you
| use a goroutine and then pin it to an OS thread, and
| there's no reason to do that in this case.
|
| However, inside Java it may look like the original
| Threading API. In that case you'd have a JVM running the
| Thread abstraction in Java on top of green threads in Go,
| while not supporting Java green threads, and there's no
| contradiction or even anything weird going on. Perfectly
| normal.
| returningfory2 wrote:
| Right right, but I wonder if there is some weird edge
| case where some Java program relies on threads being real
| OS threads...like, for sure Java threads being OS threads
| is an observable feature of a running Java program (you
| can see it in htop!). And maybe someone has built a Java
| based system somewhere that uses that...
| tracker1 wrote:
| I recall seeing some really hacky Java implementations
| that used thread references for state management...
| Mostly, in that the same devs expected the same
| experience in the C# ASP.Net lifecycle and it lead to a
| lot of wierd race bug conditions in practice.
| andrewbinstock wrote:
| That's a good question. I don't know enough about the new
| green threads to give you a definitive answer, but it's
| certainly something we'll examine closely.
| [deleted]
| twic wrote:
| I concur. Native methods have hardcoded implementations,
| wired up here, and there is nothing for java.lang.Thread:
|
| https://github.com/platypusguy/jacobin/blob/18c541820cb73bda.
| ..
|
| I couldn't figure out what happens if you invoke a native
| method that has not been set up like this.
| layer8 wrote:
| They'll have to implement _java.lang.Thread_ somehow, or not
| a lot will run on that JVM.
| andrewbinstock wrote:
| Lead dev here. Yup, that's right. There are a few classes
| that we will be forced to implement in Go. We're fixated on
| keeping this number as low as possible, but Thread is one
| that is inescapable. Class is another inescapable one--if
| we're to support reflection, etc.
| samus wrote:
| Correct, implementing the core libraries is actually a
| massive effort. You can't just take the Hotspot one because
| it is a massive cumbersome codebase and it would force your
| JVM to work like Hotspot internally. (Let's ignore for a
| moment the question whether that would even be legal)
| suumcuique wrote:
| Project Loom is not included in JDK17.
| jsd1982 wrote:
| Does "capable of running Java 17 classes" imply Java >= 17 or
| Java <= 17?
|
| I tried to run a Java 11 jar on my M1 Mac with $JAVA_HOME pointed
| at a temurin-11.0.20 JVM but no such luck: $
| ./jacobin -jar my.jar Class Format Error: Class has two
| package names: apple/security and com/sun/crypto/provider
| detected by file: cpParser.go, line: 241
| ParseAndPostClass: error parsing classes/module-info.class.
| Exiting. Class Format Error: Invalid access flags of
| MethodParameters attribute #1 in main detected by file:
| methodParser.go, line: 333 Class Format Error:
| detected by file: methodParser.go, line: 109
| ParseAndPostClass: error parsing my.Main. Exiting.
| andrewbinstock wrote:
| Thanks for your note. The package notes on Jacobin say that we
| strongly discourage folks from running it in its present form.
| There are enough features still to be implemented, that for
| anything but trivial classes, you won't have a good experience.
| TBH, we're about a year out (we think) from having a version we
| can solicit users to test.
|
| Nonetheless, if you'd be kind enough to post the above error
| and the class you used into the GitHub Issues tracker [0],
| we'll definitely include it in our test suite and make sure
| whatever the problem is, it'll be corrected.
|
| [0] https://github.com/platypusguy/jacobin/issues
| caeril wrote:
| [flagged]
___________________________________________________________________
(page generated 2023-08-24 23:00 UTC)