Chaturmind
LearnDSASystem DesignInterview PrepDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth
  • Java Interview Prep

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Java Interview Prep: 8+ Years (Senior & Lead)

Expert Core Java

  • Tricky Java Output, Operators & OOP Edge Cases — Interview Questions
  • Tricky Exceptions, Memory & Keyword Questions — Interview Questions
  • Classic Java Language Questions, Senior-Grade Answers — Interview Questions
  • Classic Collections, Threads & JDK APIs, Senior-Grade Answers — Interview Questions
  • Reflection, Dynamic Proxies, final & Modern OOP Design — Interview Questions

JVM Internals & Performance

  • Class Loading, Bytecode & Object Layout — Interview Questions
  • JIT Compilation & Runtime Optimisations — Interview Questions
  • Garbage Collectors Deep Dive — Interview Questions
  • JVM Tuning, GC Logs & Memory Footprint — Interview Questions
  • Memory Leaks, OutOfMemoryErrors & Profiling Tools — Interview Questions
  • Modules, Agents & Advanced JVM APIs — Interview Questions

Collections & Concurrency at Scale

  • Collections Internals & Complexity — Interview Questions
  • Iterators, Comparators & Ordering Contracts — Interview Questions
  • Concurrent Collections, Queues & Lock-Free Structures — Interview Questions
  • Threads, Executors & ForkJoin Internals — Interview Questions
  • Locks, Atomics, CAS & Synchronizers — Interview Questions
  • Java Memory Model, volatile, Fences & ThreadLocal — Interview Questions
  • Deadlock, Livelock, Starvation & Concurrent Design — Interview Questions
  • CompletableFuture, Parallel Streams & Non-Blocking I/O — Interview Questions

Modern Java (8 to 21+)

  • Lambdas & Functional Interfaces Internals — Interview Questions
  • Streams & Collectors Deep Dive — Interview Questions
  • Optional & Interface Default/Static Methods — Interview Questions
  • Java 9–25 Features & Virtual Threads — Interview Questions

Design Patterns, SOLID & Clean Code

  • Design Pattern Trade-offs & Combinations — Interview Questions
  • SOLID, Clean Code & Anti-Patterns — Interview Questions

Spring & Spring Boot Internals

  • IoC, Dependency Injection & Bean Lifecycle Internals — Interview Questions
  • Spring AOP, Proxies & @Async Internals — Interview Questions
  • Spring Configuration, Auto-Configuration & Custom Starters — Interview Questions
  • Spring MVC & REST Internals, Exception Frameworks — Interview Questions
  • Spring Security Advanced Internals — Interview Questions
  • Spring WebFlux, Reactor & R2DBC — Interview Questions
  • Spring Cloud, Observability & Distributed Tracing — Interview Questions
  • Spring Boot 3, Native Images & Production Scenarios — Interview Questions

JPA, Hibernate & Databases at Scale

  • Spring Data JPA — Queries, Projections, Custom Repositories & Locking — Interview Questions
  • JPA Entity Mapping, Associations & Cascades — Interview Questions
  • JPQL vs Native Queries in Depth — Interview Questions
  • Hibernate Caching — First-Level, Second-Level & Query Cache — Interview Questions
  • Lazy vs Eager Loading, LazyInitializationException & N+1 — Interview Questions
  • JPA Transactions, Propagation, Isolation & Dirty Checking — Interview Questions
  • SQL vs NoSQL, Indexing & Query Tuning — Interview Questions
  • Database Scaling, Replication, Pooling & Consistency Models — Interview Questions
  • Redis, Search, Time-Series, CDC & Transactional Data Modelling — Interview Questions

Testing Strategy & API Design

  • Spring Boot Test Slices, Context & Test Strategy — Interview Questions
  • Testing Web, Persistence, Security, Async & Messaging in Spring Boot — Interview Questions
  • JUnit 5 & Mockito, Advanced — Interview Questions
Chaturmind
← Java Interview Prep: 8+ Years (Senior & Lead)

