6 ms·
In case you missed this post and the previous ones, here's the news: this labour is gone on the JVM. The GCs in JDK 14-15 are good enough pretty much out of the
by pron 6y ago
In case you missed this post and the previous ones, here's the news: this labour is gone on the JVM. The GCs in JDK 14-15 are good enough pretty much out of the box, and they're getting better very quickly now.
OTOH, if you think other languages let you do away with a GC without pretty significant extra work, especially in concurrent systems, well, then you haven't had experience with those languages.
- VHRanger 6y agoIn D, the GC is guaranteed to only ever run if you allocate new memory on the heap. This is still a lot of work for a video game (because you never want any latency, the only way to achieve this is with an arena allocator or going full @nogc). But for apps where the latency requirement is bounded, D doesn't make it hard and the language is nice and ergonomic.
- chrisseaton 6y ago> In D, the GC is guaranteed to only ever run if you allocate new memory on the heap. Isn't that how most GCs work? Why would you do anything else? To release memory to the OS? That's not really a priority in most runtime systems, and I think wanting to do that is a pretty niche requirement.
- frant-hartm 6y agoI want memory allocation to run in constant time, so I can achieve low latency. That's the point of concurrent GCs - they run in the background to free up memory.
- chrisseaton 6y agoI know they run in the background... but I don't think they run in the background if there's nothing to do, do they?
- imtringued 6y agoA JVM with default settings will happily allocate more memory without attempting to even run a single GC cycle until it hits the configured heap size. Once it hits the limit it will try to run the GC.
- deleted 6y ago[deleted]
- nemothekid 6y agoIn Java, everything is heap allocated, whereas in D you have a choice.
- chrisseaton 6y ago> everything is heap allocated You can definitely write useful Java applications where nothing gets heap allocated, through using existing objects, and through using scalar-replacement-of-aggregates.
- wokkel 6y agoNot entirely correct: https://shipilev.net/jvm/anatomy-quarks/18-scalar-replacement/ https://shipilev.net/jvm/anatomy-quarks/18-scalar-replacemen... . Hoi cannot force a specific allocation, but the jit does do something akin to stack allocation.
- readittwice 6y agoReleasing memory is definitely useful for mobile devices or browsers. Even in some server use cases it's useful, e.g. when you pay for memory usage. I guess that's why the JVM has this -XX:SoftMaxHeapSize option. I guess it depends whether GCs are always scheduled in an allocation or can be triggered another way. Either way that should be easy to disable. I read somewhere that D doesn't have write barriers, so I would assume they have a hard time implementing more advanced GC features like generational collection or concurrent marking. It's not suprising that the GCs in the JVM achieve much better pause time.
- tjoff 6y agoThat statement has been said for pretty much every release of any GC. I'd say the opposite is true. Relying on GC requires significant extra work. Because you always need to think about memory (exception: small script-like applications). The only thing a GC does is that it enable you to not think about it, but the moment you don't you will write bad code and realize it was a disservice all along. And by then it is too late. So in a GC language you need to constantly be aware of when you take something for granted. Which is more work than just doing it manually yourself.
- RhodesianHunter 6y agoThis is not really true at all though, nor is it how people who design performance intensive systems go about it. generally you design your application the way you normally would, do some testing, and then go back through the hot path and make some adjustments.
- pron 6y agoFirst, a GC allows you to abstract over memory allocations (i.e. hide them). This makes maintaining code over time significantly easier, as allocation becomes a hidden implementation detail. Second, while there have been similar claims made in the past, they always apply for a certain target throughput and latency. GCs are constantly making great strides in that regard. Next year, ZGC will have a <1ms worst-case latency for pretty serious allocation rates, and with an acceptable hit to throughput. As you can see in this post and previous ones, G1 offers great throughput with acceptable latencies.
- jakewins 6y agoThis GC you speak of that hides allocations as an implementation detail sounds excellent! Every GC I've worked with - and my day job is orchstrating several thousand JVMs - simply moves the time when you need to think of allocations somewhere else. Either it moves it into PagerDuty, like G1, or it moves it into your GCP bill like Shenandoa and ZGC. The trade-off has always been latency-throughput-footprint, nothing I've seen yet has changed that. The innovation in Rust is realizing you can do all the tracing work at compile time.
- twic 6y agoThey're good enough for some purposes, but not for others. If you need <10 us latency, you still have to jump through hoops to get that on a JVM.
- haxen 6y agoFor < 10 µs latency there are hoops in any language, as well as the OS, like thread-pinning, marking CPU cores unusable for OS interrupt handling, all kinds of virtual memory issues, etc. And you can't afford a single network hop or even SSD access.
- Erlich_Bachman 6y agoAnd yet 90% of time we hear about this latency problems when speaking about a language, it's always about JVM... When it would be equally hard in any other dynamic language... What gives...
- vips7L 6y agoI don't know of any other runtime that gives you the tools to tune your latency and the GC other than the JVM.
- saberience 6y agoErr Dotnet? :)
- exdsq 6y agoGHC (Haskell)?
- therealdrag0 6y agoMight be a side affect of Java’s dominance/popularity.
- PaulDavisThe1st 6y ago>And you can't afford a single network hop or even SSD access. 10µs ? You can't even afford that many function calls. Seriously, 10µs is not a sensible target for a general purpose OS, maybe not even for a general purpose CPU. Achievable? Perhaps. But sensible? Not really.
- manigandham 6y agoThat's the trade-off. Predictability and determinism vs development and testing effort. The memory work remains the same, you can do it yourself or let the GC handle it. For 99% of applications, the GCs are good enough and getting better every year, but low-latency still needs predictability and would ideally choose manual memory management. The realities of the job market and IT deployments are different though and that's why we still have JVMs involved with low-latency scenarios because of talent, tooling and productivity.
- karlmdavis 6y agoI mean... Rust literally doesn't have a GC, in the usual sense -- certainly nothing resembling most GCs' generations, mark, sweep, etc. approaches. Steve lays it out far better than I could, coining the term "static garbage collection": https://steveklabnik.com/writing/borrow-checking-escape-analysis-and-the-generational-hypothesis https://steveklabnik.com/writing/borrow-checking-escape-anal.... And to your "without pretty significant extra work" qualifier: I really don't find that to be true with Rust. The initial learning curve was a bit rough, but certainly far less so than other languages/platforms I've picked up (hello, ML). In the end, I find that it's just a nice, helpful, productive, and ludicrously performant language.
- lostmyoldone 6y agoRust is really nice, I like it a lot. It's probably also one of the easier languages to write performant code with reasonable latencies in most cases, almost all. However, automatic resource freeing/deallocation can still bite you if you need really reliable low latencies, and if you think the language will handle it all. Since the automatic deallocation in rust, per default, happens at the point where the value goes out of scope, if a resource destruction could block, or perform expensive operations on destruction, it can not be allowed to go out of scope in a latency sensitive thread. Usually not that much of an issue unless you are chasing really low latencies. But things can still happen in rust that catch you off guard. Say a value goes out of scope, and because of reasons, it does so with a destructor doing a logging call, which has accidentally become blocking on a socket send call, which it did because nobody realized that the AWS/GCP/whatever logger adaptor actually didn't perform all IO in a thread with which was only communicated with locklessly, which nobody noticed before because it was only if a buffer was full, which only happened today because .... Not a big deal, it's almost all the same things which mess upp latencies in C++ code. And that's the thing. It's not necessarily easier to get low latency in rust than in C++, but the work required for hitting a quality/performance/latency target in rust is probably still lower than for C++. Unless you are lucky to have a very mature C++ low-latency stack, together with all the utility functionality you need, which seems to be exceedingly rare. Is the work required lower than Java on a custom/tuned JVM? We'll have to wait and see. It probably is, but it's a complex balance between access to utilities, language complexity, and several more parameters which ultimately decide which platform provides the best environment for low latency code, especially if the complexity is non trivial.