Good afternoon, ladies and gentlemen! I hasten to inform you that the last days are coming. It seems that the Java world has evolved to such an extent that either we can now safely use Rust instead of Java, or Java instead of Rust. Bloody details are waiting for you under the cut.
Well-known Internet activist Alexei Shipilev recently
announced the following :
“Why don't we implement no-GC in HotSpot, and win every single 5-second latency benchmark. 100 GB heap is enough to survive for 10 secs. ”
Cool, great joke - we thought, and let's go eat. And having returned from the kitchen, they choked at once with all the buns, because there appeared screenshots!
')
Well, we all know how to photoshop, right?
And then
code appeared
, and things began to take a steep turn. You understand, when Shipilev puts something in public,
then in a month it’s time to ask about it at the interview .
With trembling fingers, we open javanet, and we see ...
JEP: Epsilon GC: The Arbitrarily Low Overhead Garbage (Non-) Collector
Further I will retell briefly what is written there. I was too lazy to do an exact translation, grammar warriors can go through and read the original :)
Oh b-what is this, Barrymore?
The feature is that the GC only cares about the allocation of new memory, and clever garbage collection strategies can be scored. As soon as the available heap ends, the JVM stop procedure begins.
Goals
Provide a fully passive GC implementation with limited memory available for allocation. In return, we get the lowest overhead on the performance of such a GC. The implementation should not affect other GCs or make a big difference in the JVM.
Motivation
On the Rights of the writer of the topic for the Habr, I will speculate a little . Indeed, not every company is Google. Someone just wants to make a minimal working product, and only then take care of its development. Perhaps in the future everything will have to be rewritten on a more advanced platform. Perhaps the developers will run out of money earlier. To do this, you do not need to immediately pull into a project huge G1 type enterprise pieces.
Okay, jokes aside, go on in the text! It is assumed that there are at least 4 cases where such a thing as Epsilon can really come in handy.
First, with Epsilon, you can compare some other more advanced GC, which will help in its development and catching the bugs introduced by the assembly mechanism itself.
Secondly, for true
Baitoeb (this is a term, not a curse word!) Applications in Java, such a GC is simply indispensable. Imagine that you are writing firmware for another teapot or smart toilet, and the fact that garbage collection is considered a bug in the application: instead of collecting garbage, it’s better to fall, and smart balancers will unbalance the load on other nodes / VM. What should prepare the toilet, using a network of balanced VM, let us close for clarity. In addition, the use of Epsilon will get rid of extra barriers - the highlight on the cake of an ideal performance.
Thirdly, to test OpenJDK itself, it is a good idea to have a means of limiting the allocated memory in order to test load invariants for this very memory. Now such data is taken from MXBeans or even parsed in GC logs. If the GC supports only a limited number of allocations, it will really simplify testing for the OpenJDK developers. (Most likely, the readers of this article are not the OpenJDK developers, but now you can try to pass for the OpenJDK development at the next interview.)
Fourth, it will help establish the absolute minimum for the VM-GC interface, and can serve as proof of its correctness. What is useful, for example, for
JDK-8163329 ("GC interface") (By reference, a wall of text that anyone can translate into Habr).
Details
For the user, Epsilon looks like any other GC for OpenJDK connected with -XX: + UnlockExperimentalVMOptions -XX: + UseEpsilonGC.
Epsilon allocates linear memory in a single, consecutive piece of memory. This allows you to use the simplest code for the lock-free TLAB (thread-local allocation buffers), which can reuse the already existing code from the VM. The issuance of TLABs helps maintain the resident process memory in the amount that was actually allocated. Since in this situation and the allocation of large pieces of memory, and TLABs, not much different, they are processed by the same code.
The set of barriers used by Epsilon is completely empty, since Epsilon does not do any real assembly cycles, and therefore it doesn’t give a damn about the object graph, tagging objects, copying objects, and all the other junk. Only hardcore, only performance!
Since the only important part of the interface runtime is where Epsilon issues TLABs, its performance mainly depends on the size of the TLABs. With an arbitrarily large TLAB and an arbitrarily large pile, the performance overhead is an arbitrarily small positive number, hence the name Epsilon. Authors and users of golang can begin to turn green with envy and tear hair on generics.
Once the hip is all over, you can’t issue a TLAB anymore; Here you can do something similar to what other GCs do:
- Throw OutOfMemoryError with a weighty description
- Dump a bunch (as always, turn on via -XX: + HeapDumpOnOutOfMemoryError
- Hardly drop the JVM, and optionally perform some external action (as usual, -XX: OnError = ...), for example, start a debugger, notify the monitoring system, or at least write a letter to Sportloto
The prototype was tested at low loads, and planned to fall at increased loads.
The code still lies here until it was taken away by a UFO:
http://cr.openjdk.java.net/~shade/epsilon/
Alternatives
There is no alternative. At least such that all barriers are cut down. Seriously.
I hope that our joy and euphoria from meeting with a truly unique GC, is somewhat similar to the euphoria of those drugs that the author uses Epsilon, creating a similar suit.
In any case, if barriers are not a problem, then Serial or Parallel (old) GC can give a similar performance profile - if you can, of course, configure them so that garbage collection never starts (for example, setting huge values ​​for young gen, turning off adaptive heuristics, etc.). Considering how many options there are, it is difficult to guarantee that this will come out.
Further improvements in modern GC, such as Shenandoah, can lead to a decrease in the overhead project to a level that is negligible compared to a fully no-op GC. If / this happens, Epsilon can still be used for internal functional / load testing (if in retirement you still want to do internal GC testing).
Does it even work? Is it generally legal?
Regular tests are not needed and are not suitable for Epsilon. Most of them believe that you can scatter an arbitrary amount of garbage. Therefore, to test Epsilon will have to do new tests - you need to check that it works on tasks with low memory allocation, and when the heap is exhausted, it drops not anyhow, but predictably. To check the correctness, there will be enough new jtreg tests lying in hotspot / gc / epsilon.
A one-time test performed during the development of Epsilon is enough to assume the performance characteristics of the interpreter, C1 and C2. Further testing of nafig is not necessary, since the current implementation from the very beginning of development was more stable and reinforced Stalin bunkers.
Risks and assumptions
Useful against the cost of support. It can be assumed that this implementation is not worth the trouble, because no one needs it anyway. On the other hand, based on experience, many java ecosystem players experimented with throwing GC from their own custom JVMs. So, if we have a ready-made no-op GC, this will help this part of the community. Well, or at least, we can be proud that we used no-op GC when it was not yet mainstream. Given the overall low cost of implementation, the risks are minimal.
Public expectations. Given that this garbage collector does not actually collect any garbage, someone might perceive it as a dangerous practice. Turn on Epsilon on the production inadvertently, and immediately after the heap overflows, the most unpleasant news awaits the leaders. The author assumes that there is no real risk, as long as the feature passes under the experimental flag, that is, it requires the inclusion of -XX: + UnlockExperimentalVMOptions.
The complexity of the implementation. It is possible to imagine a situation where, in general, with other subsystems, the code will have to be changed more than originally intended - for example, in the compiler, or in backends of specific platforms. On the other hand, the prototype shows that all such changes were strictly isolated from the very beginning. If this turns out to be a real risk, then JDK-8163329 (“GC interface”), which was already mentioned above, can help.
Dependencies
In order to reduce the number of changes in external code, this work is dependent on JDK-8163329 (“GC Interface”). If the changes in the general code are minimal, then it should not require changes in the GC interface.
Conclusion
Honestly, I'm quite tired of writing all this. So just leave it here. May the force be with you! You will need it in the new bright future, with Java without garbage collection.
PS: Shipilev has a link to this post, and he is watching you, username