Expert Core Java

  • Tricky Java Output, Operators & OOP Edge Cases — Interview Questions
  • Tricky Exceptions, Memory & Keyword Questions — Interview Questions
  • Classic Java Language Questions, Senior-Grade Answers — Interview Questions
  • Classic Collections, Threads & JDK APIs, Senior-Grade Answers — Interview Questions
  • Reflection, Dynamic Proxies, final & Modern OOP Design — Interview Questions

JVM Internals & Performance

  • Class Loading, Bytecode & Object Layout — Interview Questions
  • JIT Compilation & Runtime Optimisations — Interview Questions
  • Garbage Collectors Deep Dive — Interview Questions
  • JVM Tuning, GC Logs & Memory Footprint — Interview Questions
  • Memory Leaks, OutOfMemoryErrors & Profiling Tools — Interview Questions
  • Modules, Agents & Advanced JVM APIs — Interview Questions

Collections & Concurrency at Scale

  • Collections Internals & Complexity — Interview Questions
  • Iterators, Comparators & Ordering Contracts — Interview Questions
  • Concurrent Collections, Queues & Lock-Free Structures — Interview Questions
  • Threads, Executors & ForkJoin Internals — Interview Questions
  • Locks, Atomics, CAS & Synchronizers — Interview Questions
  • Java Memory Model, volatile, Fences & ThreadLocal — Interview Questions
  • Deadlock, Livelock, Starvation & Concurrent Design — Interview Questions
  • CompletableFuture, Parallel Streams & Non-Blocking I/O — Interview Questions

Modern Java (8 to 21+)

  • Lambdas & Functional Interfaces Internals — Interview Questions
  • Streams & Collectors Deep Dive — Interview Questions
  • Optional & Interface Default/Static Methods — Interview Questions
  • Java 9–25 Features & Virtual Threads — Interview Questions

Design Patterns, SOLID & Clean Code

  • Design Pattern Trade-offs & Combinations — Interview Questions
  • SOLID, Clean Code & Anti-Patterns — Interview Questions

Spring & Spring Boot Internals

  • IoC, Dependency Injection & Bean Lifecycle Internals — Interview Questions
  • Spring AOP, Proxies & @Async Internals — Interview Questions
  • Spring Configuration, Auto-Configuration & Custom Starters — Interview Questions
  • Spring MVC & REST Internals, Exception Frameworks — Interview Questions
  • Spring Security Advanced Internals — Interview Questions
  • Spring WebFlux, Reactor & R2DBC — Interview Questions
  • Spring Cloud, Observability & Distributed Tracing — Interview Questions
  • Spring Boot 3, Native Images & Production Scenarios — Interview Questions

JPA, Hibernate & Databases at Scale

  • Spring Data JPA — Queries, Projections, Custom Repositories & Locking — Interview Questions
  • JPA Entity Mapping, Associations & Cascades — Interview Questions
  • JPQL vs Native Queries in Depth — Interview Questions
  • Hibernate Caching — First-Level, Second-Level & Query Cache — Interview Questions
  • Lazy vs Eager Loading, LazyInitializationException & N+1 — Interview Questions
  • JPA Transactions, Propagation, Isolation & Dirty Checking — Interview Questions
  • SQL vs NoSQL, Indexing & Query Tuning — Interview Questions
  • Database Scaling, Replication, Pooling & Consistency Models — Interview Questions
  • Redis, Search, Time-Series, CDC & Transactional Data Modelling — Interview Questions

Testing Strategy & API Design

  • Spring Boot Test Slices, Context & Test Strategy — Interview Questions
  • Testing Web, Persistence, Security, Async & Messaging in Spring Boot — Interview Questions
  • JUnit 5 & Mockito, Advanced — Interview Questions
HomeLearnJava Interview PrepJava Interview Prep: 8+ Years (Senior & Lead)JVM Internals & Performance
✓ FreeAdvanced· 13 min read

Modules, Agents & Advanced JVM APIs — Interview Questions

JPMS in practice (modularising a monolith, architectural impact, enterprise realities, security and maintainability claims corrected), what Java 9 changed, explaining the JVM to new developers, hidden classes, Java agents and java.lang.instrument, the Compiler API, decompilers and protecting code, sun.misc.Unsafe and its replacements (VarHandle, FFM API), MethodHandles, and real-world Java performance optimisations.

Published September 25, 2026


How to use this lesson

These questions separate people who have read about Java 9+ from people who have shipped it. Be honest about JPMS adoption:

  • Strong encapsulation of the JDK affected everyone.
  • Application-level modules are adopted selectively. Most Spring Boot applications still run on the classpath.

Also know the supported replacements for internal APIs like Unsafe.

Q1. How would you use the Java module system to modularise a monolithic application?

Short answer: Do it incrementally, and only where the boundaries pay off:

  1. Find the boundaries first: domain modules (orders, billing, catalogue) with acyclic dependencies. Enforce them on the classpath first, with ArchUnit, Spring Modulith, or Maven or Gradle subprojects. Most of the value comes from here.
  2. Adopt JPMS bottom-up. Put your JARs on the module path. Libraries without descriptors become automatic modules, named from the Automatic-Module-Name manifest entry or the JAR name.
  3. Write a module-info.java for each module:
    • requires for dependencies (requires transitive for APIs you re-expose);
    • exports only the API packages;
    • opens packages to reflection-based frameworks (Spring, Hibernate, Jackson) that need deep access;
    • uses/provides for ServiceLoader-based plugins.
  4. Fix the problems that come up: split packages (the same package in two JARs is illegal), cyclic dependencies, and reflective access to internals (--add-opens as a temporary measure, then refactor).
  5. Use jdeps to analyse dependencies, and jlink to build a trimmed runtime image.
module com.shopin.orders {
    requires com.shopin.catalog.api;           // compile- and run-time dependency
    requires transitive com.shopin.common;     // our API exposes types from common
    requires spring.context;
    exports com.shopin.orders.api;             // the only public surface
    opens com.shopin.orders.internal.persistence to org.hibernate.orm.core, spring.core;
    provides com.shopin.common.spi.EventHandler with com.shopin.orders.internal.OrderEventHandler;
}

Q2. How does the module system affect application architecture?

Short answer:

  • Reliable configuration: missing modules are detected at startup, and dependencies are explicit and acyclic. No more classpath "JAR hell" surprises at runtime.
  • Strong encapsulation: public no longer means "accessible to everyone". Only exported packages are visible, so internal packages really are internal. That enables safe refactoring of internals.
  • Service-oriented plugins through provides/uses, without reflection hacks.
  • Custom runtimes with jlink: smaller containers, and a smaller attack surface.

Its impact in practice:

  • The JDK's own modularisation (strong encapsulation of internals, fully enforced since Java 17) forced libraries to stop using sun.* internals.
  • Application teams more often get the architectural benefits from build-level modules plus architecture tests, because frameworks, reflection and split packages make full JPMS adoption costly.

Q3. What are Java modules, and how do they improve security and maintainability?

Short answer: A module is a named, self-describing unit (module-info.class) that declares what it needs (requires) and what it offers (exports, provides).

  • Security: internal packages are inaccessible, even by reflection, unless explicitly opened. That reduces the attack surface: a lot of past exploits abused JDK internals. jlink images include only the modules you need.
  • Maintainability: explicit contracts, enforced boundaries, and compile-time detection of missing or illegal dependencies.

Common trap: the source claims the JVM "loads only required modules, improving startup and memory". For classpath applications, nothing changes: classes are still loaded lazily, on first use. The real footprint win comes from jlink custom runtime images, and from CDS.

Q4. How does the module system affect large enterprise applications?

