Подтвердить что ты не робот

Является ли Try-Catch более или менее дорогостоящим, чем Try-With-Resources

Вопрос

Я недавно начал возвращаться в Java и никогда не имел возможности использовать try-with-resources. На первый взгляд это выглядит великолепно, поскольку он может сократить код, но под капотом это более или менее дорогостоящая операция, чем традиционная try-catch? Я знаю, что try-catch уже является дорогостоящей операцией, поэтому мое любопытство.

Я дал оба типа простой тест и не заметил большой разницы:

Примеры тестов

Тест с ресурсами

long startTime = System.currentTimeMillis();
ArrayList<String> list = null;

try (Scanner sc = new Scanner(new File("file.txt"))) {
    list = new ArrayList();
    while (sc.hasNext()) {
        list.add(sc.next());
    }
} catch (Exception ex) {
    System.err.println("Error: " + ex.getMessage());
} finally {
    long endTime = System.currentTimeMillis();
    System.out.println("The program completed in " + (endTime - startTime) + " ms");
}

Традиционный пробный тест

long startTime = System.currentTimeMillis();
ArrayList<String> list = null;
Scanner sc = null;

try {
    sc = new Scanner(new File("file.txt"));
    list = new ArrayList();
    while (sc.hasNext()) {
        list.add(sc.next());
    }
} catch (Exception ex) {
    System.err.println("Error: " + ex.getMessage());
} finally {
    sc.close();
    long endTime = System.currentTimeMillis();
    System.out.println("The program completed in " + (endTime - startTime) + " ms");
}

Результаты

Оба привели к времени 15-16 мс - никакой реальной заметной разницы вообще. Но, по общему признанию, это очень маленький тестовый пример.

Мой вопрос снова: под капотом try-with-resources более или менее дорогим, чем традиционный try-catch?

4b9b3361

Ответ 1

  • try-catch - не дорогая часть. Выбрасывание исключения (создание stacktrace).
  • "Дорого" выше означает "стоит несколько микросекунд".
  • try-with-resources - это просто try-catch с правильным кодом, необходимым для надежного закрытия ресурса.
  • Ваш код измерения не может ничего доказать из-за всех известных ошибок, связанных с попыткой измерить производительность в рамках оптимизации времени выполнения, такого как HotSpot. Вам нужно разогреться, повторить то же самое много раз и многое другое.
  • Если ваш результат превышает 10 мс, то, очевидно, у вас не может быть проблемы с try-catch, которая может вообще наложить накладные расходы в течение нескольких микросекунд.

Ответ 2

Это яблоки и апельсины. Блок ARM (автоматическое управление ресурсами или try-with-resources) делает больше, чем старомодный блок try-catch-finally, который вы показываете. Это потому, что он генерирует код для обработки исключений, которые возникают при закрытии ресурсов с помощью механизма подавления (A связанный ответ обсуждает это в деталях.)

Если вы пишете новый код, используйте блок ARM. Его легче читать, поддерживать, и он делает больше. Если вы не работаете в тесно связанной среде (например, смарт-карте), эти преимущества, скорее всего, перевесят стоимость нескольких дополнительных байтовых кодов.

Ответ 3

Try-catch-finally и try-with-resources имеют по существу одну и ту же производительность, потому что под обложками они генерируют по существу один и тот же байт-код.

Однако ваша вторая версия (try..catch..finally) не совсем сформулирована правильно, поскольку она (теоретически) может привести к нежелательному NullPointerException, когда вызывается sc.close(). Если акт построения Scanner вызывает исключение, то sc не будет назначаться и будет null.

Вы должны построить сканер вне try..finally и изменить это:

Scanner sc = null;
try {
    sc = new Scanner(new File("file.txt"));
    ...

в

Scanner sc = new Scanner(new File("file.txt"));
try {
    ...

В качестве альтернативы вы должны проверить, что sc != null в предложении finally перед вызовом sc.close(). Это не будет необходимо, если вы создадите сканер вне try..finally, поэтому я бы порекомендовал вам сделать это.

Чтобы выполнить ту же работу, что и try-with-resources, вам также нужно поместить второй try..catch вокруг sc.close() с пустым блоком catch, чтобы игнорировать все исключения, которые были выбраны во время закрытия. Если вы это сделаете, я полагаю, вам не нужно так беспокоиться о нулевой проверке.