Bagian mana dari membuang Eksepsi yang mahal?


256

Di Jawa, menggunakan lemparan / tangkap sebagai bagian dari logika ketika sebenarnya tidak ada kesalahan pada umumnya adalah ide yang buruk (sebagian) karena melempar dan menangkap pengecualian itu mahal, dan melakukannya berkali-kali dalam satu lingkaran biasanya jauh lebih lambat daripada yang lain mengontrol struktur yang tidak melibatkan pengecualian melempar.

Pertanyaan saya adalah, apakah biaya yang dikeluarkan dalam lemparan / tangkapan itu sendiri, atau ketika membuat objek Pengecualian (karena ia mendapatkan banyak informasi runtime termasuk tumpukan eksekusi)?

Dengan kata lain, jika saya melakukannya

Exception e = new Exception();

tetapi jangan membuangnya, apakah itu sebagian besar biaya melempar, atau apakah lemparan + tangkapan menangani apa yang mahal?

Saya tidak bertanya apakah memasukkan kode ke blok coba / tangkap menambah biaya mengeksekusi kode itu, saya bertanya apakah menangkap Exception adalah bagian yang mahal, atau membuat (memanggil konstruktor untuk) Exception adalah bagian yang mahal .

Cara lain untuk menanyakan hal ini adalah, jika saya membuat satu contoh Pengecualian dan melemparkan serta menangkapnya berulang kali, akankah itu jauh lebih cepat daripada membuat Pengecualian baru setiap kali saya melempar?


20
Saya percaya ini mengisi dan mengisi jejak stack.
Elliott Frisch


"Jika saya membuat satu contoh Exception dan melemparkan dan menangkapnya berulang-ulang," ketika pengecualian dibuat stacktrace-nya diisi yang berarti itu akan selalu menjadi stactrace yang sama terlepas dari tempat dari mana itu dilemparkan. Jika stacktrace tidak penting bagi Anda daripada Anda bisa mencoba ide Anda, tetapi ini bisa membuat debugging sangat sulit jika bukan tidak mungkin dalam beberapa kasus.
Pshemo

2
@Pshemo Saya tidak berencana untuk benar - benar melakukan ini dalam kode, saya bertanya tentang kinerja, dan menggunakan absurditas ini sebagai contoh di mana itu bisa membuat perbedaan.
Martin Carney

@ MartinCarney Saya telah menambahkan jawaban untuk paragraf terakhir Anda yaitu caching Pengecualian akan mendapatkan kinerja. Jika bermanfaat saya bisa menambahkan kode, jika tidak saya bisa menghapus jawabannya.
Harry

Jawaban:


267

Membuat objek pengecualian tidak lebih mahal daripada membuat objek biasa lainnya. Biaya utama disembunyikan dalam fillInStackTracemetode asli yang berjalan melalui tumpukan panggilan dan mengumpulkan semua informasi yang diperlukan untuk membangun jejak tumpukan: kelas, nama metode, nomor baris dll.

Mitos tentang biaya pengecualian yang tinggi berasal dari kenyataan bahwa sebagian besar Throwablekonstruktor secara implisit memanggil fillInStackTrace. Namun, ada satu konstruktor untuk membuat Throwablejejak tanpa tumpukan. Hal ini memungkinkan Anda untuk membuat throwables yang sangat cepat untuk instantiate. Cara lain untuk membuat pengecualian ringan adalah menimpanya fillInStackTrace.


Sekarang bagaimana dengan melempar pengecualian?
Bahkan, itu tergantung di mana pengecualian yang dilemparkan ditangkap .

Jika tertangkap dalam metode yang sama (atau, lebih tepatnya, dalam konteks yang sama, karena konteksnya dapat mencakup beberapa metode karena inlining), maka throwitu secepat dan sesederhana goto(tentu saja, setelah kompilasi JIT).

