When and why would you fork the compiler with meminitial/maxmem, and how does the plugin's incremental compilation work?
answer
- fork=true → separate javac JVM
- meminitial=-Xms, maxmem=-Xmx
- memory args ignored without fork
- incremental = recompile stale only
- clean fixes flaky partial builds
basics
~10 sSet <fork>true</fork> to run javac in a separate JVM, then size it with <meminitial> and <maxmem> for big modules. Incremental compilation (the default) only recompiles changed/stale classes instead of everything.
solid answer
~40 sBy default the compiler plugin runs javac in-process (in Maven's own JVM). With `<fork>true</fork>` it spawns a separate javac process, which lets you give the compiler its own heap via `<meminitial>` (-Xms) and `<maxmem>` (-Xmx), and pass custom `<compilerArgs>`/`<executable>` (a specific javac). You fork for very large modules that would otherwise blow Maven's heap, or to use a different JDK's javac than the one running Maven. Forking adds JVM-startup overhead so it's not free. Incremental compilation: since plugin 3.1, it tracks file state under target and recompiles only sources whose `.class` is missing or older, plus dependents, instead of the whole module. `<useIncrementalCompilation>` controls it; staleness/inputFileEnding detection has historically been imperfect, so flaky rebuilds are sometimes fixed with a clean.
code
xml · 6 lines<configuration>
<fork>true</fork>
<meminitial>128m</meminitial>
<maxmem>512m</maxmem>
<useIncrementalCompilation>true</useIncrementalCompilation>
</configuration>go deeper
Knows the compiler can run in a separate process and that not everything is recompiled each build.
Maps meminitial/maxmem to -Xms/-Xmx and knows incremental skips unchanged sources.
Decides when forking is justified (heap exhaustion, custom javac) and accepts its startup cost; knows incremental staleness limits.
Weighs build-capacity/performance trade-offs at scale, including when a module's size argues for forking, toolchains, or moving to Gradle.
## Forking the compiler By default `maven-compiler-plugin` invokes javac **in the same JVM that runs Maven**. Setting `<fork>true</fork>` runs javac as a **separate OS process**. Why fork: - **Heap isolation/sizing**: a huge module (tens of thousands of sources, or heavy annotation processors) can exhaust Maven's heap. A forked javac gets its own heap, sized by: - `<meminitial>` → javac's `-Xms` (initial heap), e.g. `128m`. - `<maxmem>` → javac's `-Xmx` (max heap), e.g. `512m`. - **Custom executable**: `<executable>` lets you point at a *specific* javac binary (forking is required for this), e.g. to compile with a different JDK than the one running Maven. - **Passing JVM/compiler args** that only make sense to a standalone compiler process. Cost: forking adds **JVM startup latency** per compile execution, so for small/normal modules it usually slows the build. Use it only when needed. ```xml <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <fork>true</fork> <meminitial>128m</meminitial> <maxmem>512m</maxmem> </configuration> </plugin> ``` Note: `<meminitial>`/`<maxmem>` are **ignored unless `<fork>true</fork>`** — they only apply to the separate process. ## Incremental compilation The plugin does **not** recompile everything every build. It compares source files to their existing outputs under `target/classes` and recompiles only those that are **stale** (output missing or older than source) plus affected dependents. Relevant config: - `<useIncrementalCompilation>` — enable/disable incremental behaviour (default true in modern versions). - `<staleMillis>` — granularity for the timestamp comparison. Limitation: staleness detection is timestamp-based and has historically had edge cases (deleted classes, changed dependencies, generated sources), so a confusing partial build is sometimes resolved with `mvn clean`. It is far less sophisticated than Gradle's incremental compiler, which is one reason teams move to Gradle for very large codebases. ## Putting it together Fork + memory tuning addresses *capacity* (can the compile run at all); incremental compilation addresses *speed* (don't redo unchanged work). They are independent knobs.
- Do <meminitial> and <maxmem> have any effect without forking?No. They size the separate javac process, so they are ignored unless <fork>true</fork> is set; in-process compilation shares Maven's own heap.
- What is a practical downside of fork=true for ordinary modules?It adds JVM startup overhead per compile execution, usually making small/normal builds slower.
saying these in an interview costs you the question
- Saying meminitial/maxmem tune Maven's heap directly — they only affect a forked javac process.
- Claiming forking always speeds up compilation — it adds startup cost.
- Believing incremental compilation is bulletproof; timestamp edge cases sometimes need a clean.