Summary
After regex matching, the next biggest allocation source in the parser is temporary String / byte[] and java.time objects created while parsing numeric and timestamp fields. Each parsed line slices substrings and builds intermediate java.time objects that are immediately discarded.
Evidence (JFR settings=profile, ~28s run)
Workload: sample Main parsing a 195 MB JDK8 ParallelGC rolling log (gclogs/rolling/jdk8/aagl_prd/gc.log), -Xmx1500m, G1, Java 25 (GraalVM 25.0.3). ~19,985 MB sampled allocations.
Allocation by site:
| Site |
Share |
java.lang.String.substring(int, int) |
8.06% |
com.microsoft.gctoolkit.parser.AbstractLogTrace.convertToDouble(String) |
3.33% |
java.time.LocalDate.ofEpochDay(long) |
3.29% |
java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.parse(...) |
2.51% |
com.microsoft.gctoolkit.time.DateTimeStamp.dateFromString(String) |
1.63% |
jdk.internal.math.FloatingDecimal.readJavaFormatString(...) |
1.47% |
jdk.internal.math.FloatingDecimal.copyDigits(...) |
1.23% |
Allocation by class (related): byte[] 7.98%, java.lang.String 3.30%, java.time.format.Parsed 2.61%, LocalDateTime 2.05%, LocalDate 1.87%, LocalTime 1.56%.
Suggested fixes
- Parse numerics directly off the matched region using index offsets rather than
substring(...) + Double.parseDouble(...) (avoids String + byte[] + FloatingDecimal churn).
- Avoid building intermediate
java.time objects on the hot path; reuse a preconfigured DateTimeFormatter and resolve straight to an epoch value. DateTimeStamp.dateFromString and AbstractLogTrace.convertToDouble are good entry points.
Expected impact
Reduces ~15–20% of sampled allocation pressure (substring + java.time + FloatingDecimal). Complements the Matcher-reuse work; GC is already healthy (98.76% throughput) so this improves allocation throughput / CPU, not pauses.
Summary
After regex matching, the next biggest allocation source in the parser is temporary
String/byte[]andjava.timeobjects created while parsing numeric and timestamp fields. Each parsed line slices substrings and builds intermediatejava.timeobjects that are immediately discarded.Evidence (JFR
settings=profile, ~28s run)Workload: sample
Mainparsing a 195 MB JDK8 ParallelGC rolling log (gclogs/rolling/jdk8/aagl_prd/gc.log),-Xmx1500m, G1, Java 25 (GraalVM 25.0.3). ~19,985 MB sampled allocations.Allocation by site:
java.lang.String.substring(int, int)com.microsoft.gctoolkit.parser.AbstractLogTrace.convertToDouble(String)java.time.LocalDate.ofEpochDay(long)java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.parse(...)com.microsoft.gctoolkit.time.DateTimeStamp.dateFromString(String)jdk.internal.math.FloatingDecimal.readJavaFormatString(...)jdk.internal.math.FloatingDecimal.copyDigits(...)Allocation by class (related):
byte[]7.98%,java.lang.String3.30%,java.time.format.Parsed2.61%,LocalDateTime2.05%,LocalDate1.87%,LocalTime1.56%.Suggested fixes
substring(...)+Double.parseDouble(...)(avoidsString+byte[]+FloatingDecimalchurn).java.timeobjects on the hot path; reuse a preconfiguredDateTimeFormatterand resolve straight to an epoch value.DateTimeStamp.dateFromStringandAbstractLogTrace.convertToDoubleare good entry points.Expected impact
Reduces ~15–20% of sampled allocation pressure (substring + java.time + FloatingDecimal). Complements the Matcher-reuse work; GC is already healthy (98.76% throughput) so this improves allocation throughput / CPU, not pauses.