Skip to content

Use StackWalker to deduce main application class #31665

Closed as not planned
Closed as not planned
@GGGGGHT

Description

@GGGGGHT

Recently I am learning new features after jdk9, I saw an interesting feature. Stack-Walking API.
This API allows us to iterator the current stack in a streaming fashion. So I want to use this method to rewrite the deduceMainApplicationClass method. This will make the code look more concise.
This method can be optimized like this:

return StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
            .walk((s) -> s.filter(e -> e.getMethodName().equals("main")).findFirst().map(
                StackWalker.StackFrame::getDeclaringClass))
            .orElseThrow(ClassNotFoundException::new);
JMH demo
package com.ggggght.jmh;

import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

@Fork(2) @Warmup(iterations = 1, time = 1) @State(Scope.Benchmark)
@BenchmarkMode(value = {Mode.Throughput}) public class StackWalkBench {
    private static final StackDriver STACK_DRIVER = new StackDriver();
    @Param({"50", "100", "200"}) private int initialDepth;

    @Param({"10", "20"}) private int callDepth;

    @Benchmark() public void stackWalkWalk() {
        StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
        List<StackTraceElement> stackTraceElements = walker.walk(
            stackFrameStream -> stackFrameStream.map(StackWalker.StackFrame::toStackTraceElement)
                .collect(Collectors.toList()));
        STACK_DRIVER.deepCall(initialDepth, callDepth, FQCN -> walker.walk(
            s -> s.dropWhile(f -> !f.getClassName().equals(FQCN))
                .dropWhile(f -> f.getClassName().equals(FQCN))
                .findFirst()).get().toStackTraceElement());
    }

    @Benchmark() public void throwableSearch() {
        STACK_DRIVER.deepCall(initialDepth, callDepth, FQCN -> {
            StackTraceElement[] stackTrace = new Throwable().getStackTrace();
            boolean found = false;
            for (int i = 0; i < stackTrace.length; i++) {
                String className = stackTrace[i].getClassName();
                if (FQCN.equals(className)) {
                    found = true;
                    continue;
                }

                if (found && !FQCN.equals(className)) {
                    return stackTrace[i];
                }
            }
            return null;
        });
    }

    public static void main(String[] args) throws RunnerException {
        Options opt =
            new OptionsBuilder().include(StackWalkBench.class.getSimpleName()).build();

        new Runner(opt).run();
    }
}

class StackDriver {
    public StackTraceElement deepCall(int initialDepth, int targetDepth,
        Function<String, StackTraceElement> supplier) {
        if (--initialDepth == 0) {
            Processor processor = new Processor();

            return processor.apply(targetDepth, supplier);
        }

        return deepCall(initialDepth, targetDepth, supplier);
    }

    private static class Processor
        implements BiFunction<Integer, Function<String, StackTraceElement>, StackTraceElement> {
        private static final String FQCN = Processor.class.getName();

        @Override public StackTraceElement apply(Integer depth,
            Function<String, StackTraceElement> function) {
            if (--depth == 0) {
                return function.apply(FQCN);
            }

            return apply(depth, function);
        }
    }
}

Here are the results of a benchmark I did with hotspot17 and jmh. The complete information I put in the file below.
image

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: supersededAn issue that has been superseded by anothertype: taskA general task

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions