2013-09-04 3 views
1

Из-за моей новой работы у меня много работы с Java, и теперь я попадаю в крошечные детали. Очевидно, что Java-код относится к некоторым исключениям. Мне было интересно:Насколько глубоко должен быть блок try-catch на Java?

Действительно ли вызывающий стек влияет на производительность блока try-catch? То есть следует ли избегать попытки использовать функцию, которая вызывает функцию, которая ... и идет слишком глубоко?

Я читал, что блоки try-catch влияют только на производительность при исключении. Однако, имеет ли значение вопрос, что они пузырятся?

+8

имо это больше о читаемости, чем производительность. Java довольно хороша для оптимизации. – RNJ

+0

IMHO, извлечение stacktrace является дорогостоящим, но это произойдет только в том случае, если вы достигнете блока catch. –

+1

Зачем беспокоиться о микро оптимизации? Сосредоточьтесь на читабельности кода. Такие глубокие уровни исключений являются признаком запаха кода. Сначала измените это. –

ответ

1

Исключения стоят дорого. При использовании создается трассировка стека. Если вы можете проверить исключение, сделайте это. Не используйте try..catch для управления потоком. Если вы не можете проверить/подтвердить, используйте try..catch; пример будет выполнять операции ввода-вывода.

Когда я вижу код с большим количеством блоков try..catch, моя ближайшая мысль: «Это плохой дизайн!».

+0

Изменить * вы проверяете исключение *, чтобы ** избегать исключения, если возможно **. –

+0

Как ни странно, это не всегда так. Исключения в некоторых случаях быстрее. Использование их для управления потоком обычно игнорируется по смысловым и согласованным причинам. –

+0

Всё зависит от меня. Исключения относительно недороги для большинства программ. Если реализованная функция отличается высокой производительностью, следует избегать исключений (например, в играх), но для веб-приложения, где петли происходят не более нескольких сотен раз, исключение бросания намного лучше, чем обработка кодов ошибок и нулей. – allprog

-1

Трассировка стека не загружается, пока вы не назовете .printStackTrace или .getStackTrace. Если вы положили точку останова в начале блока catch, вы заметите, что объект Exception имеет нулевую трассировку стека.

+0

AFAIK это неправда. Стек «записывается» при создании исключения, см. 'FillInStackTrace()' вызов в конструкторе Throwable. Но эти методы являются родными и, возможно, тяжелыми. 'stackTrace' имеет значение null, поскольку здесь используется ленивая инициализация. Далее вы видите, что трассировка стека создается с использованием других собственных методов как 'getStackTraceDepth()' и 'getStackTraceElement (int)' в 'getOurStackTrace()'. – Betlista

0

1.- Вызов глубины стека - ничто из того, о чем вам следует беспокоиться, Java Just In Time Compiler сделает оптимизацию для вашего кода, как метод inlining, чтобы обеспечить максимальную производительность вашего кода.

2.- Блокировки уловов влияют на производительность, потому что улавливание исключения может означать несколько различных операций внутри JVM, например, через таблицу исключений, разматывание стека и общие ловушки (деоптимизировать оптимизированный код JIT).

3.- Как совет, не беспокойтесь об инкапсулирующем коде и заканчивая несколькими вызовами методов, которые могут привести к огромной глубине стека, JVM сделает оптимизацию, чтобы получить наилучшую производительность из этого, когда ваш приложение скомпилировано и, когда оно работает, всегда нацелено на считывание кода и хорошие шаблоны проектирования, поскольку это поможет сделать ваш код более легким в обслуживании и упростить его изменение в случае, если потребуется какое-то исправление производительности. он должен иметь исключение, заброшенное или пойманное, и если вы ловите попытку поймать наиболее общее исключение, чтобы вы могли избежать огромных таблиц исключений.

2

Давайте измерять его, не так ли?

package tools.bench; 

import java.math.BigDecimal; 

public abstract class Benchmark { 

    final String name; 

    public Benchmark(String name) { 
     this.name = name; 
    } 

    abstract int run(int iterations) throws Throwable; 

    private BigDecimal time() { 
     try { 
      int nextI = 1; 
      int i; 
      long duration; 
      do { 
       i = nextI; 
       long start = System.nanoTime(); 
       run(i); 
       duration = System.nanoTime() - start; 
       nextI = (i << 1) | 1; 
      } while (duration < 1000000000 && nextI > 0); 
      return new BigDecimal((duration) * 1000/i).movePointLeft(3); 
     } catch (Throwable e) { 
      throw new RuntimeException(e); 
     } 
    } 

    @Override 
    public String toString() { 
     return name + "\t" + time() + " ns"; 
    } 

    enum ExceptionStrategy { 
     none { 
      @Override void run() { 
       // do nothing 
      } 
     }, 
     normal { 
      @Override void run() { 
       throw new RuntimeException(); 
      } 
     }, 
     withoutStackTrace { 
      @Override void run() { 
       throw new RuntimeException() { 
        public synchronized Throwable fillInStackTrace() { 
         return this; 
        }; 
       }; 
      } 
     }; 

     abstract void run(); 
    } 

    private static Benchmark tryBenchmark(final int depth, final ExceptionStrategy strat) { 
     return new Benchmark("try, depth = " + depth + ", " + strat) { 
      @Override int run(int iterations) { 
       int x = 0; 
       for (int i = 1; i < iterations; i++) { 
        try { 
         x += recurseAndThrow(depth); 
        } catch (Exception e) { 
         x++; 
        } 
       } 
       return x; 
      } 

      private int recurseAndThrow(int i) { 
       if (i > 0) { 
        return recurseAndThrow(i - 1) + 1; 
       } else { 
        strat.run(); 
        return 0; 
       } 
      } 
     }; 
    } 

    public static void main(String[] args) throws Exception { 
     int[] depths = {1, 10, 100, 1000, 10000}; 
     for (int depth : depths) { 
      for (ExceptionStrategy strat : ExceptionStrategy.values()) { 
       System.out.println(tryBenchmark(depth, strat)); 
      } 
     } 
    } 
} 

На моем (довольно датированного) ноутбук, это печатает:

try, depth = 1, none       5.153 ns 
try, depth = 1, normal      3374.113 ns 
try, depth = 1, withoutStackTrace   602.570 ns 
try, depth = 10, none       59.019 ns 
try, depth = 10, normal      9064.392 ns 
try, depth = 10, withoutStackTrace   3528.987 ns 
try, depth = 100, none      604.828 ns 
try, depth = 100, normal     49387.143 ns 
try, depth = 100, withoutStackTrace  27968.674 ns 
try, depth = 1000, none      5388.270 ns 
try, depth = 1000, normal     457158.668 ns 
try, depth = 1000, withoutStackTrace  271881.336 ns 
try, depth = 10000, none     69793.242 ns 
try, depth = 10000, normal    2895133.943 ns 
try, depth = 10000, withoutStackTrace 2728533.381 ns 

Очевидно, что конкретные результаты будут меняться в зависимости от вашего оборудования, а также внедрение и настройка виртуальной машины Java. Однако общая картина, вероятно, останется прежней.

Выводы:

  • Попробовать заявление само по себе несет ничтожное накладные расходы.
  • Выбрасывание исключения и разворачивание стоп-кадра несет служебные данные, линейные по размеру стека (или количество стека для размотки).
    • Для размеров стека приложений реального мира (предположим, что 100 кадров стека) это накладные расходы составляет около 50 микросекунд или 0,00005 секунд.
    • что накладные расходы может быть несколько уменьшен путем бросающего исключения без трассировки стека

Recommendatations:

  • Не беспокойтесь о производительности попробовать заявление.
  • Не используйте исключения для условий сигнала, которые происходят часто (скажем, более 1000 раз в секунду).
  • В противном случае не беспокойтесь о выполнении исключений для броска.
  • Кроме того, "premature optimization is the root of all evil" ;-)