O que é um rastreamento de pilha e como posso usá-lo para depurar meus erros de aplicativo?

643

Às vezes, quando executo meu aplicativo, ocorre um erro semelhante a:

Exception in thread "main" java.lang.NullPointerException
        at com.example.myproject.Book.getTitle(Book.java:16)
        at com.example.myproject.Author.getBookTitles(Author.java:25)
        at com.example.myproject.Bootstrap.main(Bootstrap.java:14)

As pessoas se referiram a isso como um "rastreamento de pilha". O que é um rastreamento de pilha? O que isso pode me dizer sobre o erro que está acontecendo no meu programa?


Sobre esta pergunta - muitas vezes vejo uma pergunta surgir na qual um programador iniciante está "recebendo um erro" e simplesmente cola seu rastreamento de pilha e algum bloco aleatório de código sem entender o que é o rastreamento de pilha ou como pode usá-lo. Esta pergunta pretende ser uma referência para programadores iniciantes que possam precisar de ajuda para entender o valor de um rastreamento de pilha.

Rob Hruska
fonte
25
Além disso, se uma linha de rastreamento de pilha não contiver o nome do arquivo e um número de linha, a classe dessa linha não foi compilada com informações de depuração.
Thorbjørn Ravn Andersen

Respostas:

589

Em termos simples, um rastreamento de pilha é uma lista das chamadas de método que o aplicativo estava no meio de quando uma exceção foi lançada.

Exemplo Simples

Com o exemplo dado na pergunta, podemos determinar exatamente onde a exceção foi lançada no aplicativo. Vamos dar uma olhada no rastreamento de pilha:

Exception in thread "main" java.lang.NullPointerException
        at com.example.myproject.Book.getTitle(Book.java:16)
        at com.example.myproject.Author.getBookTitles(Author.java:25)
        at com.example.myproject.Bootstrap.main(Bootstrap.java:14)

Este é um rastreamento de pilha muito simples. Se começarmos no início da lista de "em ...", podemos saber onde ocorreu o erro. O que estamos procurando é a chamada de método superior que faz parte do nosso aplicativo. Nesse caso, é:

at com.example.myproject.Book.getTitle(Book.java:16)

Para depurar isso, podemos abrir Book.javae olhar para a linha 16, que é:

15   public String getTitle() {
16      System.out.println(title.toString());
17      return title;
18   }

Isso indicaria que algo (provavelmente title) está nullno código acima.

Exemplo com uma cadeia de exceções

Às vezes, os aplicativos capturam uma exceção e a lançam novamente como a causa de outra exceção. Isso normalmente se parece com:

34   public void getBookIds(int id) {
35      try {
36         book.getId(id);    // this method it throws a NullPointerException on line 22
37      } catch (NullPointerException e) {
38         throw new IllegalStateException("A book has a null property", e)
39      }
40   }

Isso pode fornecer um rastreamento de pilha parecido com:

Exception in thread "main" java.lang.IllegalStateException: A book has a null property
        at com.example.myproject.Author.getBookIds(Author.java:38)
        at com.example.myproject.Bootstrap.main(Bootstrap.java:14)
Caused by: java.lang.NullPointerException
        at com.example.myproject.Book.getId(Book.java:22)
        at com.example.myproject.Author.getBookIds(Author.java:36)
        ... 1 more

A diferença deste é o "Causado por". Às vezes, as exceções terão várias seções "Causadas por". Para isso, normalmente você deseja encontrar a "causa raiz", que será uma das seções mais baixas "Causadas por" no rastreamento de pilha. No nosso caso, é:

Caused by: java.lang.NullPointerException <-- root cause
        at com.example.myproject.Book.getId(Book.java:22) <-- important line

Novamente, com essa exceção, gostaríamos de olhar para a linha 22de Book.javapara ver o que pode causar o NullPointerExceptionaqui.

Exemplo mais assustador com o código da biblioteca

Normalmente, os rastreamentos de pilha são muito mais complexos que os dois exemplos acima. Aqui está um exemplo (é longo, mas demonstra vários níveis de exceções encadeadas):

javax.servlet.ServletException: Something bad happened
    at com.example.myproject.OpenSessionInViewFilter.doFilter(OpenSessionInViewFilter.java:60)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.example.myproject.ExceptionHandlerFilter.doFilter(ExceptionHandlerFilter.java:28)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.example.myproject.OutputBufferFilter.doFilter(OutputBufferFilter.java:33)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
    at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:943)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:756)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
    at org.mortbay.jetty.bio.SocketConnector$Connection.run(SocketConnector.java:228)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
