Skip to main content

Overview

The lines command provides a source-line level breakdown of where time is spent within a specific method. This helps identify:
  • Which lines in a method are most expensive
  • Loop iterations vs initialization overhead
  • Specific statements causing bottlenecks
Line information requires debug symbols and profiler support. Not all profiles include line numbers.

Usage

ap-query lines <file> -m <method> [flags]

Flags

--method
string
required
Required. Substring to match on method name
--top
int
default:"0"
Limit output rows (0 = unlimited)
--fqn
boolean
default:"false"
Show fully-qualified method names in output

Shared Flags

Common flags supported by most commands:
--event
string
default:"cpu"
Event type: cpu, wall, alloc, lock, or hardware counter name
--thread
string
Filter to threads matching substring
--start
duration
Start time of window (JFR only)
--end
duration
End time of window (JFR only)

Output Format

Displays a ranked table:
SOURCE:LINE                               SAMPLES     PCT
HashMap.resize:1245                          4823   18.2%
HashMap.resize:1251                          3201   12.1%
HashMap.resize:1234                          1456    5.5%
Columns:
  • SOURCE:LINE: Method name and line number
  • SAMPLES: Sample count for that line
  • PCT: Percentage of total profile samples
Results are sorted by sample count (descending).

Examples

Basic Line Breakdown

ap-query lines profile.jfr -m HashMap.resize
Output:
SOURCE:LINE                               SAMPLES     PCT
HashMap.resize:1245                          4823   18.2%
HashMap.resize:1251                          3201   12.1%
HashMap.resize:1234                          1456    5.5%
HashMap.resize:1248                           892    3.4%

Top Lines Only

Show just the top 5 lines:
ap-query lines profile.jfr -m HashMap.resize --top 5

Multiple Methods Matching

When multiple methods match, all are analyzed:
ap-query lines profile.jfr -m Service.process
Output shows lines from all matching methods (e.g., UserService.process, OrderService.process).

Fully-Qualified Names

Distinguish methods with identical short names:
ap-query lines profile.jfr -m Service.process --fqn
Output:
SOURCE:LINE                                      SAMPLES     PCT
com.example.user.Service.process:42                 823   15.2%
com.example.order.Service.process:89                456    8.4%

Thread-Specific Lines

ap-query lines profile.jfr -m "Database.query" --thread worker

Allocation Hot Lines

Find which lines allocate the most:
ap-query lines profile.jfr -m processData --event alloc

Time Window Analysis

ap-query lines profile.jfr -m authenticate --start 1m --end 2m

Requirements

Line Number Availability

Line information requires:
  1. Debug symbols in the profiled binary/JAR
  2. Profiler support for line numbers:
    • async-profiler 2.0+ with -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints
    • JFR with debug info
  3. Compiled code: Interpreted methods may not have line info

Checking for Line Info

If you see:
no line info for frames matching 'MethodName'
The method exists but lacks line numbers. Possible reasons:
  • No debug symbols (javac -g)
  • Native or kernel frames
  • JVM runtime methods
  • Inlined code (line may be attributed to caller)

Use Cases

Loop Optimization

Identify expensive loop iterations:
ap-query lines profile.jfr -m processItems
Example shows line 145 (loop body) is 10x hotter than line 142 (loop setup).

Conditional Branch Cost

ap-query lines profile.jfr -m validate
Compare samples on different conditional branches to find which path dominates.

Method Refactoring

Before refactoring a large method, see which sections are expensive:
ap-query lines profile.jfr -m "LargeMethod" --top 10
Extract hot lines into a separate method for focused optimization.

Validating Optimizations

Compare line profiles before/after changes:
ap-query lines before.jfr -m compute > before-lines.txt
ap-query lines after.jfr -m compute > after-lines.txt
diff before-lines.txt after-lines.txt

Understanding Inlining

Samples may appear on the call site rather than the inlined method’s original source:
ap-query lines profile.jfr -m caller
If you see high samples on a single line that calls a small method, the JIT likely inlined it.

Interpretation Tips

Line accuracy: Line numbers are approximate due to JIT compilation and instruction reordering. Treat as “near this line” rather than exact.
Inlining effects: If a method you expected to see isn’t in the profile, it may have been inlined. Check the caller with lines.
Native code: Native methods (C, C++, Rust) rarely have line info unless compiled with debug symbols.
Line numbers can be misleading after aggressive JIT optimizations. Cross-reference with source code to verify the actual operation at each line.

Troubleshooting

No Matching Method

If you see:
no samples (empty profile or all filtered out)
Check:
  • Method name spelling (case-sensitive)
  • Method exists in this event type: try --event wall or --event alloc
  • Method was sampled: use ap-query hot to verify it appears

Method Exists But No Line Info

Error:
no line info for frames matching 'MethodName'
Solutions:
  • Recompile with debug info: javac -g
  • Add JVM flags: -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints
  • Check if method is native/kernel code (line info not available)

Unexpected Line Numbers

If line numbers don’t match your source:
  • Ensure JARs/binaries match the profiled version
  • Check for compiler optimizations reordering code
  • Verify debug info wasn’t stripped during build

See Also

  • hot - Find which methods to investigate with lines
  • callers - Show call paths to a method
  • tree - Visualize call tree from a method
  • script - Access line numbers via Frame.line in scripts

Build docs developers (and LLMs) love