Do you want your ad here?

Contact us to get your ad seen by thousands of users every day!

[email protected]

I Asked GitHub Copilot to Profile a Java App. It Found a Bug in My Heap Sizing, and Offered to Fix It

  • July 14, 2026
  • 4 min read
Likes ...
Comments ...
Table of Contents
The idea: Copilot-driven profilingStep 1 — Copilot ran it, and showed me exactly what it ran

I built an extension to collapse the entire loop of running and measuring the performance of Java workloads, so it can be used within a place some developers are starting to consider their new "development environment" in the agentic AI era: the GitHub Copilot app.

The idea: Copilot-driven profiling

This extension is called JVM Pulse, and this is a walkthrough of one real run. JVM Pulse is a canvas extension for the GitHub Copilot app. It doesn't hard-code how to build or run Java projects and it doesn't need the user to be specific either. You click Run analysis, and Copilot does the project-specific work: it detects your build tool and JDK, writes or compiles a representative workload, and launches it with the right flags: unified GC logging and a JFR recording with settings=profile.

Then the artifacts flow through Microsoft's GCToolkit and the JDK's jfr CLI, and everything lands in an interactive dashboard: throughput, pause percentiles, heap occupancy, allocation pressure, hot methods, lock contention, safepoints, I/O, etc.

For this walkthrough I pointed it at a JairoSVG rendering benchmark: 43 sample SVGs, a parse + render + PNG-encode loop, ~25 seconds, heap pinned at 256M on G1GC (JDK 25).

Article content
Run analysis - Copilot will run and profile the app

Step 1 — Copilot ran it, and showed me exactly what it ran

The thing I care most about in an AI tool is that it's transparent about what it did. JVM Pulse records the exact launch command, including every JVM flag. And after 5,633 renders completed, GC log and JFR recording were captured, and the dashboard rendered right next to the chat.

Article content

Step 2 — The verdict in plain language

The dashboard opens with a health assessment instead of a number dump:

  • 99.72% throughput — GC is not the bottleneck
  • p99 pause 7.8 ms — pauses are a non-issue
  • 156 MB/s allocation, ~4.3 GB churned — flagged as Elevated, "the main lever for further gains"
  • Peak heap 243 MB of 256 MB — basically full
Article content
The dashboard populated.

A traditional profiler shows you the p99 and lets you draw your own conclusions. This one tells you where to look: pauses are fine, allocation is your lever.

Step 3 — One click hands the data back to Copilot

A single Analyze with AI button ships the full gc-jfr-report.json and the raw jfr-all-views.txt back into the Copilot session with a ready-made prompt:

Article content
"Analyze with AI" button triggers a prompt with the profiling data.

Step 4 — A root cause, not a summary

This is where it stopped feeling like a toy. Copilot read the allocation-by-site view and came back with:

Healthy on the surface, one structural problem underneath.

The structural problem: humongous allocations against a near-full heap. With a 256M heap, G1 picks a 1M region size, so the humongous threshold is 512 KB. The int[] rasters backing the BufferedImages exceed that, so ~43% of the heap was humongous regions, and 23.4% of GCs were G1_HUMONGOUS_ALLOCATION-triggered. Every large raster was forcing a collection.

Article content
GC assessment

Step 5 — Recommendations with reasoning

The tuning advice came as a table where every flag is tied to the metric it improves:

Article content
JVM configuration recommendations

And crucially, it separated config fixes from the durable code fix: BufferedImage.<init> alone was ~35% of all allocation. A fresh raster allocated per render. The real win is a pooled/reused scratch image, which would gut the int[] 43.5% churn regardless of heap size.

Article content
Allocation and code hotspot analysis

Step 6 — It offered to run the experiment

Instead of leaving me with a to-do list, Copilot proposed the next step:

"Rerun the identical 25s workload with -Xmx1g -XX:G1HeapRegionSize=4m… Want me to run experiment #1 now, or implement the buffer-reuse change first?"

Analysis → hypothesis → experiment, all in one place.

Article content
Conclusion and follow up experiment

Step 7 — Prove it worked

Every run is saved. Flip on Compare, pick the previous run as a baseline, and JVM Pulse diffs the two side by side. Throughput, pauses, allocation, heap, GC events, and even a flag-level diff of the two launch commands. So you don't just believe the fix worked; you see it.

That's the full scientific loop: hypothesis, run, measure, compare — inside the tool you're already coding in.

Why this matters

The individual pieces here aren't new. GCToolkit parses GC logs. JFR has always had the data. What's new is removing the translation tax between "here is telemetry" and "here is what to change", and keeping the entire loop in one surface, driven by an agent that can both read the data and act on it.

JVM Pulse is open source (MIT). If you write Java and you've ever bounced between a benchmark, a log parser, and a flag reference, I'd love for you to try it.

👉 https://github.com/brunoborges/jvm-pulse

Built with Microsoft GCToolkit, the JDK jfr CLI, and the GitHub Copilot canvas extension API leveraging the GitHub Copilot SDK for AI integration. GitHub Copilot App can be used without a GitHub Copilot subscription, along with BYOK models.

  • July 14, 2026
  • 4 min read
Likes ...
Comments ...

Do you want your ad here?

Contact us to get your ad seen by thousands of users every day!

[email protected]

Comments (0)

Highlight your code snippets using [code lang="language name"] shortcode. Just insert your code between opening and closing tag: [code lang="java"] code [/code]. Or specify another language.

No comments yet. Be the first.

Subscribe to foojay updates:

https://foojay.io/feed/
Copied to the clipboard