Loading...
Loading...
Java interview guide
Class loading, memory areas, garbage collection and performance basics. Every answer is short enough to say aloud, and every question links to the lesson that teaches it.
61 questionsLast updated
this stored when an instance method runs?In slot 0 of the method's local variable array. The caller passes the reference to the object as a hidden first argument, and instructions like aload_0 followed by getfield use it to read fields on the heap.
Not in the Java language: new always means the heap. The HotSpot JIT can use escape analysis to replace an object that never leaves a method with plain local values, so no heap allocation happens. That is an optimisation you cannot control or observe in the program's results.
Stack space for locals costs almost nothing, because it comes with the frame. Heap allocation in HotSpot is also quick, a pointer bump in the thread's TLAB. The heap's real cost is garbage collection later, so creating fewer short-lived objects in hot code helps more than worrying about the allocation itself.
Lesson: Heap versus stackNot necessarily. The method's frame and its local reference are gone, but the object stays on the heap if anything else still refers to it, such as the caller's variable, a field or a collection. It becomes garbage only when nothing can reach it.
Lesson: Heap versus stack-Xss do, and when would you change it?It sets the stack size of new threads; the default is 1 MB on 64-bit HotSpot for Windows and Linux. I would raise it only for a known, bounded deep recursion, such as a parser on trusted input, and lower it rarely, to save native memory with very many threads. If the depth grows with the input, I fix the code instead.
Lesson: Each thread's own stackEventually the JVM cannot get another operating-system thread or native stack and throws OutOfMemoryError: unable to create native thread, even with plenty of free heap. Before that, switching between thousands of threads slows the machine down. The fix is a bounded thread pool, or virtual threads on Java 21 for blocking work.
Metaspace is where HotSpot keeps class metadata since Java 8. It is native memory outside the heap, has no limit by default and grows as classes are loaded. PermGen, used up to Java 7, had a fixed maximum set by -XX:MaxPermSize and was managed together with the heap, so it often ran out with OutOfMemoryError: PermGen space.
On HotSpot since Java 7, their values are stored inside the class's java.lang.Class object, which is on the heap. The field's name and type are part of the class metadata in Metaspace. If the static field is a reference, the object it points to is an ordinary heap object.
== sometimes work for strings and sometimes not?== compares references. Two equal literals refer to the same pooled object, so == is true, but a string built at run time, read from input or made with new is a different object. That is why text must always be compared with equals.
StackOverflowError and OutOfMemoryError?StackOverflowError means one thread's stack has no room for another frame, usually from runaway recursion. OutOfMemoryError means some other memory area is exhausted, and its message says which: Java heap space, Metaspace, unable to create native thread and so on. Both are Errors, not exceptions.
StackOverflowError from deep recursion on large input?I would turn the recursion into a loop, or use an explicit stack such as an ArrayDeque on the heap, so the depth no longer uses frames. A bigger -Xss is acceptable only when the maximum depth is known and small enough. If the recursion never ends, the real fix is the missing base case.
StackOverflowError crash the whole application?The error affects only the thread whose stack overflowed; if it is not caught, that thread ends and others keep running. But if that thread was the main thread or held important work or locks, the application may stop working correctly, so it still has to be fixed.
Lesson: StackOverflowError and OutOfMemoryError: which area ran outString b = a; copy the string?No, it copies the reference, so both variables point to one object and a == b is true. Because String is immutable, any "change" such as b = b + "x" creates a new object and moves b, which is why a looks unaffected.
OutOfMemoryError. What do you check first?The rest of the message, because it names the area: Java heap space or GC overhead limit exceeded means the heap, so I look at a heap dump for what keeps objects reachable; Metaspace means classes and class loaders; unable to create native thread means too many threads. Only after that do I decide whether a setting or the code must change.
OutOfMemoryError in a test?I would run the code in its own JVM with a small heap, such as -Xmx16m, and feed it enough data to exceed it. The program itself stays bounded, and the small limit makes the failure fast and repeatable. I would never do it inside a shared test JVM, because the error can break unrelated code there.
Aliasing is having two references to the same object, so a change through one is seen through the other. I avoid surprises by making defensive copies when I store or return arrays and lists, by using immutable types such as String, records and List.of, and by creating a new object in each loop iteration when each slot needs its own.
System.gc() help when the heap is full?No. The JVM already collects before it throws OutOfMemoryError, and System.gc() can only free unreachable objects. If a list or map still refers to the data, it stays; the fix is to drop those references or bound the collection.
new Object() take, and why?About 16 bytes on 64-bit HotSpot with defaults. It has no fields, so it is just the 12-byte header, and alignment to 8 bytes adds 4 bytes of padding. Other JVMs or settings can give other numbers, so I call it approximate.
Lesson: What an object looks like in memoryCompressed oops are 4-byte object references on a 64-bit JVM. Because objects are aligned to 8 bytes, the JVM stores the address divided by 8, which can reach 2³² × 8 bytes, about 32 GB. HotSpot turns them on by default when the maximum heap is below about 32 GB; above that every reference takes 8 bytes.
Lesson: What an object looks like in memoryUsually not, because HotSpot chooses the field layout itself and packs small fields into gaps, for example after the 12-byte header. What changes the size is the types of the fields, so using an int instead of a long or a char instead of a String is what really saves memory.
Runtime.totalMemory() and freeMemory()? What are the limits?Yes, the difference is the heap in use right now, so the growth around a block of code gives a rough cost. It is noisy: uncollected garbage is counted, System.gc() is only a request, and other threads allocate too. I measure many objects, keep them reachable and repeat the run.
JOL, Java Object Layout, is an OpenJDK library that asks the running JVM how it lays out objects: field offsets, header size, padding and total. I would use it when designing memory-heavy classes, to check that a change such as long to int really shrinks the object on my JDK and flags.
int[] and Integer[] in memory?int[] holds the numbers themselves, 4 bytes each, in one object. Integer[] holds 4-byte references to separate Integer objects of about 16 bytes each, so for distinct values it takes about 5 times the memory, creates many objects for the GC, and its slots can be null.
Not in the C sense. int[3][4] is an outer array of 3 references to separate int[4] arrays. That allows jagged rows and null rows, but each row has its own header and the rows may not sit next to each other in memory.
Integer a = 127, b = 127; a == b true but the same with 128 false?Autoboxing calls Integer.valueOf, which returns shared cached objects for -128 to 127. For 127 both variables get the same object, so ==, which compares references, is true. 128 is outside the cache, so each boxing makes a new object and == is false.
Byte caches all its values, Short and Long cache -128 to 127, Character caches 0 to 127, and Boolean boxing always returns Boolean.TRUE or Boolean.FALSE. Float and Double have no cache.
Integer x = null; int y = x;?It compiles, but throws NullPointerException at run time. Unboxing becomes x.intValue(), and calling a method on null fails. The same happens in x == 0 or x + 1 when x is null.
It is a table of shared String objects, one per distinct text, used for every string literal and every interned string. Since Java 7 it lives in the normal heap on HotSpot, so pooled strings nobody uses can be garbage collected. Before that it was in PermGen.
Since Java 9 a String stores its characters in a byte[] plus a coder flag. If all characters fit in Latin-1 it uses 1 byte per character; otherwise UTF16 with 2 bytes per character. Because most strings are plain ASCII, this roughly halves the character data compared with the old char[].
No. Interning is something your code calls, and it makes equal strings the same String object. Deduplication is a GC feature turned on with -XX:+UseStringDeduplication; it only makes equal strings share one byte[], so the objects stay different and == stays false. It needs no code change.
HashMap<Integer, Integer> entry take?About 70 to 75 bytes on 64-bit HotSpot with defaults: a 32-byte Node (hash, key, value, next), a 16-byte key Integer, a 16-byte value Integer, and a few bytes of table, since the table stays at most 75% full. The real data is only 8 bytes.
Replace wrapper fields with primitives, use the smallest safe types, replace repeated String values with an enum or shared instances, and remove fields that can be computed. If it is still too big, I would store the data column-wise in parallel primitive arrays. I would check each change with a class histogram or JOL.
int field take?About 16 bytes on 64-bit HotSpot with default settings: a 12-byte header (8-byte mark word and 4-byte compressed class pointer) plus the 4-byte int, which is already a multiple of 8. The exact number depends on the JVM and flags, so I call it approximate.
Byte caches all values, Short, Integer and Long cache -128 to 127, Character caches 0 to 127, and Boolean always uses TRUE and FALSE. Float and Double have no cache. Only Integer's upper bound can be raised, with -XX:AutoBoxCacheMax on HotSpot.
intern() in real code?Only when a program keeps very many long-lived copies of a small set of strings, such as city or status values read from millions of rows; then interning, or my own HashMap pool, keeps one copy of each. I would not intern unique values, and I would never intern just to compare with ==.
When no live part of the program can reach it: no local variable in a running method, no static field and no other reachable object refers to it. That happens when its last reference is reassigned, set to null, or goes away as a method returns. Eligible means it may be collected later, not that it is freed at once.
Yes. The collector frees only unreachable objects. If the program keeps references it no longer needs, for example in a list that only grows, the memory stays in use and the program can end with an OutOfMemoryError.
They are the references the collector treats as live without proof: local variables and parameters in the frames of running methods, static fields of loaded classes, live threads, and JNI references from native code (plus some JVM-internal ones). Every object the program can use is reachable from at least one of them.
Lesson: Reachability and GC rootsYes. Tracing starts from roots, and no root leads to the pair, so neither is marked and both are reclaimed. The cycle only matters for reference counting, which HotSpot does not use.
Lesson: Reachability and GC rootsNo. Only references that lead to an object from a root keep it alive. References going out of a garbage object are ignored, and the live objects it points at stay alive only through their own paths from roots.
Lesson: Reachability and GC rootsFragmentation means the free memory is split into many small gaps between live objects. A large object, such as a big array, needs one continuous block, so it may not fit even when the total free space is large. Allocation also gets slower because the allocator must search for a gap.
Lesson: Mark, sweep and compactNo. HotSpot's copying and compacting collectors move objects and update every reference to them, which is one reason Java has no pointer arithmetic. The identity hash code does not change when an object moves, because once computed it is stored in the object header.
Lesson: Mark, sweep and compactBecause every reference to a moved object must be updated, and the program could otherwise read or write an object at its old address in the middle of the move. Simple collectors stop the program for this; advanced ones such as ZGC use extra tricks to move objects while the program runs.
Lesson: Mark, sweep and compactEden is where new objects are created. The two survivor spaces hold objects that survived young collections: at each young GC, live objects from eden and one survivor space are copied into the other, empty survivor space, and then the two swap roles. One survivor space is always empty.
Lesson: Young and old generationsIn G1 it is an object of at least half a region, usually a large array. It skips eden and gets one or more whole regions, so the unused part of the last region is wasted, and many of them can cause extra collections. Reusing buffers or processing data in smaller chunks avoids them.
Lesson: Young and old generationsNo, it is an observation about typical programs, not a rule. A program that builds a big cache at start-up and keeps it, or one that holds objects for a medium time, gets less benefit, and some of those objects are copied several times before promotion. For most servers and apps, though, it holds well.
Lesson: Young and old generationsBecause G1 is designed to clean the heap with short young and mixed pauses; a full GC means it ran out of room first and had to stop everything. Common causes are a heap that is too small for the live data, a memory leak, very fast promotion or many humongous objects, and explicit System.gc() calls.
A remembered set records which places in other regions hold references into a region. During a young GC, old-to-young references from it act as extra roots, so the collector does not scan the whole old generation. The write barrier is the small piece of code the JIT adds to every reference store that keeps those records up to date.
Lesson: Minor, mixed and full collectionsIt is a period when all application threads are stopped so the collector can work alone. The collector needs stable roots and an object graph that is not changing, especially while it moves objects and updates references. Without the pause, a thread could follow a reference to an object that is halfway through being moved.
Lesson: Stop-the-world pausesYes. The GC line shows the work done at the safepoint, but threads first have to reach it, and that time to safepoint is added on top. A thread in a long loop without a poll can delay everyone; -Xlog:safepoint shows "Reaching safepoint" and the total time the threads were stopped.
Booking users wait on each request, so latency matters most. First I would read the GC log to see which pauses are long and why: full GCs, premature promotion, or a large young generation copying a lot. Then I would reduce allocation or fix the cause, and only after that consider a lower G1 pause goal or a low-latency collector such as ZGC, measuring before and after.
Lesson: Stop-the-world pausesIt is a group of objects that refer to each other but that no root can reach, such as two Room and Student objects pointing at each other after the last outside reference is dropped. Because Java traces from roots instead of counting references, the whole island is collected.
System.gc()?Almost never. It is only a request, and on HotSpot with G1 it usually triggers a full stop-the-world GC, which is the most expensive kind. Benchmarks and demos sometimes use it to settle memory numbers, but production code should leave the timing to the collector.
Lesson: Garbage collection review: trace the garbageBoth, depending on the phase and the collector. In G1, young and mixed evacuations and the Remark and Cleanup steps stop all threads at a safepoint, while old-generation marking runs concurrently. Collectors like ZGC move even more work into concurrent phases to keep pauses very short.
Lesson: Garbage collection review: trace the garbagenew to being reclaimed.It is bump-allocated in eden, inside the creating thread's TLAB. It becomes unreachable soon after, so at the next young collection it is simply not copied, and its space in eden is reused as a whole. No work is spent on it at all, which is why short-lived objects are so cheap in Java.
Lesson: Garbage collection practiceLook at the heap value after each full GC. If it stays flat, the live data is steady and the heap just needs more room. If it keeps rising from one full GC to the next, objects are piling up that should have been released: a leak.
Lesson: Garbage collection practiceThey are much rarer, because collections are mostly triggered by allocation. But other threads, JVM-internal work and explicit System.gc() calls can still trigger them, and all threads stop at safepoints for some other JVM operations too.
Both are generational and fully stop-the-world. Serial does the GC work on one thread, which suits small heaps and single-CPU machines. Parallel splits the same work across many threads and aims for the best throughput, so it suits batch jobs.
Lesson: Serial, Parallel, G1 and ZGC at a glanceWhen users wait on every request and G1's pauses, often tens of milliseconds and up to its 200 ms goal, are too long, especially with large heaps of many gigabytes. ZGC keeps pauses well below a millisecond, at the cost of some CPU and memory. On Java 21 I would also add -XX:+ZGenerational, and I would measure both under real load before switching.
CMS (Concurrent Mark Sweep) was an older low-pause collector. It was deprecated in Java 9 and removed in Java 14, mainly because G1 covers the same needs and CMS was hard to maintain. Today you would use G1 or ZGC instead.
Lesson: Serial, Parallel, G1 and ZGC at a glanceIt controls how much work goes into one pause. After every pause it measures the cost and resizes eden for the next young collection, and in mixed collections it adds only as many old regions as the goal allows. The goal, -XX:MaxGCPauseMillis, is 200 ms by default and is a target, not a guarantee.
Each lesson has quizzes, practice and more interview questions on the same topic.
Open the first lesson