Namun jika sebuah catchblok berada di suatu tempat lebih dalam di stack, maka JVM perlu melepaskan frame stack, dan ini bisa memakan waktu lebih lama secara signifikan. Dibutuhkan lebih lama lagi, jika ada synchronizedblok atau metode yang terlibat, karena melepas gulungan menyiratkan pelepasan monitor yang dimiliki oleh frame tumpukan yang dihapus.


Saya dapat mengkonfirmasi pernyataan di atas dengan tolok ukur yang tepat, tetapi untungnya saya tidak perlu melakukan ini, karena semua aspek sudah tercakup dengan sempurna dalam jabatan insinyur kinerja HotSpot, Alexey Shipilev: Kinerja Luar Biasa Pengecualian Lil .


8
Seperti disebutkan dalam artikel dan disinggung di sini, hasilnya adalah bahwa biaya pengecualian melempar / menangkap sangat tergantung pada kedalaman panggilan. Intinya di sini adalah bahwa pernyataan "pengecualian itu mahal" tidak sepenuhnya benar. Pernyataan yang lebih benar adalah bahwa pengecualian 'bisa' mahal. Sejujurnya, saya pikir mengatakan hanya menggunakan pengecualian untuk "kasus yang benar-benar luar biasa" (seperti dalam artikel) terlalu kuat. Mereka sempurna untuk hampir semua hal di luar aliran balik normal dan sulit untuk mendeteksi dampak kinerja menggunakan cara ini dalam aplikasi nyata.
JimmyJames

14
Mungkin layak untuk menghitung overhead pengecualian. Bahkan dalam kasus terburuk yang dilaporkan dalam artikel yang agak lengkap ini (melempar dan menangkap pengecualian dinamis dengan stacktrace yang sebenarnya dipertanyakan, kedalaman 1000 stack frame), membutuhkan waktu 80 mikro detik. Itu bisa menjadi signifikan jika sistem Anda perlu memproses ribuan pengecualian per detik, tetapi sebaliknya tidak perlu dikhawatirkan. Dan itu adalah kasus terburuk; jika stacktrace Anda sedikit lebih waras, atau Anda tidak menanyai stacktrace, kami dapat memproses hampir satu juta pengecualian per detik.
meriton

13
Saya menekankan ini karena banyak orang, setelah membaca bahwa pengecualian itu "mahal", tidak pernah berhenti untuk bertanya "mahal dibandingkan dengan apa", tetapi menganggap bahwa mereka adalah "bagian mahal dari program mereka", yang sangat jarang mereka miliki.
meriton

2
Ada satu bagian yang tidak disebutkan di sini: potensi biaya dalam mencegah penerapan optimasi. Contoh ekstrem adalah JVM tidak inlining untuk menghindari "tumpukan kekacauan" jejak, tapi saya telah melihat tolok ukur (mikro) di mana ada atau tidak adanya pengecualian akan membuat atau merusak optimasi di C ++ sebelumnya.
Matthieu M.

3
@ MatthieuM. Pengecualian dan coba / tangkap blok tidak mencegah JVM dari inlining. Untuk metode yang dikompilasi, jejak tumpukan nyata direkonstruksi dari tabel bingkai tumpukan virtual yang disimpan sebagai metadata. Saya tidak dapat mengingat optimasi JIT yang tidak kompatibel dengan coba / tangkap. Coba / tangkap struktur itu sendiri tidak menambahkan apa pun ke kode metode, itu hanya ada sebagai tabel pengecualian selain dari kode.
apangin

72

Operasi pertama di sebagian besar Throwablekonstruktor adalah mengisi jejak tumpukan, yang merupakan tempat sebagian besar pengeluaran.

Namun, ada konstruktor yang dilindungi dengan bendera untuk menonaktifkan jejak stack. Konstruktor ini dapat diakses saat memanjang Exceptionjuga. Jika Anda membuat jenis pengecualian khusus, Anda dapat menghindari pembuatan jejak tumpukan dan mendapatkan kinerja yang lebih baik dengan mengorbankan lebih sedikit informasi.