Short answer:

  • Benefits:
    • Enforced boundaries across teams.
    • Explicit API surfaces for shared libraries.
    • Earlier detection of dependency conflicts.
    • jlink images for leaner deployments.
    • Clean plugin architectures with ServiceLoader.
  • Realities:
    • Many frameworks rely on deep reflection, so modules need opens.
    • Split packages from legacy libraries.
    • Build and tooling complexity (tests on the module path, and --add-reads/--add-opens flags for test frameworks).
    • JPMS doesn't handle versions: you can't load two versions of a module side by side without module layers.
    • Most enterprise Spring applications use classpath + Spring Modulith/ArchUnit, and get the modularity benefits with less friction.
  • The upgrade impact everyone felt: strong encapsulation of JDK internals (Java 16/17) broke old libraries (for example, old Lombok, Groovy and Mockito versions). Enterprise upgrades require dependency updates, and sometimes --add-opens flags.

Q5. What were the major changes in Java 9, and how did they affect development?

Short answer:

  • JPMS (Jigsaw): a modular JDK, module-info, and jlink.
  • JShell: a REPL for quick experiments.
  • Collection factory methods: List.of, Set.of, Map.of (immutable).
  • API improvements:
    • Streams got takeWhile, dropWhile, ofNullable and iterate-with-predicate.
    • Optional got ifPresentOrElse, or and stream.
    • CompletableFuture got timeouts (orTimeout, completeOnTimeout) and delayedExecutor.
  • Private interface methods.
  • Reactive Streams interfaces in java.util.concurrent.Flow.
  • ProcessHandle, a better process API.
  • Stack-Walking API.
  • HTTP/2 client (incubating, finalised in Java 11 as java.net.http.HttpClient).
  • VarHandles (replacing many Unsafe uses).
  • Compact strings (Latin-1 storage).
  • G1 becomes the default GC.
  • Multi-release JARs.
  • Unified JVM logging (-Xlog).
  • A new release cadence (every six months from Java 10, with LTS releases every two years since 17).

The impact: upgrade projects (JDK internals, removed Java EE modules in Java 11), cleaner APIs, and quicker experimentation. Practically, the six-month cadence changed how organisations plan upgrades: LTS to LTS (11 → 17 → 21 → 25).

Q6. How would you explain the JVM's role in running a simple Java program to new developers?

Short answer: Walk them through java Hello:

  1. javac compiles Hello.java into bytecode (Hello.class): instructions for a virtual CPU, the same on every OS.
  2. java starts the JVM, which loads the class (class loaders), verifies the bytecode, and links and initialises it.
  3. It calls main on the main thread. Its stack holds the local variables, and objects go on the heap.
  4. The execution engine first interprets the bytecode. Then the JIT compiles hot methods to native code, which is why Java gets faster as it warms up.
  5. The garbage collector frees objects that are no longer reachable, so there's no manual free.
  6. When main returns (and no non-daemon threads remain), the JVM runs its shutdown hooks and exits.

The payoff is portability ("write once, run anywhere"), safety (verification, no pointer arithmetic), and automatic memory management.

Q7. What is a hidden class (Java 15), and what's it used for?

Short answer: A class created with MethodHandles.Lookup.defineHiddenClass(bytes, ...) that:

  • isn't discoverable by name: it's not registered with any class loader, so it can't be linked from other classes' bytecode or found through Class.forName;
  • can be unloaded independently when unreachable, even if its defining loader is still alive (optionally strongly tied to the loader);
  • lives in the lookup class's nest and package, so it can access privately what the framework allows.

It's used by framework-generated code: lambda proxy classes (LambdaMetafactory), dynamic proxies, and bytecode-generating libraries (ByteBuddy, and language runtimes). It replaced the internal Unsafe.defineAnonymousClass, which was removed in Java 17. The benefits are less Metaspace retention (fewer classloader leaks) and no need for dedicated loaders.

Common trap: "hidden classes are inaccessible to reflection". You can reflect on a hidden class if you have its Class object. It just can't be looked up by name or referenced symbolically.

Q8. How does a Java agent work, and what is java.lang.instrument for?

Short answer: An agent is a JAR with a premain method (started with -javaagent:agent.jar) or an agentmain method (attached to a running JVM through the Attach API). The JVM hands it an Instrumentation instance. With java.lang.instrument, it can:

  • register ClassFileTransformers, which rewrite bytecode as classes load (usually with ASM or ByteBuddy);
  • retransform or redefine already-loaded classes (method bodies only: no adding fields or methods);
  • query object sizes (getObjectSize), append JARs to class loader search paths, and so on.

