Approach summary: treat this as a reproducible memory leak investigation with three parallel tracks — data collection in prod (non-invasive), controlled reproduction, and mitigation + fixes. Iterate: collect evidence → reproduce → root-cause → fix → verify.1) Collect in-production evidence (non-disruptive)- Capture OOM/Kube events: kubectl describe pod, kubectl get events, check Kubelet logs.- Gather memory metrics from Prometheus/Grafana (container RSS, JVM/Go heap, GC pause/allocs).- Enable coredumps or heap dumps on OOM: for JVM set -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dumps; for Go enable GODEBUG=madvdontneed=1 and pprof endpoint.- Collect process-level snapshots: kubectl exec into pod and run top, pmap (linux), /proc/<pid>/smaps, or gcore (if allowed).- Save last N logs and stack traces around OOM.Example commands:bash
kubectl describe pod mysvc-abc
kubectl logs mysvc-abc --previous
kubectl exec -it mysvc-abc -- top -b -n1
kubectl exec -it mysvc-abc -- cat /proc/1/smaps > /tmp/smaps
2) Reproduce locally/in-staging- Create a scaled-down environment with identical JVM/Go/build flags, same container image and config (limits/requests, env vars).- Use traffic replay or synthetic load (wrk, vegeta, JMeter) emulating production patterns (payload sizes, concurrent users, long-lived connections).- Gradually ramp load while monitoring memory, GC, and pprof/heap dump endpoints.3) Collect detailed profiles- For Go: enable net/http/pprof and capture heap/alloc profiles during increasing load:bash
curl http://localhost:6060/debug/pprof/heap > heap.prof
go tool pprof -http=:8080 /path/to/binary heap.prof
- For JVM: capture heap dump (.hprof) and use jmap/jstack, jcmd GC.heap_info, and analyze with Eclipse MAT or VisualVM.- Produce CPU and memory flamegraphs (Flamegraph tool + pprof or async-profiler for JVM).4) Analyze- Look for growing retained sizes, leaking goroutines/threads, pinned buffers, caches without eviction, native allocations (direct ByteBuffers, C libraries).- Identify allocation sites dominating growth (pprof top, allocation stacks, MAT dominator tree).- Correlate with application logic: singletons, global caches, channel/goroutine leaks, connection pools, unbounded queues.5) Short-term mitigations- Add resource limits/requests if missing; set reasonable memory requests < limits to let scheduler work.- Configure OOMScoreAdj or JVM flags to prefer killing less critical components.- Restart strategy: increase liveness probe grace to avoid churn, or use controlled rolling restarts to avoid thrashing.- Apply temporary caps on cache sizes, reduce concurrency, or throttle traffic at ingress (Istio/Ingress rate limiting).6) Long-term fixes- Fix root cause: free unused references, implement eviction policies, bound caches/queues, close connections, cancel contexts, fix goroutine leaks.- Add unit/integration tests reproducing leak pattern (load tests, long-run soak tests).- Improve observability: expose pprof/JMX endpoints, add custom mem metrics, alert on sustained memory growth (e.g., > 20% increase over 10m).7) Verification- Run long-duration soak tests with same traffic profile; capture periodic heap/pprof snapshots and assert no monotonic growth in retained heap or number of goroutines/threads.- Deploy canary with increased monitoring and automated rollback if memory trend exceeds threshold.- Post-deploy: monitor for several retention windows (GC cycles, full heap cycles) before marking resolved.Key trade-offs and notes:- Heap dumps can be heavy — schedule off-peak and store securely.- Avoid intrusive profiling in high-traffic prod unless you can tolerate overhead.- Use feature flags to roll out code fixes gradually.This plan provides an evidence-driven path from symptom to fix and verification, balancing immediate reliability and long-term robustness.