Jika Anda membuat pengecualian tunggal dari jenis apa pun dengan cara biasa, Anda dapat melemparkannya berkali-kali tanpa perlu mengisi jejak tumpukan. Namun, jejak tumpukannya akan mencerminkan di mana ia dibangun, bukan di mana itu dilemparkan dalam contoh tertentu.

Versi Java saat ini membuat beberapa upaya untuk mengoptimalkan pembuatan jejak stack. Kode asli dipanggil untuk mengisi jejak tumpukan, yang mencatat jejak dalam struktur asli yang lebih ringan. Sesuai Java StackTraceElementobjek yang malas diciptakan dari catatan ini hanya ketika getStackTrace(), printStackTrace()atau metode lain yang memerlukan jejak disebut.

Jika Anda menghilangkan timbunan jejak, biaya utama lainnya adalah melepaskan tumpukan di antara lemparan dan tangkapan. Semakin sedikit bingkai intervensi yang ditemukan sebelum pengecualian ditangkap, semakin cepat ini terjadi.

Rancang program Anda sehingga pengecualian hanya dibuang dalam kasus yang benar-benar luar biasa, dan optimasi seperti ini sulit untuk dibenarkan.



25

Ada tulisan yang bagus tentang Pengecualian di sini.

http://shipilev.net/blog/2014/exceptional-performance/

Kesimpulannya adalah bahwa konstruksi tumpukan jejak dan tumpukan unwinding adalah bagian yang mahal. Kode di bawah ini memanfaatkan fitur di 1.7mana kita dapat menghidupkan dan mematikan tumpukan jejak. Kita kemudian dapat menggunakan ini untuk melihat biaya seperti apa yang dimiliki berbagai skenario

Berikut ini adalah pengaturan waktu untuk pembuatan Obyek saja. Saya telah menambahkan di Stringsini sehingga Anda dapat melihat bahwa tanpa tumpukan yang ditulis hampir tidak ada perbedaan dalam membuat JavaExceptionObyek dan a String. Dengan penulisan stack dihidupkan perbedaannya dramatis yaitu setidaknya satu urutan besarnya lebih lambat.

Time to create million String objects: 41.41 (ms)
Time to create million JavaException objects with    stack: 608.89 (ms)
Time to create million JavaException objects without stack: 43.50 (ms)

Berikut ini menunjukkan berapa lama untuk kembali dari lemparan pada kedalaman tertentu jutaan kali.

|Depth| WriteStack(ms)| !WriteStack(ms)| Diff(%)|
|   16|           1428|             243| 588 (%)|
|   15|           1763|             393| 449 (%)|
|   14|           1746|             390| 448 (%)|
|   13|           1703|             384| 443 (%)|
|   12|           1697|             391| 434 (%)|
|   11|           1707|             410| 416 (%)|
|   10|           1226|             197| 622 (%)|
|    9|           1242|             206| 603 (%)|
|    8|           1251|             207| 604 (%)|
|    7|           1213|             208| 583 (%)|
|    6|           1164|             206| 565 (%)|
|    5|           1134|             205| 553 (%)|
|    4|           1106|             203| 545 (%)|
|    3|           1043|             192| 543 (%)| 

Berikut ini hampir pasti lebih dari penyederhanaan ...

Jika kita mengambil kedalaman 16 dengan penulisan tumpukan maka penciptaan objek memakan waktu ~ 40% dari waktu, jejak jejak sebenarnya menyumbang sebagian besar dari ini. ~ 93% dari instantiating objek JavaException disebabkan oleh jejak stack yang diambil. Ini berarti bahwa membuka tumpukan tumpukan dalam kasus ini menghabiskan 50% waktu lainnya.

Ketika kita mematikan stack trace, penciptaan objek menyumbang fraksi yang jauh lebih kecil, yaitu 20% dan stack unwinding kini merupakan 80% dari waktu.

Dalam kedua kasus, susunan pemunduran membutuhkan sebagian besar waktu keseluruhan.