Caused by: com.example.myproject.MyProjectServletException
    at com.example.myproject.MyServlet.doPost(MyServlet.java:169)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
    at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
    at com.example.myproject.OpenSessionInViewFilter.doFilter(OpenSessionInViewFilter.java:30)
    ... 27 more
Caused by: org.hibernate.exception.ConstraintViolationException: could not insert: [com.example.myproject.MyEntity]
    at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:96)
    at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
    at org.hibernate.id.insert.AbstractSelectingDelegate.performInsert(AbstractSelectingDelegate.java:64)
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2329)
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2822)
    at org.hibernate.action.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:71)
    at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:268)
    at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:321)
    at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
    at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:130)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210)
    at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:56)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195)
    at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:50)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
    at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:705)
    at org.hibernate.impl.SessionImpl.save(SessionImpl.java:693)
    at org.hibernate.impl.SessionImpl.save(SessionImpl.java:689)
    at sun.reflect.GeneratedMethodAccessor5.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:344)
    at $Proxy19.save(Unknown Source)
    at com.example.myproject.MyEntityService.save(MyEntityService.java:59) <-- relevant call (see notes below)
    at com.example.myproject.MyServlet.doPost(MyServlet.java:164)
    ... 32 more
Caused by: java.sql.SQLException: Violation of unique constraint MY_ENTITY_UK_1: duplicate value(s) for column(s) MY_COLUMN in statement [...]
    at org.hsqldb.jdbc.Util.throwError(Unknown Source)
    at org.hsqldb.jdbc.jdbcPreparedStatement.executeUpdate(Unknown Source)
    at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeUpdate(NewProxyPreparedStatement.java:105)
    at org.hibernate.id.insert.AbstractSelectingDelegate.performInsert(AbstractSelectingDelegate.java:57)
    ... 54 more

Neste exemplo, há muito mais. O que mais nos preocupa é procurar métodos que sejam do nosso código , que seriam qualquer coisa no com.example.myprojectpacote. No segundo exemplo (acima), primeiro queremos procurar a causa raiz, que é:

Caused by: java.sql.SQLException

No entanto, todas as chamadas de método abaixo são código de biblioteca. Então, passaremos para o "Causado por" acima dele e procuraremos a primeira chamada de método originada em nosso código, que é:

at com.example.myproject.MyEntityService.save(MyEntityService.java:59)

Como nos exemplos anteriores, devemos olhar MyEntityService.javaon-line 59, porque é onde esse erro se originou (este é um pouco óbvio o que deu errado, já que o SQLException indica o erro, mas o procedimento de depuração é o que estamos procurando).

Rob Hruska
fonte
3
@RobHruska - Muito bem explicado. +1. Você conhece algum analisador que aceita o rastreamento de exceção como uma sequência e fornece métodos úteis para analisar o rastreamento de pilha? - como getLastCausedBy () ou getCausedByForMyAppCode ("com.example.myproject")
Andy Dufresne
1
@AndyDufresne - Eu não encontrei nenhum, mas também não olhei de verdade.
precisa
1
Melhoria sugerida: explique a primeira linha de um rastreamento de pilha que começa com Exception in thread "main"seu primeiro exemplo. Eu acho que seria especialmente útil explicar que essa linha geralmente é acompanhada de uma mensagem, como o valor de uma variável, que pode ajudar a diagnosticar o problema. Tentei fazer uma edição, mas estou lutando para encaixar essas idéias na estrutura existente da sua resposta.
Code-Apprentice
5
Além disso, o java 1.7 adicionou "Suprimido:" - que lista os rastreamentos de pilha de exceções suprimidos antes de exibir "Causado por:" para esta exceção. Ele é usado automaticamente pela construção try-with-resource: docs.oracle.com/javase/specs/jls/se8/html/… e contém exceções, se houver, que foram lançadas durante o fechamento do (s) recurso (s).
Dhblah 01/07/2015
Existe um JEP openjdk.java.net/jeps/8220715 que visa melhorar ainda mais a compreensibilidade de especialmente NPEs, fornecendo detalhes como "Não é possível gravar o campo 'nullInstanceField' porque 'this.nullInstanceField' é nulo".
Mahatma_Fatal_Error 5/06/19
80

Estou postando esta resposta para que a resposta superior (quando classificada por atividade) não seja a que está completamente errada.

