Changelog
0.3.0
The semantic dimensions grow up: every agent dimension is now source-anchored and feeds the fix-hint + correlation machinery, cross-dimension correlation can confirm a chain, and two new stores (MongoDB, Redis) plus RabbitMQ land — the P1→P5 form-factor cycle.
-
Source anchors + fix-hints on every semantic dimension —
db,web,cache, andmessagingrows now carry the app call-site they were issued from (· at UserRepo:88) and feed--hintswith their own mechanical levers: N+1 → batch,SELECT *→ project, high error rate → validate/handle, low cache hit rate → check key/TTL, synchronous per-message send → batch/async. Built on a shared, bounded, fail-open call-site walk (#137–#140). -
Cross-dimension correlation, confirmed — the correlation note renders an ordered chain (endpoint → SQL → cache → messaging → I/O → hot path → lock → GC) showing each link’s anchor and flag, and upgrades from co-occurrence to a ✓ Confirmed chain when the agent’s captured call-path proves a deeper op ran inside an endpoint’s handler; it degrades to the honest co-occurrence wording offline (
#141,#142). -
ORM N+1 sharpening — a repeated single-row
insert/updatereads as un-batched writes (enable JDBC batching), and a repeatedSELECTthat already usesWHERE id IN (…)is the batch-fetch fix, so it’s no longer a false N+1 (#143). -
MongoDB dimension (
mongo) — times sync-driverMongoCollectionops; a repeatedfindis a possible N+1 document fetch (Filters.in/ aggregation), a repeated single-documentinsertOne/updateOne/deleteOneis un-batched (insertMany/bulkWrite) (#144). -
Redis dimension (
redis) — times direct Lettuce/Jedis commands (distinct from the Spring-Cachecachedimension); many single-key reads read as possible N+1 round-trips — pipeline orMGET/HMGET(#145). -
RabbitMQ messaging — the
messagingdimension now also hooks the low-levelcom.rabbitmq.client.Channel(basicPublish/basicGet), covering native and Spring AMQP with no double-count; the synchronous-send flag is broker-agnostic. ActiveMQ stays on the JMS path (Classic + Artemis) — instrumenting its core API would double-count JMS-over-Artemis (#148).
0.2.2
More honest profile diffs, still from dogfooding real projects (the unitrack field-findings).
-
Disproportionate-rise CPU hedge — a fixed-duration capture of a now-faster workload runs more iterations, so an unchanged hot-path frame accrues more samples and gains share even as its per-op cost is flat. The diff now hedges a
▲row that outpaced a modestly-rising exec-sample total (its share climbed), and prints a throughput caveat pointing at a fixed-iterationbenchA/B for the clean per-op number. This closes the gap the opposing-row hedge (#110, needs a falling row) and the flat-total caveat (#122, needs a ~flat total) both missed (#127). -
trendrestart segmentation — a multi-day monitor spanning rolling redeploys is no longer skewed by each new pod’s cold-start burst.trenddetects a restart from the large gap it leaves between sample timestamps, adds a## Lifecyclenote, excludes each lifetime’s first (warmup) window from the steady-state aggregates, and assesses retention within the latest lifetime only (old-object counts reset on restart). A gapless single run is unchanged (#129). -
-xfolds excluded allocated types —--excludenow also reaches the Top allocated types block: any type whose package matches an exclude (array descriptors included) rolls up into one accounted«excluded types (-x)»line, so a test capture’s embedded-DB internals (an in-process H2’s MVStore types) stop crowding out the app’s own types. No new flag — the same-xthat already scopes hot paths and allocation sites (#128). -
Agent launch-time
scope=arg — a headless long-running monitor can now pin the application scope at startup (scope=app:org.alexmond.unitrack,+-separated,exclude:side supported) instead of relying on heuristic attribution or a mid-run control command.history/trendattribute to the real module from the first sample — the launch equivalent of the in-flightscope app(#133).
0.2.1
Publishing scope, a sharper regex hint, honest CPU-diff hedging, and scope-filter fixes — from the gotmpl4j/jhelm dogfooding loop.
-
CPU flat-total redistribution hedge — when total exec samples are ~flat, a hot-path
▲row is hedged as a larger share of a conserved total (not more work), and the diff adds a caveat that fixed-duration exec-sample deltas conflate per-op cost with throughput — use a fixed-iterationbenchA/B (#122). -
--hintsregex rule — aPatterncompiled per call (or aString.replaceAll/matches/split/replaceFirston a hot line) now names the concrete fix: hoist thePatternto astatic finalfield and reuse theMatcher(a structural, parity-safe change) (#119). -
Scope
-xwins inside-a— an exclude prefix now applies even within an include, instead of the include short-circuiting and leaving an excluded (e.g. test) frame in the roll-up; plus a child-process/pipe-wait I/O teaser and a surefireprofile <pid>capture tip (#121). -
Maven Central publishing scope — the test-only
jvmlens-itmodule is kept off Central via the aggregator’sexcludeArtifacts(thecentral-publishingplugin ignoresmaven.deploy.skip);jvmlens-engine/-cli/-agent/-jmhstill publish (#117). -
Docs — the floatString case study now cross-references the gotmpl4j and jhelm performance pages, where the same loop is worked end-to-end on real workloads (#125).
0.2.0
The optimize→measure loop gets honest about measured significance — so a sampled swing can’t be mistaken for a real win — plus richer source attribution. All from dogfooding real JVM projects (the gotmpl4j field-findings).
Measured A/B verdicts (gate the loop on the wall-clock, not the sampler)
-
Measured allocation A/B — with JMH’s
-prof gcand abaseline=, the JMH profiler prints an exact bytes/op before→after verdict that isSIGNIFICANTonly if the relative Δ exceeds the combined noise band and the confidence intervals don’t overlap; it reconciles the sampled diff total against the measured one and flags a disagreement as likely sampling redistribution / JIT elision (#104). -
Measured throughput A/B — the CPU analog: an exact primary-score (ops/s, us/op, …) verdict with the same significance test. When a sampled hot-path share moved but wall-clock throughput is flat, it says so explicitly — a CPU-share shift is not a speedup (#112).
-
Dispersion verdict — a genuine structural allocation removal collapses the cross-fork variance band (the removed allocation was the noise); a
~500×collapse is flagged as a strong real-win signal the mean alone can’t give, and a now near-deterministic bytes/op emits a STOP signal (diminishing returns — pivot off allocation) (#110). -
Per-benchmark baseline match — the inline
baseline=now records the benchmark method in its sidecar; a baseline recorded for a different method is detected and the verdict skipped with a warning, instead of emitting confident wrong per-method numbers (#112). -
JMH
-prof gcheader — the summary leads with JMH’s exactgc.alloc.rate.normnext to jvmlens’s sampled per-site bytes, and drops the JMH harness control socket from External I/O noise (#100).
Sharper attribution & honest hedges
-
analyze --source <roots>— echoes the source-line text inline at eachfile:lineanchor (floatString:129 ⟶ mantissa.substring(…)), so a coding agent sees the offending line without opening the file; opt-in, degrades silently (#109). -
Allocation-by-type rollup — the diff sums allocation sites by declaring type, so an extract-method refactor (caller ▼ + a
NEWcallee) reads as one net change instead of a regression (#99). -
Escape-analysis caveat — an allocation site dominated by a boxed primitive or captured lambda is tagged as possibly scalar-replaced (a false lever); the
--hintsdirections hedge the same, so you verify with-prof gcbefore chasing it (#103). -
CPU-share redistribution hedge — the diff hedges a hot-path
▲row whose absolute samples rose while total exec samples fell, so a share that climbed only because the rest of the profile shrank isn’t misread as a regression (#110).
0.1.0
First public release. Proof-of-concept graduated from the incubator, then built out into a full JVM-evidence → LLM-ready summarizer with a live control plane.
Commands
-
analyze <file.jfr|dir>— summarize a recording, or a directory of them (a JMH-prof jfrrun: every fork’s.jfrmerged into one summary), into markdown / JSON / prompt;--baseline <before.jfr>diffs two recordings — anchored on absolute weight (bytes / ms / samples) with share as context, so an optimize-loop reduction isn’t mislabelled a regression;--assert "<rules>"is a CI perf-gate (non-zero exit on regression);--hintsadds hedged[possible]fix directions;--top-k <n>/--max-tokens <n>budget the size;--skip-warmup <ms>drops JIT/classload churn (per-file cutoff) for steady-state hot paths. -
profile <pid>— live attach + timed JFR capture (no pre-recorded file);--engine asyncadds native frames via async-profiler;-w/--warmup,-k/--keep. -
bench --main <class>— the no-JMH harness: runs anymain(String[])in a warmup→timed loop, captures a JFR over only the timed phase, and summarizes;--cp,-w/--warmup,-i/--iters,--jfr(keep for a baseline diff). -
watch <pid>— continuous ring buffer; periodic, or dump-on-trigger (--on-gc-ms/--on-cpu-pct/--on-old-objects). -
trend <history.jsonl>— reduce a long-running monitor’s accumulated history to a change-over-time digest (per-dimension direction, hot-path shift, hedged retention). -
control <control-file> <cmd…>— in-flight agent control (see Runtime control). -
mcp— stdio MCP server exposing scoped, navigable tools (one per dimension) plus a liveprofiletool; reachable remotely over stdio-over-ssh. Serves data only — never calls an LLM. -
JMH profiler plugin —
-prof org.alexmond.jvmlens.jmh.JvmlensProfilerprints the summary inline after a benchmark trial;keep=<path>retains the fork’s recording andbaseline=<prev.jfr>prints the diff inline (the whole optimize loop inside one JMH run). Ships as a dependency-lightjvmlens-jmh.jar(engine + profiler, no Spring/picocli/jmh).
Dimensions summarized
-
CPU — hot paths (application-attributed) + hot leaf methods.
-
Memory — allocation sites + types, GC pressure.
-
Wait — lock contention by method + contended monitors.
-
External I/O — socket + file blocking I/O by endpoint (
jdk.SocketRead/Write,jdk.FileRead/Write). -
Virtual-thread pinning —
jdk.VirtualThreadPinnedby site, withpinnedReason. -
Deadlocks — authoritative
ThreadMXBean.findDeadlockedThreadswait-for cycle (agent). -
Database (SQL) — agent JDBC instrumentation; sanitized SQL shapes, latency, N+1 flag.
-
HTTP endpoints — agent servlet instrumentation; route-shape, latency, errors.
-
Messaging / cache — agent Kafka/JMS and Spring-
Cacheoperation timing. -
Micrometer — summarize an existing registry’s timers (no extra instrumentation).
-
Variable snapshots — agent
snapshot=Class#methodargument digests (correctness). -
Cross-dimension correlation — a hedged note co-locating the dominant signal per dimension.
Trust & honesty
-
Per-row absolute hit
countbeside every share; sections tagged[sampled]vs[measured]; a⚠adequacy caveat under 200 execution samples, and a matching per-site allocation caveat when there are too few allocation samples (the total stays reliable). -
Source-line anchoring — hot-path leaves and allocation sites carry their source line (
Svc.compute:88), so a coding agent goes from diagnosis to the edit in one step. -
Leaf confidence — a hot path’s teaser lists the top leaves with counts and flags
⚠ diffusewhen no single leaf dominates, instead of trusting one (possibly stray) frame. -
The heuristic under-interprets — retention is flagged as possible growth, never a confident "leak"; correlation suggests, never asserts.
Agent & runtime control
-
-javaagent:jvmlens-agent.jar— in-process, container-native, no attach/JMX. Writes periodic summaries;history=appends a JSONL time-series; per-dimension opt-in. -
Runtime control via a watched control file (
control=<file>) + thejvmlens controlCLI:start/stop,clear,dump,enable/disable <dim>,settings profile|default,interval,scope app|exclude|reset(filtering),topn [<category>] <n>(rows/section),status— each returns the agent’s state to the caller. No ports, no JMX. -
pausedlaunch +start-after-warm-up — the clean fix for short cold runs profiling startup.
Outputs & engine
-
Formats
md/json/prompt; report focusesfull/cpu/memory/locks/gc/io/pinning/db/web/messaging/cache/metrics/deadlock. -
Application-frame scoping (
-a/--app-package,-x/--exclude); runtime-adjustable top-N. -
Dependency-free engine (
jdk.jfr.consumeronly) reused by every front-end.