Maven, Git, Docker, kubectl, JDK diagnostic tools, Actuator endpoints, a multi-stage Dockerfile and JVM container flags.
Maven
| Command |
Effect |
mvn clean verify |
clean, compile, test, package, integration tests (failsafe) |
mvn -q -DskipTests package |
build jar without running tests (still compiles tests; -Dmaven.test.skip=true skips compiling too) |
mvn test -Dtest=FooTest#bar |
single test / method |
mvn -pl api -am package |
build module api and the modules it depends on |
mvn -T 1C package |
parallel build, one thread per core |
mvn dependency:tree -Dincludes=org.slf4j |
why a dependency is present / conflicts |
mvn dependency:analyze |
unused-declared and used-undeclared |
mvn versions:display-dependency-updates |
outdated dependencies |
mvn help:effective-pom |
resolved POM |
mvn -o ... / -U |
offline / force snapshot update |
mvn spring-boot:run |
run app (-Dspring-boot.run.profiles=dev) |
mvn spring-boot:build-image |
OCI image via Buildpacks (no Dockerfile) |
./mvnw ... |
wrapper, pins Maven version |
- Lifecycle:
validate, compile, test, package, verify, install, deploy; running a phase runs all earlier phases.
- Scopes:
compile (default), provided, runtime, test, import (BOM, in dependencyManagement).
- Surefire runs
*Test; Failsafe runs *IT in integration-test / verify.
- Gradle equivalents:
./gradlew build, test --tests Foo, dependencies, bootRun, bootBuildImage.
Git
git switch -c feature/x # create + switch (restore/switch replace checkout in 2.23+)
git add -p # stage hunks interactively
git commit --amend --no-edit # fold staged changes into last commit
git fetch --prune && git rebase origin/main
git rebase -i HEAD~4 # squash/reword/reorder (rewrites history; never on shared branches)
git push --force-with-lease # safer than --force
git stash push -m "wip" -u # include untracked; git stash pop
git cherry-pick <sha> # copy a commit; -x records origin
git revert <sha> # new commit that undoes (safe on shared branches)
git reset --soft HEAD~1 # keep changes staged; --mixed (default) unstaged; --hard discards
git restore --staged <file> # unstage; git restore <file> discards working changes
git reflog # recover "lost" commits, then git branch rescue <sha>
git bisect start; git bisect bad; git bisect good <sha> # binary search for a regression
| Need |
Command |
| Who changed a line |
git blame -L 10,20 file |
| Find commit by message / code |
git log --grep=text, git log -S'symbol' --oneline |
| Compact graph |
git log --oneline --graph --decorate --all |
| Diff staged / vs branch |
git diff --staged, git diff main...HEAD |
| Undo merge in progress |
git merge --abort / git rebase --abort |
| Tag release |
git tag -a v1.2.0 -m "msg" && git push origin v1.2.0 |
| Parallel checkouts |
git worktree add ../hotfix hotfix |
- Merge keeps history (merge commit); rebase rewrites onto a new base for linear history; squash-merge collapses a branch to one commit.
HEAD~n = n-th ancestor along first parent; HEAD^2 = second parent of a merge.
Docker
| Command |
Effect |
docker build -t app:1.0 . |
build (BuildKit default since 23.0) |
docker run -d --name app -p 8080:8080 -e SPRING_PROFILES_ACTIVE=prod --memory=512m app:1.0 |
run detached |
docker ps -a / docker logs -f --tail 100 app |
list / follow logs |
docker exec -it app sh |
shell in running container |
docker stop app && docker rm app |
stop, remove |
docker images / docker rmi / docker system prune -af |
images / remove / clean everything unused |
docker inspect app / docker stats |
config / live CPU, memory |
docker compose up -d --build / down -v / logs -f svc |
multi-container (v2 plugin: docker compose) |
docker cp app:/tmp/heap.hprof . |
copy file out |
docker network ls / docker volume ls |
networks / volumes |
CMD is default args, ENTRYPOINT the executable; use exec form (JSON array) so the JVM is PID 1 and receives SIGTERM.
- Use
.dockerignore (target/, .git); order layers least-to-most changing (dependencies before source) for cache.
kubectl
kubectl config get-contexts; kubectl config use-context prod
kubectl get pods -n app -o wide -w # -w watch
kubectl describe pod web-7d9 -n app # events: OOMKilled, probe failures, image pull
kubectl logs web-7d9 -c app --previous -f # previous container instance (after crash)
kubectl exec -it web-7d9 -- sh
kubectl port-forward svc/web 8080:80
kubectl apply -f deploy.yaml; kubectl diff -f deploy.yaml
kubectl rollout status deploy/web; kubectl rollout undo deploy/web
kubectl scale deploy/web --replicas=5
kubectl set image deploy/web app=repo/app:1.1
kubectl top pod -n app # needs metrics-server
kubectl get events --sort-by=.lastTimestamp
kubectl debug -it web-7d9 --image=busybox --target=app # ephemeral container
- Pod status hints:
CrashLoopBackOff (app exits), ImagePullBackOff, Pending (resources/scheduling), OOMKilled (exit 137, memory limit exceeded), Evicted.
- Probes:
startupProbe (slow boot), readinessProbe (receive traffic), livenessProbe (restart). Spring Boot: /actuator/health/readiness and /liveness (enabled automatically on Kubernetes, or management.endpoint.health.probes.enabled=true).
- Set
terminationGracePeriodSeconds >= Spring graceful shutdown timeout.
JDK diagnostic tools
| Command |
Use |
jps -lv |
list JVMs |
jcmd <pid> help |
list available diagnostic commands |
jcmd <pid> Thread.print / jstack -l <pid> |
thread dump with locks and deadlocks (take 3 dumps, 5-10 s apart) |
jcmd <pid> GC.heap_info / GC.class_histogram |
heap summary / object counts per class |
jcmd <pid> GC.heap_dump /tmp/h.hprof |
heap dump (preferred over jmap; file written by the target JVM) |
jmap -dump:live,format=b,file=h.hprof <pid> |
heap dump (live triggers a full GC) |
jmap -histo:live <pid> |
class histogram |
jstat -gcutil <pid> 1000 |
GC utilization every second (S0 S1 E O M CCS YGC FGC GCT) |
jcmd <pid> VM.flags / VM.system_properties / VM.native_memory |
effective flags / props / NMT (needs -XX:NativeMemoryTracking=summary) |
jcmd <pid> JFR.start duration=60s filename=/tmp/r.jfr settings=profile |
Flight Recorder (open in JDK Mission Control) |
jcmd <pid> Thread.dump_to_file -format=json f.json |
thread dump including virtual threads (21) |
- High CPU:
top -H -p <pid>, convert thread id to hex, find nid=0x... in the thread dump.
- Memory leak: compare two class histograms, heap dump on OOM:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dumps; analyze with Eclipse MAT.
- Tools attach as the same user as the JVM; in containers run them inside it (JDK image, not JRE) or use
kubectl debug.
Actuator endpoints
| Endpoint |
Purpose |
/actuator/health (/liveness, /readiness) |
status; details with management.endpoint.health.show-details=when-authorized |
/actuator/info |
build/git info |
/actuator/metrics, /metrics/jvm.memory.used |
Micrometer metrics |
/actuator/prometheus |
Prometheus scrape (needs micrometer-registry-prometheus) |
/actuator/env, /configprops, /beans, /mappings, /conditions |
configuration introspection (sensitive) |
/actuator/loggers |
change log level at runtime (POST {"configuredLevel":"DEBUG"}) |
/actuator/threaddump, /heapdump |
diagnostics (heapdump is sensitive) |
/actuator/httpexchanges |
recent requests (Boot 3; needs a repository bean) |
- Only
health is exposed over HTTP by default; expose others via management.endpoints.web.exposure.include. Put on a separate management.server.port and secure it.
Dockerfile: multi-stage Spring Boot
FROM eclipse-temurin:21-jdk AS build
WORKDIR /src
COPY mvnw pom.xml ./
COPY .mvn .mvn
RUN ./mvnw -q dependency:go-offline
COPY src src
RUN ./mvnw -q -DskipTests package && cp target/*.jar /app.jar
FROM eclipse-temurin:21-jre
RUN useradd -r -u 1001 app
WORKDIR /app
COPY --from=build /app.jar app.jar
USER 1001
EXPOSE 8080
ENTRYPOINT ["java", "-XX:MaxRAMPercentage=75", "-jar", "app.jar"]
- Better caching: extract layers with
java -Djarmode=tools -jar app.jar extract --layers (Boot 3.3+; earlier -Djarmode=layertools extract) and COPY dependencies/ and application/ separately.
- The Boot repackage plugin leaves one
*.jar (the original is *.jar.original), so the glob is safe.
- Alternative:
mvn spring-boot:build-image (Buildpacks) or Jib.
JVM container flags
| Flag |
Meaning |
-XX:MaxRAMPercentage=75 |
heap as % of container memory limit (default 25; leave room for metaspace, threads, direct buffers) |
-XX:InitialRAMPercentage=50 |
initial heap |
-Xms / -Xmx |
explicit fixed heap (set equal in steady services) |
-XX:ActiveProcessorCount=N |
override CPU count (JVM reads cgroup quota by default; -XX:+UseContainerSupport on since 10 / 8u191) |
-XX:+UseG1GC / -XX:+UseZGC -XX:+ZGenerational / -XX:+UseSerialGC |
collector; G1 default with 2+ CPUs and 1792 MB+, otherwise Serial |
-XX:MaxGCPauseMillis=200 |
G1 pause goal |
-XX:MaxMetaspaceSize=256m, -Xss512k |
metaspace cap, thread stack |
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dumps |
dump on OOM |
-XX:+ExitOnOutOfMemoryError |
let the orchestrator restart |
-Xlog:gc*:file=/logs/gc.log:time,uptime:filecount=5,filesize=20m |
unified GC logging (9+) |
-XX:NativeMemoryTracking=summary |
off-heap diagnostics |
-XX:TieredStopAtLevel=1 |
faster startup, lower peak (dev/short jobs) |
-XX:+UseStringDeduplication |
G1 string dedupe |
- Kubernetes: memory limit = heap + metaspace + threads + code cache + direct memory; heap 70-75% of limit. CPU limit < 1 core makes the JVM pick Serial GC and 1 processor.
- Env var form:
JAVA_TOOL_OPTIONS="-XX:MaxRAMPercentage=75" (logged at startup as "Picked up JAVA_TOOL_OPTIONS").
- Startup: Spring AOT + GraalVM native image (
-Pnative), CDS/AppCDS (Boot 3.3+), CRaC (Boot 3.2+ with supported JDK).