簡體   English   中英

Java中的try-catch塊應該有多深?

[英]How deep should a try-catch block be in Java?

由於我的新工作,我在Java方面做了很多工作,現在我開始研究一些微小的細節。 顯然,Java代碼在某種程度上與異常有關。 我在想:

調用堆棧是否會對try-catch塊的性能產生很大影響? 即我應該避免嘗試調用一個函數的方法,而該函數又太深了?

我讀到try-catch塊只會影響異常情況下的性能。 但是,它們冒了多遠呢?

我們來衡量一下吧?

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

顯然,具體結果將隨您的硬件以及JVM的實現和配置而變化。 但是,一般模式可能保持不變。

結論:

  • try語句本身產生的開銷可忽略不計。
  • 引發異常並展開調用棧會導致開銷的增加與堆棧大小(或展開的堆棧量)成線性關系。
    • 對於實際應用程序的堆棧大小(假設100個堆棧幀),該開銷約為50微秒或0.00005秒。
    • 通過在沒有堆棧跟蹤的情況下拋出異常,可以稍微減少開銷

Recommendatations:

  • 不必擔心try語句的性能。
  • 不要使用異常來表示頻繁發生的情況(例如,每秒超過1000次)。
  • 否則,不必擔心引發異常的性能。
  • 另外, “過早的優化是萬惡之源” ;-)

例外很昂貴。 使用時,將創建堆棧跟蹤。 如果可以檢查異常,請執行此操作。 不要使用try..catch進行流量控制。 當您無法檢查/驗證時,請使用try..catch 一個示例是進行IO操作。

當我看到帶有大量try..catch塊的代碼時,我立即想到的是“這是一個糟糕的設計!”。

1.-調用堆棧深度無需擔心,Java即時編譯器將對代碼進行優化,例如方法內聯,以在代碼上提供最佳性能。

2.-捕獲塊確實會影響性能,這是因為捕獲異常可能意味着JVM內部有幾種不同的操作,例如遍歷異常表,堆棧展開和常見陷阱(對JIT的優化代碼進行優化)。

3.-作為一項建議,不要擔心封裝代碼並最終遇到可能導致巨大堆棧深度的多個方法調用,JVM會進行優化以在編譯應用程序並從中獲得最佳性能時進行優化當它運行時,始終要着眼於代碼的可讀性和良好的設計模式,因為這將有助於使您的代碼更易於維護和更容易修改,以防萬一必須進行性能修復。關於catch塊,請評估其必要性引發或捕獲的異常,如果要捕獲,請嘗試捕獲最通用的異常,這樣可以避免使用大量異常表。

在調用.printStackTrace或.getStackTrace之前,不會加載堆棧跟蹤。 如果在catch塊的開頭放置一個斷點,您會注意到Exception對象的堆棧跟蹤為空。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM