RFR: 8291641: Optimize StackTraceElement.toString()

Carter Kozak duke at openjdk.org
Mon Aug 1 17:31:42 UTC 2022


On Wed, 27 Jul 2022 22:56:39 GMT, David Schlosnagle <duke at openjdk.org> wrote:

> I would like to contribute an optimized version of `StackTraceElement#toString()` that uses a single StringBuilder throughout creation to avoid intermediate `String` allocations. `StackTraceElement#toString()` is used in a number of JDK code paths including `Throwable#printStackTrace()`, as well as many JDK consumers may transform `StackTraceElement` `toString()` in logging frameworks capturing throwables and exceptions, and diagnostics performing dumps.
> 
> Given this usage and some observed JFR profiles from production services, I'd like to reduce the intermediate allocations to reduce CPU pressure in these circumstances. I have added a couple benchmarks for a sample `Throwable#printStackTrace()` converted to String via `StringWriter` and individual `StackTraceElement` `toString`. The former shows ~15% improvement, while the latter shows ~40% improvement.
> 
> Before
> 
> Benchmark                               Mode  Cnt       Score      Error  Units
> StackTraceElementBench.printStackTrace  avgt   15  167147.066 ± 4260.521  ns/op
> StackTraceElementBench.toString         avgt   15     132.781 ±    2.095  ns/op
> 
> 
> After
> 
> Benchmark                               Mode  Cnt       Score      Error  Units
> StackTraceElementBench.printStackTrace  avgt   15  142909.133 ± 2290.720  ns/op
> StackTraceElementBench.toString         avgt   15      78.939 ±    0.469  ns/op

I think there's another angle to consider here:
This wasn't an issue using java 8 and earlier because the data encapsulated by StackTraceElement was available using the public API, this meant that logging frameworks were free to implement custom formatters that could be used in place of the toString implementation without losing features and optimizing for niche use-cases (e.g. allocation avoidance).
In recent java versions, the StackTraceElement string value is based on data that's not available using the public API (whether the classloader is builtin, and whether the module is from java.base). String matching can get around this at a potential correctness cost.

It would be helpful for my uses if the new internal attributes were exposed in some way from StackTraceElement, however I understand why that isn't desirable from an API design standpoint.
I suspect a public `appendTo(Appendable)` is a more feasible API addition as it encapsulates the functionality without awareness of what a builtin classloader is, and roughly matches prior art within `Throwable.printStackTrace`. I'd love to get an opinion from someone more closely involved with openjdk core-libs development :-)

src/java.base/share/classes/java/lang/StackTraceElement.java line 374:

> 372:      * @throws IOException If an I/O error occurs
> 373:      */
> 374:     private void appendTo(Appendable dest) throws IOException {

Perhaps this could be package-private for reuse by `Throwable.printStackTrace`, avoiding the intermediate toString.

-------------

PR: https://git.openjdk.org/jdk/pull/9665


More information about the core-libs-dev mailing list