public class JavaException extends Exception {
  JavaException(String reason, int mode) {
    super(reason, null, false, false);
  }
  JavaException(String reason) {
    super(reason);
  }

  public static void main(String[] args) {
    int iterations = 1000000;
    long create_time_with    = 0;
    long create_time_without = 0;
    long create_string = 0;
    for (int i = 0; i < iterations; i++) {
      long start = System.nanoTime();
      JavaException jex = new JavaException("testing");
      long stop  =  System.nanoTime();
      create_time_with += stop - start;

      start = System.nanoTime();
      JavaException jex2 = new JavaException("testing", 1);
      stop = System.nanoTime();
      create_time_without += stop - start;

      start = System.nanoTime();
      String str = new String("testing");
      stop = System.nanoTime();
      create_string += stop - start;

    }
    double interval_with    = ((double)create_time_with)/1000000;
    double interval_without = ((double)create_time_without)/1000000;
    double interval_string  = ((double)create_string)/1000000;

    System.out.printf("Time to create %d String objects: %.2f (ms)\n", iterations, interval_string);
    System.out.printf("Time to create %d JavaException objects with    stack: %.2f (ms)\n", iterations, interval_with);
    System.out.printf("Time to create %d JavaException objects without stack: %.2f (ms)\n", iterations, interval_without);

    JavaException jex = new JavaException("testing");
    int depth = 14;
    int i = depth;
    double[] with_stack    = new double[20];
    double[] without_stack = new double[20];

    for(; i > 0 ; --i) {
      without_stack[i] = jex.timerLoop(i, iterations, 0)/1000000;
      with_stack[i]    = jex.timerLoop(i, iterations, 1)/1000000;
    }
    i = depth;
    System.out.printf("|Depth| WriteStack(ms)| !WriteStack(ms)| Diff(%%)|\n");
    for(; i > 0 ; --i) {
      double ratio = (with_stack[i] / (double) without_stack[i]) * 100;
      System.out.printf("|%5d| %14.0f| %15.0f| %2.0f (%%)| \n", i + 2, with_stack[i] , without_stack[i], ratio);
      //System.out.printf("%d\t%.2f (ms)\n", i, ratio);
    }
  }
 private int thrower(int i, int mode) throws JavaException {
    ExArg.time_start[i] = System.nanoTime();
    if(mode == 0) { throw new JavaException("without stack", 1); }
    throw new JavaException("with stack");
  }
  private int catcher1(int i, int mode) throws JavaException{
    return this.stack_of_calls(i, mode);
  }
  private long timerLoop(int depth, int iterations, int mode) {
    for (int i = 0; i < iterations; i++) {
      try {
        this.catcher1(depth, mode);
      } catch (JavaException e) {
        ExArg.time_accum[depth] += (System.nanoTime() - ExArg.time_start[depth]);
      }
    }
    //long stop = System.nanoTime();
    return ExArg.time_accum[depth];
  }