Uses:

  • APM and tracing (the OpenTelemetry Java agent, New Relic, Datadog);
  • profilers;
  • code coverage (JaCoCo);
  • mocking frameworks (Mockito's inline mock maker);
  • hot-swapping tools.

Key points to cover:

  • Dynamic attachment of agents to a running JVM prints warnings since Java 21 (JEP 451), and will be disallowed by default in future. Enable it explicitly with -XX:+EnableDynamicAgentLoading, or load agents at startup.
public final class TimingAgent {
    public static void premain(String args, Instrumentation inst) {
        new AgentBuilder.Default()                                       // ByteBuddy
            .type(ElementMatchers.nameStartsWith("com.shopin.service"))
            .transform((b, type, loader, module, pd) -> b.visit(Advice.to(TimingAdvice.class).on(ElementMatchers.isMethod())))
            .installOn(inst);
    }
}

Q9. What is the Java Compiler API used for?

Short answer: javax.tools.JavaCompiler (from ToolProvider.getSystemJavaCompiler()) compiles Java source programmatically, with in-memory file managers, diagnostics collection and classpath control. Uses:

  • runtime code generation: rule engines, templating that compiles to Java, online judges and education platforms;
  • IDEs and build tools;
  • testing annotation processors (Google's compile-testing);
  • plugins that compile user-supplied code.

Key points to cover:

  • It needs a JDK at runtime (not just a jlinked runtime without jdk.compiler).
  • Compiling untrusted source is dangerous, because the code runs with your privileges. Isolate it in a separate process or container.
  • Related tools: the Class-File API (Java 24) for generating bytecode directly, and annotation processors (javax.annotation.processing) for build-time generation.

Q10. What is a Java decompiler, and how do you use one securely? How do you protect your own code?

Short answer: A decompiler (CFR, Fernflower/Vineflower, Procyon, JD-GUI) reconstructs Java-like source from bytecode. It's easy, because bytecode keeps names and structure. Legitimate uses:

  • debugging third-party libraries without sources (IntelliJ does this automatically);
  • recovering lost code;
  • security review of dependencies;
  • understanding a regression.

Using it responsibly:

  • respect licences (many forbid reverse engineering);
  • use trusted tools from official sources;
  • don't expose decompiled proprietary code;
  • remember that the artefacts you decompile could themselves be malicious (open them in a sandbox).

Protecting your code:

  • Don't ship secrets in code. Anything in a JAR is readable, obfuscated or not.
  • Keep sensitive logic server-side.
  • Obfuscators (ProGuard/R8, or commercial tools) raise the effort needed, but aren't real security.
  • GraalVM native images or licensing and attestation schemes add further hurdles.

Q11. How does sun.misc.Unsafe work, what is it used for, and what replaces it?

Short answer: Unsafe is an internal JDK class that exposes raw operations:

  • off-heap memory (allocateMemory, putLong at addresses);
  • field access by offset, bypassing access checks and final;
  • CAS and memory fences;
  • allocating instances without constructors (allocateInstance);
  • park/unpark;
  • (formerly) defining anonymous classes.

High-performance libraries (Netty, Cassandra, Kryo, Chronicle, LMAX Disruptor) used it for speed. It's dangerous: a wrong address can crash the JVM or silently corrupt memory, and it bypasses every Java safety guarantee.

The supported replacements:

  • VarHandle (Java 9): atomic operations, CAS and ordered or opaque memory access on fields and array elements.
  • The Foreign Function & Memory API (final in Java 22): safe, deterministic off-heap memory (Arena, MemorySegment), and native calls, replacing JNI too.
  • MethodHandles.Lookup.defineHiddenClass, instead of defineAnonymousClass.
  • LockSupport, instead of park.

The status: Unsafe's memory-access methods are deprecated for removal (JEP 471, Java 23), and emit runtime warnings from Java 24 (JEP 498), on the way to being removed.

Q12. What is a MethodHandle?

Short answer: A typed, directly executable reference to a method, constructor, field accessor or composed operation (java.lang.invoke, Java 7):

  • It's created through a MethodHandles.Lookup, which checks access once, at lookup time.
  • It can be adapted and combined: bind arguments, filter, insert guards, catch exceptions.
  • It's invoked with invokeExact (the types must match exactly) or invoke (with conversions).

Why it matters:

  • The JIT can inline calls through constant method handles (static final), so they're close to direct calls.
  • It's the foundation of invokedynamic: lambdas, string concatenation, records, pattern-matching switches.
  • Since Java 18, core reflection is implemented on method handles.
private static final MethodHandle TOTAL;
static {
    try {
        TOTAL = MethodHandles.lookup().findVirtual(Order.class, "total", MethodType.methodType(BigDecimal.class));
    } catch (ReflectiveOperationException e) { throw new ExceptionInInitializerError(e); }
}
BigDecimal t = (BigDecimal) TOTAL.invokeExact(order);     // types checked at call time; cast required for invokeExact

Common trap: saying method handles are "type-checked at compile time". They're signature-polymorphic. The call site's type is checked at invocation (WrongMethodTypeException), not by javac.

Q13. What performance optimisations have you done in your Java projects?

Short answer: Answer with measured, specific stories (STAR, with numbers). Good examples:

  • Database:
    • Removed an N+1 with JOIN FETCH/entity graphs, and added a covering index: p95 dropped from 1.2 s to 90 ms.
    • Switched a report to keyset pagination and streaming.
    • Batched inserts (hibernate.jdbc.batch_size, reWriteBatchedStatements).
  • Caching: a Caffeine L1 plus Redis L2 for the product catalogue, with explicit invalidation. Hit ratio 94%, and database CPU down 60%.
  • Allocation and GC:
    • Found a hot String.format and boxing in a pricing loop with JFR. Replaced them with StringBuilder/primitives, and allocation rate halved, so young GCs went down by 50%.
    • Removed a System.gc() from a library.
  • Concurrency:
    • Replaced a globally synchronised map with ConcurrentHashMap.computeIfAbsent.
    • Moved blocking I/O off the common ForkJoin pool.
    • Adopted virtual threads for an I/O-heavy fan-out service (thread count from 800 to about 30 carriers, and throughput ×3).
  • I/O: HTTP connection pooling and keep-alive, gzip, and bulk APIs instead of chatty calls.
  • JVM: fixed a container running Serial GC (1 CPU); right-sized MaxRAMPercentage; added a CDS/AOT cache (startup from 9 s to 4 s).
  • Method: always profile first (JFR, async-profiler), benchmark with JMH, and load-test before and after.

Follow-up questions this topic invites — and their answers

Q: What is jdeps used for? A: It analyses class and JAR dependencies, both package-level and on JDK internals (--jdk-internals). It can generate module-info.java skeletons (--generate-module-info), and helps plan JDK upgrades and jlink module lists (--print-module-deps).

Q: What does --add-opens do, and why is it a smell? A: It opens a package of a module to deep reflection by another module (or the unnamed classpath module), bypassing strong encapsulation. It's needed for some legacy libraries, but it signals dependence on internals that may break in future JDKs. Upgrade the library instead, where possible.

Q: What are module layers? A: ModuleLayer lets an application create new sets of modules at runtime, with their own class loaders. That enables plugin systems, and even different versions of the same module in different layers.

Q: What is a multi-release JAR? A: A JAR with Multi-Release: true, and version-specific classes under META-INF/versions/N/. A Java 9+ runtime picks the newest versions it supports, which lets libraries use new APIs while still supporting old JDKs.

Previous

Memory Leaks, OutOfMemoryErrors & Profiling Tools — Interview Questions

Next

Collections Internals & Complexity — Interview Questions

AI Tutor

Lesson: Modules, Agents & Advanced JVM APIs — Interview Questions

Quick actions

AI responses can be inaccurate. Verify critical information.