Здравствуйте, дорогие друзья,Брат Лэй технологий, о прогрессе и говорить нечего! Добро пожаловать в новую серию интерпретаций перформанса, меня зовут Лэй Гэ.
То, что я приношу вам сегодня, касаетсяСтатьи о том, следует ли помещать try-catch вне или внутри цикла, мы начнемпредставлениеиАнализ бизнес-сценариевответить на этот вопрос двумя способами.
Многие люди имеют определенное непонимание try-catch, например, мы часто отождествляем его (try-catch) с «низкой производительностью», но суть try-catch (что это такое) не имеет самого элементарного понимания, поэтому мы такжеВ этой статье мы рассмотрим природу try-catch..
Советы: я сделаю все возможное, чтобы использовать код и результаты оценки, чтобы доказать проблему, но из-за ограничений моего собственного познания, если есть какие-либо несоответствия, пожалуйста, читатели и друзья укажите в области комментариев.
Оценка эффективности
Без лишних слов приступим к сегодняшнему тесту.В этой статье мы по-прежнему используем JMH (Java Microbenchmark Harness, JAVA Microbenchmark Test Suite), официально предоставленный Oracle для тестирования.
Сначала добавьте фреймворк JMH в файл pom.xml, конфигурация выглядит следующим образом:
<!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-core -->
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>{version}</version>
</dependency>
Полный тестовый код выглядит следующим образом:
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
* try - catch 性能测试
*/
@BenchmarkMode(Mode.AverageTime) // 测试完成时间
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS) // 预热 1 轮,每次 1s
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) // 测试 5 轮,每次 3s
@Fork(1) // fork 1 个线程
@State(Scope.Benchmark)
@Threads(100)
public class TryCatchPerformanceTest {
private static final int forSize = 1000; // 循环次数
public static void main(String[] args) throws RunnerException {
// 启动基准测试
Options opt = new OptionsBuilder()
.include(TryCatchPerformanceTest.class.getSimpleName()) // 要导入的测试类
.build();
new Runner(opt).run(); // 执行测试
}
@Benchmark
public int innerForeach() {
int count = 0;
for (int i = 0; i < forSize; i++) {
try {
if (i == forSize) {
throw new Exception("new Exception");
}
count++;
} catch (Exception e) {
e.printStackTrace();
}
}
return count;
}
@Benchmark
public int outerForeach() {
int count = 0;
try {
for (int i = 0; i < forSize; i++) {
if (i == forSize) {
throw new Exception("new Exception");
}
count++;
}
} catch (Exception e) {
e.printStackTrace();
}
return count;
}
}
Результат теста приведенного выше кода:
Из приведенных выше результатов видно, что когда программа зацикливается 1000 раз, среднее время выполнения одного выполнения составляет:
- Среднее время выполнения try-catch в цикле составляет 635 нс ±75 нс, то есть ошибка 635 нс составляет 75 нс;
- Среднее время выполнения с try-catch вне цикла составляет 630 наносекунд с погрешностью 38 наносекунд.
То есть при отсутствии аномалий, сняв значение ошибки, получаем вывод:попробуй-поймай ли вfor
внутри цикла илиfor
Вне цикла они работают одинаково почти без разницы.
Суть try-catch
Чтобы разобраться в вопросах производительности try-catch, необходимо начать с анализа его байт-кода, только тогда я смогу узнать, в чем суть try-catch и как он выполняется.
На этом этапе мы пишем простейший код try-catch:
public class AppTest {
public static void main(String[] args) {
try {
int count = 0;
throw new Exception("new Exception");
} catch (Exception e) {
e.printStackTrace();
}
}
}
затем используйтеjavac
После генерации байт-кода используйтеjavap -c AppTest
Команда для просмотра файла байт-кода:
➜ javap -c AppTest
警告: 二进制文件AppTest包含com.example.AppTest
Compiled from "AppTest.java"
public class com.example.AppTest {
public com.example.AppTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: new #2 // class java/lang/Exception
5: dup
6: ldc #3 // String new Exception
8: invokespecial #4 // Method java/lang/Exception."<init>":(Ljava/lang/String;)V
11: athrow
12: astore_1
13: aload_1
14: invokevirtual #5 // Method java/lang/Exception.printStackTrace:()V
17: return
Exception table:
from to target type
0 12 12 Class java/lang/Exception
}
Как видно из приведенного выше байт-кода, есть таблица исключений:
Exception table:
from to target type
0 12 12 Class java/lang/Exception
Описание параметра:
- from: указывает начальный адрес try-catch;
- to: указывает конечный адрес try-catch;
- цель: указывает бит начала обработки исключения;
- тип: указывает имя класса исключений.
Из инструкций байт-кода видно, что при возникновении ошибки при выполнении кода он сначала определяет, находятся ли данные об ошибке вfrom
прибытьto
диапазон, если да, то отtarget
Бит флага выполняется вниз, если нет ошибки, напрямуюgoto
прибытьreturn
. То есть, если код работает правильно, производительность почти не меняется, а логика выполнения обычного кода такая же.
Анализ деловой ситуации
Хотя производительность try-catch внутри и вне цикла одинакова, бизнес-значение кода, который они кодируют, совершенно различно, например, следующий код:
public class AppTest {
public static void main(String[] args) {
System.out.println("循环内的执行结果:" + innerForeach());
System.out.println("循环外的执行结果:" + outerForeach());
}
// 方法一
public static int innerForeach() {
int count = 0;
for (int i = 0; i < 6; i++) {
try {
if (i == 3) {
throw new Exception("new Exception");
}
count++;
} catch (Exception e) {
e.printStackTrace();
}
}
return count;
}
// 方法二
public static int outerForeach() {
int count = 0;
try {
for (int i = 0; i < 6; i++) {
if (i == 3) {
throw new Exception("new Exception");
}
count++;
}
} catch (Exception e) {
e.printStackTrace();
}
return count;
}
}
Результат выполнения вышеуказанной программы:
java.lang.Exception: new Exception
at com.example.AppTest.innerForeach(AppTest.java:15)
at com.example.AppTest.main(AppTest.java:5)
java.lang.Exception: new Exception
at com.example.AppTest.outerForeach(AppTest.java:31)
at com.example.AppTest.main(AppTest.java:6)
Результат выполнения внутри цикла: 5
Результаты выполнения вне цикла: 3
Можно видеть, что try-catch в теле цикла может продолжать выполнение цикла после возникновения исключения, в то время как try-catch вне цикла завершит цикл после возникновения исключения.
поэтому мыРешение о том, следует ли помещать try-catch внутри цикла или вне цикла, не зависит от производительности (поскольку производительность почти одинакова), но должно зависеть от конкретного бизнес-сценария..
Например, нам нужно обработать пакет данных, и какие бы данные в этом наборе данных не были проблемными, это не может повлиять на нормальное выполнение других групп, в это время мы можем поместить try-catch в тело цикла ; и когда нам нужно вычислить набор данных Когда общее значение данных, пока есть ошибка в наборе данных, нам нужно прекратить выполнение и выдать исключение. В это время нам нужно поместите try-catch вне цикла для выполнения.
Суммировать
В этой статье мы проверили производительность try-catch, помещенного внутри цикла и вне цикла, и обнаружили, чтоПроизводительность обоих практически одинакова в случае многих циклов.. Затем с помощью анализа байт-кода мы обнаружили, что только при возникновении исключения таблица исключений будет сравниваться для обработки исключений, а выполнение try-catch при нормальных обстоятельствах может быть проигнорировано. Но использование try-catch внутри тела цикла или вне цикла совершенно различно для результата выполнения программы, поэтомуМы должны начать с фактического бизнеса, чтобы решить, где следует хранить try-catch, а не с соображений производительности..
Подпишитесь на официальный аккаунт «Java Chinese Community» и ответьте на «Галантные товары», чтобы получить 50 оригинальных галантерейных товаров.Топ-лист.