Stacktrace与类名中的数字 - 为什么? [英] Stacktrace with numbers in class names - why?

查看:136
本文介绍了Stacktrace与类名中的数字 - 为什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当finalizeOperation正在运行时(在更大的应用程序中生产):

When finalizeOperation is running (in production in a larger application):

public interface OperationFinalizerHook {
    void onOperationFinalize(Operation operation, Object context);
}
private final List<OperationFinalizerHook> operationFinalizeHooks = new ArrayList<>();
...
public void finalizeOperation(Object context) {
    final Operation operation = getOperation();
    operationFinalizeHooks.forEach(hook -> hook.onOperationFinalize(operation, context));
}

在构建调用树/堆栈跟踪之后:

following call tree/stacktrace is built:

11 at com.company.SomeClass.lambda$finalizeOperation$0 (SomeClass.java:51)
12 at com.company.SomeClass$$Lambda$135/2085968933.accept (Unknown source)
13 at java.util.ArrayList.forEach (ArrayList.java:1249)
14 at com.company.SomeClass.finalizeOperation (SomeClass.java:51)

我对第12行感兴趣 - 这个名字来自哪里?为什么我会期望一个类的名字随机数?

I'm interested in line 12 - where does this name come from? Why are there random numbers where I would expect the name of a class?

编辑:
这是来自博客文章提到 Niklas P:

Here's the code from blog post mentioned by Niklas P:

public class Test {
    public static void main(String... args) {
        List<String> names = Arrays.asList("adam", "");
        Stream lengths = names.stream().map(name -> check(name));
        lengths.count();
    }
    public static int check(String s) {
        if (s.equals(""))
            throw new IllegalArgumentException();
        return s.length();
    }
}

但结果不包含此数字名称, stacktrace是(jdk8u102):

But the result does not contain this numeric name, the stacktrace is (jdk8u102):

Exception in thread "main" java.lang.IllegalArgumentException
    at Test.check(Test.java:19)
    at Test.lambda$main$0(Test.java:12)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
    at Test.main(Test.java:14)

在jdk8u25上,数字为那里:

On jdk8u25 the number is there:

Exception in thread "main" java.lang.IllegalArgumentException
    at Test.check(Test.java:18)
    at Test.lambda$main$0(Test.java:11)
    at Test$$Lambda$1/1554547125.apply(Unknown Source)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.LongPipeline.reduce(LongPipeline.java:438)
    at java.util.stream.LongPipeline.sum(LongPipeline.java:396)
    at java.util.stream.ReferencePipeline.count(ReferencePipeline.java:526)
    at Test.main(Test.java:13)


推荐答案

有两个重叠的问题这里。首先,当您将lambda表达式转换为对象类型时,必须有一些实现功能接口的东西 - 细节不是那么重要;唯一需要了解的是,将有一些实现接口并调用lambda表达式的代码或方法引用的目标。

There are two overlapping issues here. First, when you convert a lambda expression to an object type, there has to be something that implements the functional interface—the details are not so important; the only thing you have to understand that there will be something implementing the interface and invoking the code of your lambda expression or the target of a method reference.

当前的JRE实现生成匿名类,顾名思义,它们不依赖于它们的名称是唯一的。类名后面打印的数字是此属性的工件。无论哪种方式,使用数字或不使用,您都无法使用 ClassLoader 查找这些类。

The current JRE implementation generates anonymous classes, which, as the name suggests, do not depend on their name for being unique. The number printed after the class name is an artifact of this property. Either way, with number or without, you can’t lookup these classes with a ClassLoader.

拥有合成工件在堆栈跟踪中对Java来说并不新鲜。使用内部类时会生成存取方法,例如。

Having synthetic artifacts in a stack trace is nothing new to Java. There are generated accessor methods when using inner classes, e.g.

import java.util.*;
import java.util.function.Function;
import java.util.stream.Stream;