  private int bad_method14(int i, int mode) throws JavaException  {
    if(i > 0) { this.thrower(i, mode); }
    return i;
  }
  private int bad_method13(int i, int mode) throws JavaException  {
    if(i == 13) { this.thrower(i, mode); }
    return bad_method14(i,mode);
  }
  private int bad_method12(int i, int mode) throws JavaException{
    if(i == 12) { this.thrower(i, mode); }
    return bad_method13(i,mode);
  }
  private int bad_method11(int i, int mode) throws JavaException{
    if(i == 11) { this.thrower(i, mode); }
    return bad_method12(i,mode);
  }
  private int bad_method10(int i, int mode) throws JavaException{
    if(i == 10) { this.thrower(i, mode); }
    return bad_method11(i,mode);
  }
  private int bad_method9(int i, int mode) throws JavaException{
    if(i == 9) { this.thrower(i, mode); }
    return bad_method10(i,mode);
  }
  private int bad_method8(int i, int mode) throws JavaException{
    if(i == 8) { this.thrower(i, mode); }
    return bad_method9(i,mode);
  }
  private int bad_method7(int i, int mode) throws JavaException{
    if(i == 7) { this.thrower(i, mode); }
    return bad_method8(i,mode);
  }
  private int bad_method6(int i, int mode) throws JavaException{
    if(i == 6) { this.thrower(i, mode); }
    return bad_method7(i,mode);
  }
  private int bad_method5(int i, int mode) throws JavaException{
    if(i == 5) { this.thrower(i, mode); }
    return bad_method6(i,mode);
  }
  private int bad_method4(int i, int mode) throws JavaException{
    if(i == 4) { this.thrower(i, mode); }
    return bad_method5(i,mode);
  }
  protected int bad_method3(int i, int mode) throws JavaException{
    if(i == 3) { this.thrower(i, mode); }
    return bad_method4(i,mode);
  }
  private int bad_method2(int i, int mode) throws JavaException{
    if(i == 2) { this.thrower(i, mode); }
    return bad_method3(i,mode);
  }
  private int bad_method1(int i, int mode) throws JavaException{
    if(i == 1) { this.thrower(i, mode); }
    return bad_method2(i,mode);
  }
  private int stack_of_calls(int i, int mode) throws JavaException{
    if(i == 0) { this.thrower(i, mode); }
    return bad_method1(i,mode);
  }
}

class ExArg {
  public static long[] time_start;
  public static long[] time_accum;
  static {
     time_start = new long[20];
     time_accum = new long[20];
  };
}

Frame tumpukan dalam contoh ini kecil dibandingkan dengan apa yang biasanya Anda temukan.

Anda dapat mengintip bytecode menggunakan javap

javap -c -v -constants JavaException.class

yaitu ini untuk metode 4 ...

   protected int bad_method3(int, int) throws JavaException;
flags: ACC_PROTECTED
Code:
  stack=3, locals=3, args_size=3
     0: iload_1       
     1: iconst_3      
     2: if_icmpne     12
     5: aload_0       
     6: iload_1       
     7: iload_2       
     8: invokespecial #6                  // Method thrower:(II)I
    11: pop           
    12: aload_0       
    13: iload_1       
    14: iload_2       
    15: invokespecial #17                 // Method bad_method4:(II)I
    18: ireturn       
  LineNumberTable:
    line 63: 0
    line 64: 12
  StackMapTable: number_of_entries = 1
       frame_type = 12 /* same */

Exceptions:
  throws JavaException

13

Penciptaan Exceptiondengan nulljejak stack memakan waktu sebanyak throwdan try-catchblok bersama. Namun, mengisi jejak tumpukan memakan waktu rata-rata 5x lebih lama .

Saya membuat tolok ukur berikut untuk menunjukkan dampak pada kinerja. Saya menambahkannya -Djava.compiler=NONEke Run Configuration untuk menonaktifkan optimisasi kompiler. Untuk mengukur dampak dari membangun tumpukan jejak, saya memperluas Exceptionkelas untuk mengambil keuntungan dari konstruktor bebas tumpukan:

class NoStackException extends Exception{
    public NoStackException() {
        super("",null,false,false);
    }
}

Kode benchmark adalah sebagai berikut:

public class ExceptionBenchmark {

    private static final int NUM_TRIES = 100000;

    public static void main(String[] args) {

        long throwCatchTime = 0, newExceptionTime = 0, newObjectTime = 0, noStackExceptionTime = 0;

        for (int i = 0; i < 30; i++) {
            throwCatchTime += throwCatchLoop();
            newExceptionTime += newExceptionLoop();
            newObjectTime += newObjectLoop();
            noStackExceptionTime += newNoStackExceptionLoop();
        }

        System.out.println("throwCatchTime = " + throwCatchTime / 30);
        System.out.println("newExceptionTime = " + newExceptionTime / 30);
        System.out.println("newStringTime = " + newObjectTime / 30);
        System.out.println("noStackExceptionTime = " + noStackExceptionTime / 30);

    }

    private static long throwCatchLoop() {
        Exception ex = new Exception(); //Instantiated here
        long start = System.currentTimeMillis();
        for (int i = 0; i < NUM_TRIES; i++) {
            try {
                throw ex; //repeatedly thrown
            } catch (Exception e) {

                // do nothing
            }
        }
        long stop = System.currentTimeMillis();
        return stop - start;
    }

    private static long newExceptionLoop() {
        long start = System.currentTimeMillis();
        for (int i = 0; i < NUM_TRIES; i++) {
            Exception e = new Exception();
        }
        long stop = System.currentTimeMillis();
        return stop - start;
    }

    private static long newObjectLoop() {
        long start = System.currentTimeMillis();
        for (int i = 0; i < NUM_TRIES; i++) {
            Object o = new Object();
        }
        long stop = System.currentTimeMillis();
        return stop - start;
    }

    private static long newNoStackExceptionLoop() {
        long start = System.currentTimeMillis();
        for (int i = 0; i < NUM_TRIES; i++) {
            NoStackException e = new NoStackException();
        }
        long stop = System.currentTimeMillis();
        return stop - start;
    }

}

Keluaran:

throwCatchTime = 19
newExceptionTime = 77
newObjectTime = 3
noStackExceptionTime = 15

Ini menyiratkan bahwa membuat a NoStackExceptionkira-kira sama mahal dengan berulang kali melemparkan yang sama Exception. Ini juga menunjukkan bahwa membuat Exceptiondan mengisi jejak stacknya memakan waktu sekitar 4x lebih lama.


1
Bisakah Anda menambahkan satu case lagi di mana Anda membuat satu instance Exception sebelum waktu mulai, lalu melempar + menangkapnya berulang kali dalam satu lingkaran? Itu akan menunjukkan biaya hanya melempar + menangkap.
Martin Carney

@MartinCarney Saran yang bagus! Saya memperbarui jawaban saya untuk melakukan hal itu.
Austin D

Saya melakukan beberapa penyesuaian kode pengujian Anda, dan sepertinya kompiler sedang melakukan beberapa optimasi yang mencegah kami mendapatkan angka yang akurat.
Martin Carney

@ MartinCarney Saya memperbarui jawaban untuk pengoptimalan kompilator diskon
Austin D

FYI, Anda mungkin harus membaca jawaban untuk Bagaimana cara saya menulis benchmark mikro di Jawa? Petunjuk: ini bukan.
Daniel Pryden

4

Ini bagian dari pertanyaan ...

Cara lain untuk menanyakan hal ini adalah, jika saya membuat satu contoh Pengecualian dan melemparkan serta menangkapnya berulang kali, akankah itu jauh lebih cepat daripada membuat Pengecualian baru setiap kali saya melempar?

Tampaknya akan bertanya apakah membuat pengecualian dan menyimpannya di suatu tempat meningkatkan kinerja. Ya itu. Ini sama dengan mematikan tumpukan yang sedang ditulis pada pembuatan objek karena sudah dilakukan.

Ini adalah timing yang saya dapatkan, silakan baca peringatan setelah ini ...

|Depth| WriteStack(ms)| !WriteStack(ms)| Diff(%)|
|   16|            193|             251| 77 (%)| 
|   15|            390|             406| 96 (%)| 
|   14|            394|             401| 98 (%)| 
|   13|            381|             385| 99 (%)| 
|   12|            387|             370| 105 (%)| 
|   11|            368|             376| 98 (%)| 
|   10|            188|             192| 98 (%)| 
|    9|            193|             195| 99 (%)| 
|    8|            200|             188| 106 (%)| 
|    7|            187|             184| 102 (%)| 
|    6|            196|             200| 98 (%)| 
|    5|            197|             193| 102 (%)| 
|    4|            198|             190| 104 (%)| 
|    3|            193|             183| 105 (%)| 