O que é um Stacktrace?

Um stacktrace é uma ferramenta de depuração muito útil. Ele mostra a pilha de chamadas (ou seja, a pilha de funções que foram chamadas até esse ponto) no momento em que uma exceção não capturada foi lançada (ou no momento em que o rastreamento de pilha foi gerado manualmente). Isso é muito útil, pois não apenas mostra onde o erro ocorreu, mas também como o programa foi parar naquele local do código. Isso leva à próxima pergunta:

O que é uma exceção?

Uma exceção é o que o ambiente de tempo de execução usa para informar que ocorreu um erro. Exemplos populares são NullPointerException, IndexOutOfBoundsException ou ArithmeticException. Cada uma delas é causada quando você tenta fazer algo que não é possível. Por exemplo, uma NullPointerException será lançada quando você tentar desreferenciar um objeto Nulo:

Object a = null;
a.toString();                 //this line throws a NullPointerException

Object[] b = new Object[5];
System.out.println(b[10]);    //this line throws an IndexOutOfBoundsException,
                              //because b is only 5 elements long
int ia = 5;
int ib = 0;
ia = ia/ib;                   //this line throws an  ArithmeticException with the 
                              //message "/ by 0", because you are trying to
                              //divide by 0, which is not possible.

Como devo lidar com Stacktraces / Exceptions?

Primeiro, descubra o que está causando a exceção. Tente pesquisar no Google o nome da exceção para descobrir qual é a causa dessa exceção. Na maioria das vezes, isso é causado por código incorreto. Nos exemplos acima, todas as exceções são causadas por código incorreto. Portanto, para o exemplo NullPointerException, você pode garantir que anunca seja nulo naquele momento. Você pode, por exemplo, inicializar aou incluir uma verificação como esta:

if (a!=null) {
    a.toString();
}

Dessa forma, a linha incorreta não será executada se a==null. O mesmo vale para os outros exemplos.

Às vezes, você não pode ter certeza de que não recebe uma exceção. Por exemplo, se você estiver usando uma conexão de rede em seu programa, não poderá impedir que o computador perca sua conexão com a Internet (por exemplo, não poderá impedir o usuário de desconectar a conexão de rede do computador). Nesse caso, a biblioteca de rede provavelmente lançará uma exceção. Agora você deve capturar a exceção e lidar com isso. Isso significa que, no exemplo com a conexão de rede, você deve tentar reabrir a conexão ou notificar o usuário ou algo assim. Além disso, sempre que você usar o catch, sempre pegue apenas a exceção que deseja capturar, não use instruções de catch abrangentes comocatch (Exception e)isso pegaria todas as exceções. Isso é muito importante, pois, caso contrário, você poderá pegar acidentalmente a exceção errada e reagir da maneira errada.

try {
    Socket x = new Socket("1.1.1.1", 6789);
    x.getInputStream().read()
} catch (IOException e) {
    System.err.println("Connection could not be established, please try again later!")
}

Por que não devo usar catch (Exception e)?

Vamos usar um pequeno exemplo para mostrar por que você não deve apenas pegar todas as exceções:

int mult(Integer a,Integer b) {
    try {
        int result = a/b
        return result;
    } catch (Exception e) {
        System.err.println("Error: Division by zero!");
        return 0;
    }
}

O que esse código está tentando fazer é capturar o ArithmeticExceptioncausado por uma possível divisão por 0. Mas também captura um possível NullPointerExceptionque é lançado se é aou bnão null. Isso significa que você pode obter um, NullPointerExceptionmas o tratará como uma ArithmeticException e provavelmente fará a coisa errada. Na melhor das hipóteses, você ainda sente falta de uma NullPointerException. Coisas assim tornam a depuração muito mais difícil, então não faça isso.

TLDR

  1. Descubra qual é a causa da exceção e corrija-a, para que ela não ative a exceção.
  2. Se 1. não for possível, pegue a exceção específica e lide com ela.

    • Nunca basta adicionar uma tentativa / captura e ignorar a exceção! Não faça isso!
    • Nunca use catch (Exception e), sempre pegue exceções específicas. Isso vai lhe poupar muitas dores de cabeça.
