From c8d2b3cb3af386de0aa4bd1a53fe57a639f759b4 Mon Sep 17 00:00:00 2001 From: Nick Telford Date: Thu, 22 Feb 2024 15:50:12 +0000 Subject: [PATCH] Estimate StringBuilder capacity for exported names When we generate metric names, we use the default `StringBuilder` constructor, which allocates a _16 character_ buffer. Most metrics will exceed this, especially when we start adding attributes to it. This causes `StringBuilder` to "grow", automatically, by allocating a new array and copying its contents over to it. Each time it only grows enough to contain the `append`ed String, which means we need to grow on almost every `append` call. While allocating new memory is cheap in the JVM, copying memory is not. These copies add up, especially with a large number of long-named metrics with many attributes. Instead, we can calculate the necessary capacity of the `StringBuilder` up-front, which should avoid doing any copying during `append`. Signed-off-by: Nick Telford --- .../src/main/java/io/prometheus/jmx/JmxCollector.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/collector/src/main/java/io/prometheus/jmx/JmxCollector.java b/collector/src/main/java/io/prometheus/jmx/JmxCollector.java index 567f12f05..fbe685127 100644 --- a/collector/src/main/java/io/prometheus/jmx/JmxCollector.java +++ b/collector/src/main/java/io/prometheus/jmx/JmxCollector.java @@ -476,7 +476,12 @@ private MatchedRule defaultExport( Double value, double valueFactor, String type) { - StringBuilder name = new StringBuilder(); + // avoid having to grow the StringBuilder incrementally, by calculating its capacity up-front + int estimatedSize = domain.length() + + (beanProperties.isEmpty() ? 0 : 1 + beanProperties.values().iterator().next().length()) + + (attrKeys.stream().map(k -> k.length() + 1).reduce(0, Integer::sum)) + + attrName.length() + 1; + StringBuilder name = new StringBuilder(estimatedSize); name.append(domain); if (beanProperties.size() > 0) { name.append(SEP);