首页 > 解决方案 > Java 类是用调试符号编译的,但堆栈跟踪中没有显示行号?

问题描述

我收到了来自使用我编写的 Java 库的人的日志,但令人困惑的是,堆栈跟踪没有列出我的方法的行号。

这个问题似乎表明该类是在没有调试符号的情况下编译的,但是如果我.class从他们正在使用的 JAR 中获取有问题的文件并javap -v在其上运行,我可以看到它实际上是使用调试符号编译的,并且有一个有问题的方法的 LineNumberTable:

      LineNumberTable:
        line 387: 0
        line 389: 4
        line 391: 11
        line 393: 23
        line 395: 30
        line 397: 62
        line 399: 69
        line 412: 101
        line 413: 107
        line 414: 116
        line 415: 122
        line 416: 134
        line 417: 141
        line 418: 150
        line 419: 156
        line 421: 168
        line 422: 178
        line 423: 192
        line 425: 206
        line 431: 214
        line 428: 217
        line 430: 219
        line 432: 224

所以我的问题就变成了,即使我已经确认.class文件具有调试符号,什么可能导致行号不显示在堆栈跟踪中?如果重要的话,这是在 Android 的上下文中。不,它不是 ProGuard 或剥离调试符号的东西,因为行号列在堆栈跟踪的其他部分。

标签: javaandroidstack-tracebytecodedebug-symbols

解决方案


所以我想通了。

一个简短的说明,我可能应该在我的问题中提到这一点:有问题的堆栈跟踪不是崩溃/异常的结果,而是打印出来以显示线程在看门狗杀死它之前的位置,因为它没有响应.

  1. 如果不是死锁,至少是长线程争用造成的
  2. synchronized当一个线程等待调用一个方法而另一个线程正在执行另一个方法时,堆栈跟踪的样子synchronized与 ART 与 JVM 不同!

在 ART 上,顶部堆栈帧将显示为方法中没有行号,但在 JVM 中它将显示为方法中的第一行,并带有行号。

这是 Android 的“完整、最小、可重现”的示例:

public class MainActivity extends Activity
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        launchThread("TH1");
        sleep(100);
        launchThread("TH2");
        sleep(20);
        dumpThreadTraces();
    }

    void launchThread(String name)
    {
        Thread thread = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                doThings();
            }
        });

        thread.setName(name);
        thread.start();
    }

    synchronized void doThings()
    {
        sleep(1000);
    }

    void dumpThreadTraces()
    {
        Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();

        Set<Thread> threads = traces.keySet();

        for(Thread th : threads)
        {
            if(th.getName().startsWith("TH"))
            {
                logStackTrace(th, traces.get(th));
            }
        }
    }

    void logStackTrace(Thread thread, StackTraceElement[] stackTrace)
    {
        System.out.printf("thread id=%d name=\"%s\"\n", thread.getId(), thread.getName());
        logStackFrames(stackTrace);
    }

    void logStackFrames(StackTraceElement[] stackTrace)
    {
        for (StackTraceElement frame : stackTrace)
        {
            System.out.printf("    at %s\n", frame.toString());
        }
    }

    void sleep(int millis)
    {
        try
        {
            Thread.sleep(millis);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

运行时,将打印以下内容到 logcat:

I/System.out: thread id=2051 name="TH1"
I/System.out:     at java.lang.Thread.sleep(Native Method)
I/System.out:     at java.lang.Thread.sleep(Thread.java:371)
I/System.out:     at java.lang.Thread.sleep(Thread.java:313)
I/System.out:     at com.domain.helloworld.MainActivity.sleep(MainActivity.java:94)
I/System.out:     at com.domain.helloworld.MainActivity.doThings(MainActivity.java:58)
I/System.out:     at com.domain.helloworld.MainActivity$1.run(MainActivity.java:48)
I/System.out:     at java.lang.Thread.run(Thread.java:761)
I/System.out: thread id=2052 name="TH2"
I/System.out:     at com.domain.helloworld.MainActivity.doThings(MainActivity.java)
I/System.out:     at com.domain.helloworld.MainActivity$1.run(MainActivity.java:48)
I/System.out:     at java.lang.Thread.run(Thread.java:761)

请注意,对于线程 2,顶部堆栈跟踪元素的行号未打印!

at com.domain.helloworld.MainActivity.doThings(MainActivity.java)

推荐阅读