Stacktrace与类名中的数字 - 为什么?

时间:2016-10-04 21:42:34

标签: java java-8 jvm

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

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)

构建了以下调用树/堆栈跟踪:

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();
    }
}

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

编辑: 以下是来自blog post mentioned by Niklas P的代码:

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)

但结果不包含此数字名称,堆栈跟踪是(jdk8u102):

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)

在jdk8u25上有数字:

$img = "img"; // input name="img"
$this->upload->do_upload($img);

2 个答案:

答案 0 :(得分:5)

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

当前的JRE实现生成匿名类,顾名思义,它们不依赖于它们的名称是唯一的。类名后面打印的数字是此属性的工件。无论哪种方式,无论是否有数字,您都无法使用@ContextConfiguration(classes = {WebConfig.class}) 来查找这些类。

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

ClassLoader
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) ,它不会出现在源代码中;它的关联行号是没有意义的,它只是外部类定义的开始。

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

MethodHandle

将打印

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接口的accept方法的调用,但最终在类Predicate的方法contains中。我们可以最大限度地解决这个问题:

String

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

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();
    }
}

省略最终将在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方法的调用,因此更加令人困惑。 interface实例不是interface(在该代码中已经检查过很久)。

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

答案 1 :(得分:1)

这些数字来自匿名类,为lambda操作创建的JVM - 请参见:the-dark-side-of-lambda-expressions-in-java-8