Master the fundamental concepts of jvm internals through this focused micro-challenge.
You have read the whole brief, and the concepts above stay free on every task. Writing and running the code needs a plan.
Three hints are available for this task, revealed one at a time inside the code workspace so you can struggle productively before seeing them.
Every task includes starter code, theory, and hidden tests so you can implement and verify locally in the browser.
How it worksYour toy JVM allocates objects on a simple heap. A mark-and-sweep pass reclaims those unreachable from roots: operand stacks, static fields, and thread-local roots in a real JVM.
Algorithm:
Object headers need a mark bit and a link for the allocation list. Reference fields in objects must be traced during mark.
For example, allocate strings and arrays in \`main\`, drop references, run GC, and print freed versus live counts.
This mirrors the algorithm family behind older stop-the-world JVM collectors before regional designs like G1.
Compacting is optional in your toy collector but fragmentation will show up if you allocate variable-size objects repeatedly. Mark-and-sweep alone is enough to learn root scanning discipline.
This exercise asks you to add mark-and-sweep to your mini-JVM's heap. You will implement root scanning from interpreter stacks and statics, then sweep unreachable objects between bytecode runs.
You will use the same mental model here when reading production interpreter source later in the track. Sketch one concrete input on paper, predict the outcome, then confirm with code. That discipline catches logic errors early and makes debugging far faster when you extend the implementation in follow-on tasks.
Add a mark-and-sweep garbage collector to a mini-JVM. The interpreter allocates objects in a fixed-size heap with first-fit placement. When no hole is big enough, it runs the collector. The collector marks from the roots, which are every frame's local variables and operand stack, then sweeps the unmarked objects. It never moves objects, so the heap can fragment. If the collection doesn't free a big enough hole, the program dies with OutOfMemoryError.
# starts a comment.
cLoading…
| instruction | effect |
|---|---|
new CLASS F | allocate an object with F reference fields (0-8, all null), taking 1 + F slots; push it |
aload i / astore i | push or pop local i (i < N) |
aconst_null, dup, pop | the usual stack operations |
putfield k | pop the value, pop the object; object.field k = value |
getfield k | pop the object; push its field k |
call NAME A | move the top A stack values into the callee's locals 0..A-1 (the deepest first) |
areturn / return | pop the frame; areturn pushes the value onto the caller's stack |
gc | an explicit collection |
heapdump | print the live objects by address |
Locals start as null. Object ids count from 1 in each run.
allocation failure) and retry. If it still fails, throw java.lang.OutOfMemoryError: Java heap space (FREE free slots, largest hole H, need S), or … (FREE free slots, need S) when even the total is too small.GC #N (REASON): marked M, freed K object(s) (S slot(s)), heap USED/SIZE. The reason is allocation failure or System.gc().java.lang.NullPointerException (a field access on null) and java.lang.StackOverflowError (a call when 64 frames exist).
Exception in thread "main" X, then \tat FUNC(pc P) per frame, innermost first, where P is the instruction index (the call, for callers).\t... N more.VerifyError: stack underflow at FUNC PC (OP)VerifyError: CLASS#ID has no field KVerifyError: no function XVerifyError: FUNC falls off the endVerifyError: stack overflow in FUNC (16 values)VerifyError: FUNC has only N locals (a call passing too many arguments)cLoading…
@ADDR CLASS#ID [fields], with the fields as #id or null, or (empty heap).NAME returned after N GC(s); heap USED/SIZE.heap: SLOTS (1-1024)func: NAME locals N (0-8)FUNC: bad instruction: LINErun: no function Xunexpected line: LINEInput:
cLoading…
Output:
cLoading…
Hidden tests cover a GC inside nested calls that must keep the callers' objects, an unreachable cycle, an OutOfMemoryError caused by fragmentation, a null dereference, runaway recursion, and verify and parse errors.