Dakkaron
fonte
1
boa explicação de por que devemos evitar bug mascaramento
Sudip Bhandari
2
Estou postando esta resposta para que a resposta principal (quando classificada por atividade) não seja totalmente errada. Não faço idéia de qual delas você está falando, já que isso provavelmente já mudou. Mas a resposta aceita é definitivamente mais interresting;)
AxelH
1
O que eu quis dizer foi excluído até agora, tanto quanto eu sei. Ele basicamente dizia "basta colocar uma tentativa {} catch (Exceção e) {} e ignorar todos os erros". A resposta aceita é muito mais antiga que a minha, então eu pretendia dar uma visão um pouco diferente sobre o assunto. Não acho que ajude alguém a copiar a resposta de outra pessoa ou a cobrir o que outras pessoas já cobriram bem.
Dakkaron
É enganoso dizer "Não capturar exceção", esse é apenas um caso de uso. Seu exemplo é ótimo, mas e onde você está no topo do seu loop de threads (execução interna)? SEMPRE você deve capturar a exceção (ou talvez Throwable) e registrá-la para que não desapareça invisivelmente (as exceções lançadas na execução geralmente não são registradas corretamente, a menos que você tenha configurado seu thread / logger para isso).
Bill K
1
Não incluí este caso especial, pois só importa com multithreading. Na segmentação única, uma exceção vazada mata o programa e é registrada visivelmente. Se alguém não sabe como lidar com exceções corretamente, geralmente também não sabe como usar o multithreading.
Dakkaron 08/03
21

Para adicionar o que Rob mencionou. Definir pontos de interrupção no seu aplicativo permite o processamento passo a passo da pilha. Isso permite que o desenvolvedor use o depurador para ver em que ponto exato o método está fazendo algo imprevisto.

Como Rob usou o NullPointerException(NPE) para ilustrar algo comum, podemos ajudar a remover esse problema da seguinte maneira:

se tivermos um método que aceita parâmetros como: void (String firstName)

Em nosso código, gostaríamos de avaliar que firstNamecontém um valor, faríamos assim:if(firstName == null || firstName.equals("")) return;

O acima exposto nos impede de usar firstNamecomo um parâmetro não seguro. Portanto, fazendo verificações nulas antes do processamento, podemos ajudar a garantir que nosso código seja executado corretamente. Para expandir um exemplo que utiliza um objeto com métodos, podemos procurar aqui:

if(dog == null || dog.firstName == null) return;

A descrição acima é a ordem correta para verificar se há nulos. Começamos com o objeto base, neste caso, dog, e depois começamos a percorrer a árvore de possibilidades para garantir que tudo seja válido antes do processamento. Se a ordem fosse revertida, um NPE poderia ser lançado e nosso programa falharia.

Woot4Moo
fonte
Acordado. Essa abordagem pode ser usada para descobrir qual referência em uma declaração é nullquando a NullPointerExceptionestá sendo examinada, por exemplo.
Rob Hruska
16
Ao lidar com String, se você quiser usar o método equals, acho melhor usar a constante no lado esquerdo da comparação, assim: Em vez de: if (firstName == null || firstName.equals ("" )) Retorna; Eu sempre uso: if ((""). Equals (firstName)) Isso impede a exceção Nullpointer
Torres
15

Há mais um recurso de rastreamento de pilha oferecido pela família Throwable - a possibilidade de manipular informações de rastreamento de pilha.

Comportamento padrão:

package test.stack.trace;

public class SomeClass {

    public void methodA() {
        methodB();
    }

    public void methodB() {
        methodC();
    }

    public void methodC() {
        throw new RuntimeException();
    }

    public static void main(String[] args) {
        new SomeClass().methodA();
    }
}

Rastreio de pilha:

Exception in thread "main" java.lang.RuntimeException
    at test.stack.trace.SomeClass.methodC(SomeClass.java:18)
    at test.stack.trace.SomeClass.methodB(SomeClass.java:13)
    at test.stack.trace.SomeClass.methodA(SomeClass.java:9)
    at test.stack.trace.SomeClass.main(SomeClass.java:27)

Rastreio de pilha manipulado:

package test.stack.trace;

public class SomeClass {

    ...

    public void methodC() {
        RuntimeException e = new RuntimeException();
        e.setStackTrace(new StackTraceElement[]{
                new StackTraceElement("OtherClass", "methodX", "String.java", 99),
                new StackTraceElement("OtherClass", "methodY", "String.java", 55)
        });
        throw e;
    }

    public static void main(String[] args) {
        new SomeClass().methodA();
    }
}

Rastreio de pilha:

Exception in thread "main" java.lang.RuntimeException
    at OtherClass.methodX(String.java:99)
    at OtherClass.methodY(String.java:55)
