From d75d9774c806e4bf73caa69cd78c31a132e4c812 Mon Sep 17 00:00:00 2001 From: Mandy Chung Date: Tue, 12 Sep 2023 16:24:16 +0000 Subject: [PATCH] 8285447: StackWalker minimal batch size should be optimized for getCallerClass Reviewed-by: simonis --- .../classes/java/lang/StackStreamFactory.java | 29 ++++++++--- .../bench/java/lang/CallerClassBench.java | 50 +++++++++++++++++++ .../bench/java/lang/StackWalkBench.java | 10 ---- 3 files changed, 71 insertions(+), 18 deletions(-) create mode 100644 test/micro/org/openjdk/bench/java/lang/CallerClassBench.java diff --git a/src/java.base/share/classes/java/lang/StackStreamFactory.java b/src/java.base/share/classes/java/lang/StackStreamFactory.java index 67ca9e58ab9be..b9ad5ba6cc7ea 100644 --- a/src/java.base/share/classes/java/lang/StackStreamFactory.java +++ b/src/java.base/share/classes/java/lang/StackStreamFactory.java @@ -69,10 +69,12 @@ private StackStreamFactory() {} // lazily add subclasses when they are loaded. private static final Set> stackWalkImplClasses = init(); + // Number of elements in the buffer reserved for VM to use + private static final int RESERVED_ELEMENTS = 1; + private static final int MIN_BATCH_SIZE = RESERVED_ELEMENTS + 2; private static final int SMALL_BATCH = 8; private static final int BATCH_SIZE = 32; private static final int LARGE_BATCH_SIZE = 256; - private static final int MIN_BATCH_SIZE = SMALL_BATCH; // These flags must match the values maintained in the VM @Native private static final int DEFAULT_MODE = 0x0; @@ -196,7 +198,7 @@ protected AbstractStackWalker(StackWalker walker, int mode, int maxDepth) { protected abstract int batchSize(int lastBatchFrameCount); /* - * Returns the next batch size, always >= minimum batch size (32) + * Returns the next batch size, always >= minimum batch size * * Subclass may override this method if the minimum batch size is different. */ @@ -538,8 +540,7 @@ protected void initFrameBuffer() { protected int batchSize(int lastBatchFrameCount) { if (lastBatchFrameCount == 0) { // First batch, use estimateDepth if not exceed the large batch size - // and not too small - int initialBatchSize = Math.max(walker.estimateDepth(), SMALL_BATCH); + int initialBatchSize = Math.max(walker.estimateDepth()+RESERVED_ELEMENTS, MIN_BATCH_SIZE); return Math.min(initialBatchSize, LARGE_BATCH_SIZE); } else { if (lastBatchFrameCount > BATCH_SIZE) { @@ -747,19 +748,31 @@ protected Integer consumeFrames() { return n; } + /* + * Typically finding the caller class only needs to walk two stack frames + * 0: StackWalker::getCallerClass + * 1: API + * 2: caller class + * + * So start the initial batch size with the minimum size. + */ @Override protected void initFrameBuffer() { - this.frameBuffer = new ClassFrameBuffer(walker, getNextBatchSize()); + this.frameBuffer = new ClassFrameBuffer(walker, MIN_BATCH_SIZE); } @Override protected int batchSize(int lastBatchFrameCount) { - return MIN_BATCH_SIZE; + // this method is only called when the caller class is not found in + // the first batch. getCallerClass may be invoked via core reflection. + // So increase the next batch size as there may be implementation-specific + // frames before reaching the caller class's frame. + return SMALL_BATCH; } @Override protected int getNextBatchSize() { - return MIN_BATCH_SIZE; + return SMALL_BATCH; } } @@ -785,7 +798,7 @@ protected void initFrameBuffer() { * Each specialized AbstractStackWalker subclass may subclass the FrameBuffer. */ abstract static class FrameBuffer { - static final int START_POS = 2; // 0th and 1st elements are reserved + static final int START_POS = RESERVED_ELEMENTS; // buffers for VM to fill stack frame info int currentBatchSize; // current batch size diff --git a/test/micro/org/openjdk/bench/java/lang/CallerClassBench.java b/test/micro/org/openjdk/bench/java/lang/CallerClassBench.java new file mode 100644 index 0000000000000..01399c7c2700f --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/CallerClassBench.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.lang; + +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(value = 3, jvmArgsAppend = {"-Xmx1g", "-Xms1g"}) +@State(Scope.Benchmark) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +public class CallerClassBench { + static final StackWalker INST = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); + + @Benchmark + public Class getCallerClass() { + return INST.getCallerClass(); + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/StackWalkBench.java b/test/micro/org/openjdk/bench/java/lang/StackWalkBench.java index 08fb90b9bff5a..08cbc85833bf0 100644 --- a/test/micro/org/openjdk/bench/java/lang/StackWalkBench.java +++ b/test/micro/org/openjdk/bench/java/lang/StackWalkBench.java @@ -310,16 +310,6 @@ public void run() { } } - /** - * StackWalker.getCallerClass() - */ - @Benchmark - public void getCallerClass(Blackhole bh) { - final StackWalker sw = walker(walker); - Class c = sw.getCallerClass(); - bh.consume(c); - } - /** * StackWalker.getCallerClass() with generated call stack of * the given depth.