1

I would like to demonstrate to my students that using Java in real-time systems might be problematic since Java might do unexpected garbage collection. How can I write a Java program that will:

  1. likely cause Java to stop and do garbage collection in an unexpected time (without System.gc());
  2. The garbage collection will take noticeable time (e.g. several seconds)?

In case this matters, I use Open JDK 8 and Oracle JDK 8 on Ubuntu 16.04.

If it is not possible to do both, then I will be happy with at least item 2, i.e, a program where the garbage collection takes a long time when I do System.gc().

NOTE: I am not looking for a graphic representation of the garbage collection process - only to show that it takes a long time.

Erel Segal-Halevi
  • 33,955
  • 36
  • 114
  • 183
  • 1
    so what have you tried so far? – Scary Wombat Jan 29 '18 at 06:13
  • This is... hard. Without knowing how Open JDK 8 works internally it would be impossible. Also... for (2) you can just make the finalize function wait for a few seconds (???) – user202729 Jan 29 '18 at 06:17
  • You could always trigger a GC through your favourite profiling tool if you wanted to. – Joe C Jan 29 '18 at 06:19
  • Oracle has a Garbage collection guide complete with a hands-on demo application: http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html - I can't say I've tried it, but it might be worth a look. – Sam Jan 29 '18 at 08:26

5 Answers5

1

You might want to check out 'The Real-Time Specification for Java' here. You might also want to read through this, an introduction to real time programming on/in java.

Erik
  • 2,013
  • 11
  • 17
1

You can start with a relatively large heap space java -Xmx512m ... to achieve a sufficiently messy heap.

Then create many objects. Best graphs with cycles (cyclic references with long paths). Let many of them become obsolete. Best multiple threads.

Show some animation that on garbage collection would halt. Best showing a step time in a diagram and gc; Runtime.freeMemory().

Use a jvm monitor. It would be good moment to introduce memory & CPU profiling; in NetBeans or eclipse.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • The garbage collector doesn’t traverse dead objects. Neither do loops affect the performance significantly. What do you think will happen if the garbage collector encounters a reference pointing to an already encountered object? – Holger Jan 30 '18 at 09:17
  • @Holger I wanted to have large, interwoven, cyclic lists (graphs). To _determine whether such structures become dead_ (gc) is of a higher order than O(N), N being the newly recursively unreachable objects. Do you think this approach feasible? Or am I erring? Would be great if you could elaborate a bit. – Joop Eggen Jan 30 '18 at 10:47
  • There is no such thing as “recursively unreachable objects”. The garbage collector will traverse *reachable* objects, starting from the garbage collection roots (local variables of live threads and `static` variables of permanent classes). All objects it didn’t encounter during the marking process, are garbage *per se*, regardless of how these dead objects are referencing each other. In case of a copying collector, all survivor objects are transferred to a new memory space and afterwards, the old space is considered free, without any effort. It doesn’t even know, how many objects just died. – Holger Jan 30 '18 at 11:41
  • @Holger, I understood; of course the gc does not start with some unreachable object (which impossibly would have meant to handle every assignment), but with all reachable objects. If `root->r1000->r999->....->r2->r1` then to mark r1 might take a bit, depending on the branching of objects. Mark-and-sweep would just need O(N_alive). I wonder how to overload the gc, I doubt object finalisation would help there or be considered genuine. Or CPU intensive work impairing gc. Thanks, you have convinced me. – Joop Eggen Jan 30 '18 at 12:14
  • Creating lots of objects with nontrival finalizers would impose a significant overhead (even at object creation), but is far away from a typical Java program’s behavior. A large linear chain of live objects could raise the marking time, but when creating lots of these objects, the oldest will eventually get promoted to the next generation and for the old generation, the JVM tracks modifications, to only process those objects that have changed since the last garbage collection. And last but not least, the G1 collector has a configurable maximum pause time goal… – Holger Jan 30 '18 at 13:04
1

Have you considered that what you are telling your students is not generally true if you cannot reproduce it? If your toy definition of realtime is "pauses less than 1s" then many JVM applications can be considered realtime.

And that's without resorting to pauseless collectors.

But of course one can also trivially construct cases where a GC takes more than 1 second, e.g. by allocating a sufficiently large heap so that the system starts swapping. But in that case non-GCed languages would also experience latency spikes - maybe not quite as bad - and thus it would be a quite poor demonstration of GC issues.

System.gc() also is a bad demonstration since it currently invokes a single-threaded collection while the normal operation of the default collectors make use of multiple cores. Fixes are underway

To construct a somewhat realistic scenario under which modern collectors will experience >1s pauses, you will need

  • a large heap - multiple cores can generally chew through small heaps quite fast
  • a large live set size - dead objects are cheap
  • lots of small objects - large arrays of primitives are generally collected quickly
  • lots of reference between objects - null fields don't need updating. random object graphs also make life a lot harder for G1GC

And since you're using OpenJDK you would merely be testing collectors of a JVM targeting desktop and server-class workloads. There are other JVMs and 3rd-party garbage collector implementations which aim for realtime goals so one could simply dismiss your demonstration as calling a downhill bike a horrible bicycle because you can't win a velodrome race with it.

the8472
  • 40,999
  • 5
  • 70
  • 122
  • That was my first thought too. If a claim is that hard to prove, it’s worth rethinking the claim. – Holger Jan 30 '18 at 09:13
0

Small general java program cannot produce GC for seconds, but instead, you can run a program with creating some Objects with very less heap size and while executing program use below switches which will print GC logs in much details easy to explain.

-XX:+UseSerialGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps

ie. java -XX:+UseSerialGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps abc.java

0

Reason System.gc() takes long time because it makes Garbage collector to do full collection, basically looking all the objects in the heap.

If you create many objects and let them die young, mostly young collection will occur. Young(minor) collection is much faster than major collection because it is only looking objects in the young generation space.

To ensure a long gc pause, I suggest hold object references to a degree where heap is almost full and release some of the older object references and try allocating more object this way you can ensure major collection will likely happen and it will cause longer latency.

You can view the latency with jdk tool flight recorder(jmc).

miskender
  • 7,460
  • 1
  • 19
  • 23