Tentu saja masalah dengan ini adalah jejak tumpukan Anda sekarang menunjuk ke tempat Anda membuat objek bukan tempat objek itu dilempar.


3

Menggunakan jawaban @ AustinD sebagai titik awal, saya melakukan beberapa penyesuaian. Kode di bawah.

Selain menambahkan kasus di mana satu instance Exception dilemparkan berulang kali, saya juga mematikan optimisasi kompiler sehingga kami bisa mendapatkan hasil kinerja yang akurat. Saya menambahkan -Djava.compiler=NONEargumen VM, sesuai jawaban ini . (Dalam gerhana, edit Run Configuration → Arguments untuk mengatur argumen VM ini)

Hasil:

new Exception + throw/catch = 643.5
new Exception only          = 510.7
throw/catch only            = 115.2
new String (benchmark)      = 669.8

Jadi membuat biaya pengecualian sekitar 5x sebanyak membuang + menangkapnya. Dengan asumsi kompiler tidak mengoptimalkan banyak biaya.

Sebagai perbandingan, inilah uji coba yang sama tanpa menonaktifkan pengoptimalan:

new Exception + throw/catch = 382.6
new Exception only          = 379.5
throw/catch only            = 0.3
new String (benchmark)      = 15.6

Kode:

public class ExceptionPerformanceTest {

    private static final int NUM_TRIES = 1000000;

    public static void main(String[] args) {

        double numIterations = 10;

        long exceptionPlusCatchTime = 0, excepTime = 0, strTime = 0, throwTime = 0;

        for (int i = 0; i < numIterations; i++) {
            exceptionPlusCatchTime += exceptionPlusCatchBlock();
            excepTime += createException();
            throwTime += catchBlock();
            strTime += createString();
        }

        System.out.println("new Exception + throw/catch = " + exceptionPlusCatchTime / numIterations);
        System.out.println("new Exception only          = " + excepTime / numIterations);
        System.out.println("throw/catch only            = " + throwTime / numIterations);
        System.out.println("new String (benchmark)      = " + strTime / numIterations);

    }

    private static long exceptionPlusCatchBlock() {
        long start = System.currentTimeMillis();
        for (int i = 0; i < NUM_TRIES; i++) {
            try {
                throw new Exception();
            } catch (Exception e) {
                // do nothing
            }
        }
        long stop = System.currentTimeMillis();
        return stop - start;
    }

    private static long createException() {
        long start = System.currentTimeMillis();
        for (int i = 0; i < NUM_TRIES; i++) {
            Exception e = new Exception();
        }
        long stop = System.currentTimeMillis();
        return stop - start;
    }

    private static long createString() {
        long start = System.currentTimeMillis();
        for (int i = 0; i < NUM_TRIES; i++) {
            Object o = new String("" + i);
        }
        long stop = System.currentTimeMillis();
        return stop - start;
    }

    private static long catchBlock() {
        Exception ex = new Exception(); //Instantiated here
        long start = System.currentTimeMillis();
        for (int i = 0; i < NUM_TRIES; i++) {
            try {
                throw ex; //repeatedly thrown
            } catch (Exception e) {
                // do nothing
            }
        }
        long stop = System.currentTimeMillis();
        return stop - start;
    }
}

Menonaktifkan optimasi = teknik hebat! Saya akan mengedit jawaban asli saya agar tidak menyesatkan siapa pun
Austin D

3
Menonaktifkan pengoptimalan tidak lebih baik daripada menulis patokan yang cacat, karena mode yang ditafsirkan murni tidak ada hubungannya dengan kinerja dunia nyata. Kekuatan JVM adalah kompiler JIT, jadi apa gunanya mengukur sesuatu yang tidak mencerminkan cara kerja aplikasi yang sebenarnya?
apangin

2
Ada lebih banyak aspek dalam menciptakan, melempar, dan menangkap pengecualian daripada dikonversikan dalam 'patokan' ini. Saya sangat menyarankan Anda untuk membaca posting ini .
apangin
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.