Skip to main content

Overview

By default, ap-query aggregates samples across all threads. The -t/--thread flag filters to threads matching a substring, essential when different thread pools have different workloads.

Basic Usage

Filter by Thread Name

# HTTP request handler threads
ap-query hot profile.jfr -t "http-nio-8080"

# Kafka consumer threads
ap-query tree profile.jfr -t "kafka-consumer" -m poll

# Background scheduler threads
ap-query hot profile.jfr -t "pool-1-thread" --top 20
The filter matches any thread name containing the substring (case-sensitive).

Filter Output

ap-query prints the filter match count:
Thread filter: http-nio-8080 — 1,234/5,678 samples (21.7%)
  • 1,234 samples matched the filter
  • 5,678 samples total
  • 21.7% of samples are from matching threads
If no threads match, analysis continues with 0 samples and prints empty results.

Discovering Thread Names

Use the threads command to see thread distribution:
ap-query threads profile.jfr
Output:
=== THREADS (top 10) ===
THREAD                                SAMPLES      %
http-nio-8080-exec-12                   1,234   21.7%
http-nio-8080-exec-7                      987   17.4%
ScheduledExecutor-1                       456    8.0%
kafka-consumer-0                          321    5.7%
pool-1-thread-3                           234    4.1%
Copy thread names from threads output to use as -t filter values.

Thread Name Patterns

Exact Thread Name

ap-query hot profile.jfr -t "http-nio-8080-exec-12"
Matches only that specific thread.

Thread Pool (Common Prefix)

ap-query hot profile.jfr -t "http-nio"
Matches all threads with http-nio in the name:
  • http-nio-8080-exec-1
  • http-nio-8080-exec-2
  • http-nio-9090-exec-1

Thread Group

ap-query hot profile.jfr -t "exec"
Matches any thread containing exec (executor pools).

Multiple Filters (Repeated Flag Not Supported)

To analyze multiple thread groups, run separate commands:
ap-query hot profile.jfr -t "http-nio" > http-threads.txt
ap-query hot profile.jfr -t "kafka" > kafka-threads.txt
Or use scripting (see Starlark Scripting).

Commands Supporting Thread Filtering

All analysis commands accept -t/--thread:
  • hot — Hot methods per thread
  • tree — Call tree per thread
  • trace — Hottest path per thread
  • callers — Callers per thread
  • lines — Line-level profile per thread
  • threads — Pre-filtered thread list
  • filter — Filter stacks by thread + method
  • collapse — Export thread-filtered stacks
  • diff — Compare thread-filtered profiles
  • timeline — Timeline per thread

Combining with Other Filters

Thread + Event Type

# CPU hotspots in HTTP threads
ap-query hot profile.jfr -t "http-nio" --event cpu

# Wall-clock blocking in Kafka consumers
ap-query hot profile.jfr -t "kafka" --event wall --no-idle

Thread + Time Range

# HTTP threads during load test spike
ap-query hot profile.jfr -t "http-nio" --from 2m --to 2m30s

Thread + Method Filter

# HashMap usage in scheduler threads
ap-query filter profile.jfr -t "Scheduled" -m HashMap | ap-query hot -

Real-World Use Cases

Diagnose HTTP Handler Latency

1

Identify thread pool

ap-query threads profile.jfr
# http-nio-8080-exec-* threads show 45% of samples
2

Isolate HTTP threads

ap-query hot profile.jfr -t "http-nio-8080" --event wall --no-idle
Wall-clock events show where threads block (DB, network, locks).
3

Drill into blocking calls

ap-query tree profile.jfr -t "http-nio-8080" -m Socket.read --depth 6
Reveals call paths leading to slow socket reads.

Compare Thread Groups

Identify workload differences between pools:
echo "=== HTTP Threads ==="
ap-query hot profile.jfr -t "http-nio" --top 10

echo "=== Kafka Threads ==="
ap-query hot profile.jfr -t "kafka" --top 10
Output:
=== HTTP Threads ===
HashMap.get         28.3%
JSON.parse          15.7%

=== Kafka Threads ===
deserialize         42.1%
CRC32.update        19.8%

Find Thread-Specific Regressions

ap-query diff before.jfr after.jfr -t "worker" --min-delta 1.0
Shows changes only in worker threads.

Analyze Concurrency Bottlenecks

# Lock contention in executor pool
ap-query hot profile.jfr -t "pool-1" --event lock --top 20
Reveals lock hotspots in a specific thread pool.

Thread Grouping

The threads command supports --group to aggregate by normalized name:
ap-query threads profile.jfr --group
Output:
THREAD                    SAMPLES      %
http-nio-exec                8,234   45.2%  (12 threads)
pool-thread                  3,456   19.0%  (8 threads)
ScheduledExecutor              987    5.4%  (2 threads)
Grouping normalizes:
  • http-nio-8080-exec-1http-nio-exec
  • pool-1-thread-3pool-thread
  • ScheduledExecutor-1ScheduledExecutor
Use --group to see aggregate thread pool impact before filtering.

Empty Thread Names

Some stacks (kernel frames, native code) may have empty thread names. These are excluded unless you filter for them explicitly:
ap-query hot profile.jfr -t ""
Matches stacks with no thread name.

Timeline Per Thread

Combine timeline with -t to see temporal patterns per thread:
ap-query timeline profile.jfr -t "http-nio" --resolution 5s
Output:
0.0s-5.0s       [████████████░░░░░░░░░░░░] 1,234 samples  HashMap.get
5.0s-10.0s      [██████████████████████░░] 2,345 samples  JSON.parse
Shows hot methods in HTTP threads over time.

Thread Filter Pitfalls

Case Sensitivity

Thread names are case-sensitive:
ap-query hot profile.jfr -t "HTTP"    # Won't match "http-nio-8080-exec-1"
ap-query hot profile.jfr -t "http"    # Matches

Overly Generic Filters

Be specific to avoid unintended matches:
ap-query hot profile.jfr -t "thread"  # Matches almost everything
ap-query hot profile.jfr -t "pool-1-thread"  # More targeted

No Threads Match

If filter matches nothing:
Thread filter: nonexistent — 0/5,678 samples (0.0%)
no samples (empty profile or all filtered out)
Double-check thread name with ap-query threads profile.jfr.

Scripting Multi-Thread Analysis

For complex thread comparisons, use Starlark:
p = open("profile.jfr")

# Group by thread and analyze each
groups = p.group_by(lambda s: s.thread if s.thread else "(no thread)")

for name in sorted(groups.keys()):
    prof = groups[name]
    print("=== " + name + " ===")
    for m in prof.hot(5):
        print("  " + m.name + " " + str(m.self_pct) + "%")
See Starlark Scripting for details.

Common Patterns

HTTP vs Background Threads

echo "HTTP request threads:"
ap-query hot profile.jfr -t "http-nio" --top 5

echo "Background scheduler:"
ap-query hot profile.jfr -t "Scheduled" --top 5

Find Which Thread Pool is Blocking

ap-query threads profile.jfr --event wall --no-idle
Sorts threads by wall-clock time (including blocked time).

Compare Same Thread Across Time

ap-query diff profile.jfr \
  -t "worker-3" \
  --from 0s --to 30s \
  --vs-from 2m --vs-to 2m30s
Shows how one thread’s profile changed over time.

Performance Considerations

Thread filtering is cheap — it’s a simple substring match on metadata. No performance impact even with millions of samples.

Build docs developers (and LLMs) love