przemek hertel
fonte
2
Não sei como me sinto sobre isso ... com a natureza do encadeamento, aconselho os novos desenvolvedores a não definirem seu próprio rastreamento de pilha.
PeonProgrammer
15

Para entender o nome : Um rastreamento de pilha é uma lista de exceções (ou pode-se dizer uma lista de "Causar por"), desde a exceção mais superficial (por exemplo, exceção da camada de serviço) até a mais profunda (por exemplo, exceção do banco de dados). Assim como a razão pela qual chamamos de 'pilha' é porque a pilha é First in Last out (FILO), a exceção mais profunda ocorreu no início, então uma cadeia de exceções gerou uma série de consequências, a exceção de superfície foi a última um aconteceu a tempo, mas o vemos em primeiro lugar.

Chave 1 : Uma coisa complicada e importante aqui precisa ser entendida é: a causa mais profunda pode não ser a "causa raiz", porque se você escrever algum "código incorreto", poderá causar alguma exceção embaixo, que é mais profunda que a sua camada. Por exemplo, uma consulta sql incorreta pode causar a redefinição da conexão SQLServerException no bottem, em vez de um erro syndax, que pode estar no meio da pilha.

-> Localize a causa raiz no meio é o seu trabalho. insira a descrição da imagem aqui

Chave 2 : Outra coisa complicada, mas importante, está dentro de cada bloco "Causa por", a primeira linha foi a camada mais profunda e aconteceu em primeiro lugar nesse bloco. Por exemplo,

Exception in thread "main" java.lang.NullPointerException
        at com.example.myproject.Book.getTitle(Book.java:16)
           at com.example.myproject.Author.getBookTitles(Author.java:25)
               at com.example.myproject.Bootstrap.main(Bootstrap.java:14)

Book.java:16 foi chamado por Auther.java:25, que foi chamado por Bootstrap.java:14, Book.java:16 foi a causa raiz. Anexe aqui um diagrama e classifique a pilha de rastreamento em ordem cronológica. insira a descrição da imagem aqui

Kevin Li
fonte
8

Apenas para adicionar aos outros exemplos, existem classes internas (aninhadas) que aparecem com o $sinal. Por exemplo:

public class Test {

    private static void privateMethod() {
        throw new RuntimeException();
    }

    public static void main(String[] args) throws Exception {
        Runnable runnable = new Runnable() {
            @Override public void run() {
                privateMethod();
            }
        };
        runnable.run();
    }
}

Resultará neste rastreamento de pilha:

Exception in thread "main" java.lang.RuntimeException
        at Test.privateMethod(Test.java:4)
        at Test.access$000(Test.java:1)
        at Test$1.run(Test.java:10)
        at Test.main(Test.java:13)
Eugene S
fonte
5

As outras postagens descrevem o que é um rastreamento de pilha, mas ainda pode ser difícil trabalhar.

Se você obtiver um rastreamento de pilha e desejar rastrear a causa da exceção, um bom ponto de partida para entender isso é usar o Java Stack Trace Console no Eclipse . Se você usar outro IDE, pode haver um recurso semelhante, mas esta resposta é sobre o Eclipse.

Primeiro, verifique se todas as suas fontes Java estão acessíveis em um projeto Eclipse.

Na perspectiva Java , clique na guia Console (geralmente na parte inferior). Se a visualização Console não estiver visível, vá para a opção de menu Janela -> Mostrar Visualização e selecione Console .

Em seguida, na janela do console, clique no botão a seguir (à direita)

Botão de consoles

e, em seguida, selecione Java Stack Trace Console na lista suspensa.

Cole seu rastreamento de pilha no console. Ele fornecerá uma lista de links para o seu código-fonte e qualquer outro código-fonte disponível.

Isto é o que você pode ver (imagem da documentação do Eclipse):

Diagrama da documentação do Eclipse

A chamada de método mais recente feita será a parte superior da pilha, que é a linha superior (excluindo o texto da mensagem). Descendo a pilha volta no tempo. A segunda linha é o método que chama a primeira linha, etc.

Se você estiver usando software de código aberto, pode ser necessário fazer o download e anexar ao seu projeto as fontes, se desejar examinar. Faça o download dos jars de origem, em seu projeto, abra a pasta Bibliotecas referenciadas para encontrar seu jar para o módulo de código-fonte aberto (aquele com os arquivos de classe) e clique com o botão direito do mouse, selecione Propriedades e anexe o jar de origem.

rghome
fonte