Skip to content
academia.sh

Course Advanced

JVM, Concurrency and Performance

Published

By the end of this course

Start course

01

JVM Internals

A class passing through the class loader hierarchy into its runtime, and which thread runs its initialization; the memory areas that decide whether a value is thread-local or shared; who takes on the cleanup of an object that has lost its reachability; and which data, and on which side, gives rise to an optimization decision not found in the class file.

  1. 01 Class Loading The class loader hierarchy runs on delegation: the class a loader is asked for is, in most cases, defined by the loader above it, and identity is read only through `==`. Three of four triggers run class initialization; one — reading a compile-time constant — never does. When two threads touch the same class at the same time, only one of them runs the initialization block; the other waits for it to finish.
  2. 02 Memory Areas Three of six values written by the same thread are never visible to another thread, and three are — what decides this is not the value's type but where it sits: a local variable and a parameter sit in the call frame, an object field and an array element sit on the heap, a static field sits in the class's own storage. Being shared is not being visible: without an ordering tool, the same read is undefined.
  3. 03 Garbage Collectors Who takes on the cleanup of an object that has lost its reachability: not the caller, not a thread the program created — a party that never appears in any record at all. Cleanup does not run while a strong reference stands and runs once it is removed; a weak and a phantom reference get enqueued within a few requests; a soft reference resists as long as there is no memory pressure. `System.gc()` is a request, not a command.
  4. 04 Just-in-Time Compilation The class file holds no optimization decision at all: even if the same method is called three hundred thousand times, the instruction list read from the class file stays byte-for-byte identical. The party that makes the decision is not the program itself; the receiver type count the decision rests on, though, is produced by the calling thread while it runs. When a call site moves from monomorphic to polymorphic, the assumption becomes invalid — this lesson does not measure speed, it only counts instructions and receiver types.

02

Concurrency

The call that decides whether a body runs on the caller or on another thread, the memory model's promises of visibility and ordering, measuring monitor locks and reentrancy, which side concurrency utilities load the work onto, who an executor pool hands a task to once it is full, the distinct identity count of virtual threads, and who runs the step in future-based chaining.

  1. 01 Threads and Lifecycle The same body runs on the caller with run(), on a thread created with start(); the difference is a single method name. Of three interruption setups, only one stops the body — the other two reach the same next step whether the interrupt call arrives or not.
  2. 02 Java Memory Model Of eight publication mechanisms, four ask both the writer and the reader to act, one puts the load on the writer alone (a final field), one on the reader alone (join), and two never ask the visibility question at all. The visibility defect of a field without synchronization is not shown by running it; it is modeled as a rule.
  3. 03 Synchronization Re-entering a monitor lock raises the hold count from 0 to 2 and then brings it back down. The same synchronized method called on the same instance uses a single lock; called on two separate instances, or on an instance method and a class method, two separate locks come into play — in two of the three setups, the lock count rises to two.
  4. 04 Concurrency Utilities Compare-and-set reaches its result in three attempts under a modeled contention and never leaves the caller for the whole operation. CountDownLatch and Semaphore block the waiter and have the other side drop the count; CopyOnWriteArrayList keeps each of four snapshots unchanged while the writer produces a new array on every change.
  5. 05 Executor Services The same execute call runs on its own thread when the pool is idle and on the calling thread when it is full; distinct thread identity count in a fixed pool stays bounded by the core count, and the rejection policy is a parameter chosen by the caller. An exception thrown via execute reaches the uncaught exception handler, while one thrown via submit only reaches the caller once get is called.
  6. 06 Virtual Threads For the same task set, distinct identity stays bounded at four in a fixed pool but grows with the task count in an executor that gives one virtual thread per task; a virtual thread is a real Thread distinguished with isVirtual(), not a platform thread; the carrier cannot be observed; and ThreadLocal keeping a separate value per virtual thread is a cost.
  7. 07 Asynchronous Composition Measures, in a future-based chain, which side runs each stage — the caller on a completed future, the completing side on a pending future, a pool we never created or a given one on an async-suffixed call — and that the same chain written four ways runs on four different sides while the result stays the same in all four. Shows that when a stage drops, the following stages are skipped and the error reaches the caller wrapped at join.

Final exam

JVM, Concurrency and Performance — Final Exam

In preparation

This course's question bank is not complete yet. The exam opens here automatically once enough questions are added.

Start typing to search.

↑↓ ↵ Esc navigate · open · close