public class Test {
    public static void main(String... args) {
        List<String> names = Arrays.asList("adam", "");
        Stream lengths = names.stream().map(new Function<String, Integer>() {
            public Integer apply(String name) {
                return check(name);
            }
        });
        lengths.count();
    }
    private static int check(String s) {
        if (s.equals(""))
            throw new IllegalArgumentException();
        return s.length();
    }
}



Exception in thread "main" java.lang.IllegalArgumentException
    at Test.check(Test.java:17)
    at Test.access$000(Test.java:5)
    at Test$1.apply(Test.java:10)
    at Test$1.apply(Test.java:8)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
    … (shortened it a bit)
    at java.util.stream.ReferencePipeline.count(ReferencePipeline.java:526)
    at Test.main(Test.java:13)

注意堆栈跟踪中存在访问$ 000 ,这不会出现在源代码中;它的关联行号是没有意义的,它只是外部类定义的开始。

Note the presence of access$000 in the stack trace, which doesn’t appear in source code; it’s associated line number is meaningless, it’s just the beginning of the outer class definition.

现在,似乎堆栈跟踪生成发生了变化,省略了合成最近JRE版本中的匿名类的成员。这也将影响反射调用的堆栈轨迹,例如,使用 MethodHandle 实例。这可能被认为对大多数用例有用,但它也暗示在某些情况下调用者和被调用者之间可能存在不匹配,因为堆栈跟踪报告调用者调用接口方法,但最终在其他地方,例如

Now, it seems that there was a change in the stack trace generation, to omit synthetic members of anonymous classes in recent JRE versions. This will also affect the stack traces of reflective invocations, e.g. using MethodHandle instances. This might be considered useful for most use cases, but it also implies that there might be a mismatch between caller and callee in some cases, as the stack trace reports that the caller invokes an interface method, but ends up somewhere else, e.g.

import java.util.*;
import java.util.stream.Stream;

public class Test {
    public static void main(String... args) {
        Stream.of("adam", "", null).filter("foo"::contains).count();
    }
}

将打印

Exception in thread "main" java.lang.NullPointerException
    at java.lang.String.contains(String.java:2133)
    at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:174)
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
…

其中 ReferencePipeline.java:174 包含调用 Predicate 接口的 accept 方法,但最后在方法中包含 String 。我们可以最大化:

where ReferencePipeline.java:174 contains the invocation of the accept method of the Predicate interface, but ends up in the method contains of class String. We can max that out:

import java.util.*;
import java.util.stream.Stream;

public class Test {
    public static void main(String... args) {
        Stream.of("adam", "", null).filter(String::isEmpty).count();
    }
}

将在最新的JRE上产生以下内容:

will produce the following on the most recent JREs:

Exception in thread "main" java.lang.NullPointerException
    at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:174)
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.LongPipeline.reduce(LongPipeline.java:438)
    at java.util.stream.LongPipeline.sum(LongPipeline.java:396)
    at java.util.stream.ReferencePipeline.count(ReferencePipeline.java:526)
    at Test.main(Test.java:6)

省略最终将调用的合成代码isEmpty String 实例上,由于 ReferencePipeline.java:174 只包含一个调用,这可能更令人困惑一个接口方法和接口实例不是 null (这个代码早已经过检查了。)

omitting the synthetic code which will eventually invoke isEmpty on the String instance, which can be even more confusing as ReferencePipeline.java:174 only contains an invocation of an interface method and the interface instance is not null (this has been checked much earlier in that code).

请注意,这是一个动态的开发。使用Java 9,将有 StackWalker API,允许应用程序使用已配置的隐藏/反射堆栈帧处理生成自己的快照。一旦应用程序使用此API创建可预测的堆栈跟踪,即不再依赖于 Throwable.getStackTrace()的特定行为,throwable的行为可以通过JVM选项进行配置或系统属性......

Note that this is a development in motion. With Java 9, there will be the StackWalker API which will allow applications to generate their own snapshots with a configured treatment of hidden/reflection stack frames. Once applications use this API to create predictable stack traces, i.e. do not rely on a specific behavior of Throwable.getStackTrace() anymore, the behavior of throwables could become configurable via JVM options or system properties…

这篇关于Stacktrace与类名中的数字 - 为什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