Come ottenere gli ultimi stack frame invece di una traccia stack completa in Java 8?

Sep 05 2020

C'è l'API Stack Walking in Java 9 che ci consente di ottenere solo l'ultimo paio di stack frame. Ma sono bloccato con Java 8. In Java 8 possiamo ottenere la traccia dello stack con uno Thread.currentThread().getStackTrace()o new Throwable().getStackTrace()entrambi i metodi sono lenti perché costruiscono l'intera traccia dello stack. C'è un modo per limitare il numero di frame nella traccia dello stack (per costruire solo gli ultimi 3 frame per esempio)?

Aggiornamento: ho testato il codice di Stephen e funziona davvero bene. Riduce davvero il tempo di esecuzione di un fattore costante a seconda della profondità scelta. Quello che ho trovato interessante è che non riduce il tempo per creare una nuova eccezione anche se la traccia dello stack viene costruita durante la creazione dell'eccezione e archiviata in un formato interno. La maggior parte del tempo viene impiegata per convertire questo formato interno in un StackTraceElementquando getStackTrace()viene chiamato. Quindi, se la traccia dello stack è meno profonda, ovviamente ci vuole meno tempo per convertirla. Ma perché non ci vuole meno tempo per creare la rappresentazione interna della traccia dello stack quando viene creata l'eccezione ( new Throwable())?


    public static void main(String[] args) throws IOException {
        f1();
    }

    private static void f1() {
       f2();
    }

    private static void f2() {
       f3();
    }

    private static void f3() {
       f4();
    }

    private static void f4() {
        f5();
    }

    private static void f5() {
        int sum = 0;
        long l = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            Throwable throwable = new Throwable();
            //comment out to test the internal representation -> StackTraceElement conversion
            //StackTraceElement[] trace = throwable.getStackTrace();
            //sum += trace.length;
        }
        long l2 = System.currentTimeMillis();
        System.out.println(l2-l);
        System.out.println(sum);
    }

Aggiornamento 2: cercando di eliminare i problemi di benchamrk, ho memorizzato tutte le mie Throwableistanze in un array, quindi alla fine dell'esecuzione ne ho recuperato una casualmente e ho stampato la traccia dello stack. Penso che questo impedirebbe qualsiasi ottimizzazione fatta per eliminare le new Throwable()chiamate.

        int sizez = 2000000;
        Throwable[] th = new Throwable[sizez];
        for (int i = 0; i < sizez; i++) {
            th[i] = new Throwable();
            //StackTraceElement[] trace = throwable.getStackTrace();
            //sum += trace.length;
        }
        long l2 = System.currentTimeMillis();
        System.out.println(l2-l);
        System.out.println(sum);

        Random rand = new Random();
        StackTraceElement[] trace = th[rand.nextInt(sizez)].getStackTrace();
        System.out.println(trace[0]);

Risposte

3 StephenC Sep 05 2020 at 11:16

È possibile utilizzare l'opzione JVM -XX:MaxJavaStackTraceDepth=depthper limitare la dimensione degli stacktrace.

Tuttavia, questo vale per tutti gli stacktrace e non sembra che sia ciò di cui hai bisogno. Non credo che ci sia un modo per limitare le dimensioni caso per caso.