android try catch并不影响性能
今天,簡單講講android里使用try--catch語句是否會影響性能。
我在app的代碼里有一些for循環里面有try - catch語句,擔心循環里一直執行try - catch語句會影響效率,所以在網上查詢了資料,后來發現并不影響性能。這里記錄一下。
1、JAVA性能調優-將try/catch塊移出循環
據說把try/catch塊放入循環體內,會極大的影響性能。因為使用了try/catch模塊的使用,會讓JAVA虛擬機做很多額外的工作。就好比對每個人說,“嗨,哥們,路上可能有蛇。于是聽到的人只好手拿木棍,小心翼翼的往前走”。
把try/catch塊放到循環外面,就好比對一大群人說,“嗨,兄弟們,路上可能有蛇。于是聽到的人安排部分人員拿木棍往前走,其他人基本不受影響”
這個理論蠻不錯的,測試下對性能的實際影響
2、將try/catch塊在循環條件進行比對的源代碼
3、運行結果
...預熱循環開始 ...
...預熱結束
...預熱階段,try/catch在循環中耗時: 76507316
...預熱階段,try/catch不在循環中耗時: 76292613
...進入正式循環 ...
...正式運行階段,try/catch在循環中耗時: 389151690
...正式運行階段,try/catch不在循環中耗時: 389874615
4、結論
從測試結果來看,可能我們的JDK(1.6)會自動優化字節碼的緣故,因此try/catch是否在循環中,對整體性能的影響幾乎微乎其微,389151690 389874615 差距非常的小。
以上是沒有發生異常的情況,如果發生異常,那么也就無法比較了。
這里再舉一個例子:
討論的問題
當時討論的是這樣的問題:
比較下面兩種try catch寫法,哪一種性能更好。
結論
在沒有發生異常時,兩者性能上沒有差異。如果發生異常,兩者的處理邏輯不一樣,已經不具有比較的意義了。
分析
要知道這兩者的區別,最好的辦法就是查看編譯后生成的Java字節碼??匆幌聇ry catch到底做了什么。
下面是我的測試代碼
使用javap -c fileName.class輸出對應的字節碼
Compiled from "ForTryAndTryFor.java" public class com.kevin.java.performancetTest.ForTryAndTryFor {public com.kevin.java.performancetTest.ForTryAndTryFor();Code:0: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]);Code:0: invokestatic #2 // Method tryFor:()V3: invokestatic #3 // Method forTry:()V6: returnpublic static void tryFor();Code:0: iconst_31: istore_02: iconst_03: istore_14: iload_15: sipush 10008: if_icmpge 2311: iload_012: i2d13: invokestatic #4 // Method java/lang/Math.sin:(D)D16: pop217: iinc 1, 120: goto 423: goto 3126: astore_127: aload_128: invokevirtual #6 // Method java/lang/Exception.printStackTrace:()V31: returnException table:from to target type2 23 26 Class java/lang/Exceptionpublic static void forTry();Code:0: iconst_31: istore_02: iconst_03: istore_14: iload_15: sipush 10008: if_icmpge 3111: iload_012: i2d13: invokestatic #4 // Method java/lang/Math.sin:(D)D16: pop217: goto 2520: astore_221: aload_222: invokevirtual #6 // Method java/lang/Exception.printStackTrace:()V25: iinc 1, 128: goto 431: returnException table:from to target type11 17 20 Class java/lang/Exception }</init>
指令含義不是本文的重點,所以這里就不介紹具體的含義,感興趣可以到Oracle官網查看相應指令的含義The Java Virtual Machine Instruction Set
好了讓我們來關注一下try catch 到底做了什么。我們就拿forTry方法來說吧,從輸出看,字節碼分兩部分,code(指令)和exception table(異常表)兩部分。當將java源碼編譯成相應的字節碼的時候,如果方法內有try catch異常處理,就會產生與該方法相關聯的異常表,也就是Exception table:部分。異常表記錄的是try 起點和終點,catch方法體所在的位置,以及聲明捕獲的異常種類。通過這些信息,當程序出現異常時,java虛擬機就會查找方法對應的異常表,如果發現有聲明的異常與拋出的異常類型匹配就會跳轉到catch處執行相應的邏輯,如果沒有匹配成功,就會回到上層調用方法中繼續查找,如此反復,一直到異常被處理為止,或者停止進程。具體介紹可以看這篇文章How the Java virtual machine handles exceptions
所以,try 在反映到字節碼上的就是產生一張異常表,只有發生異常時才會被使用。由此得到出開始的結論。
這里再對結論擴充:
try catch與未使用try catch代碼區別在于,前者禁止try語句塊中的代碼進行優化,例如重排序,try catch里面的代碼是不會被編譯器優化重排的。對于上面兩個函數而言,只是異常表中try起點和終點位置不一樣。至于剛剛說到的指令重排的問題,由于for循環條件部分符合happens- before原則,因此兩者的for循環都不會發生重排。當然只是針對這里而言,在實際編程中,還是提倡try catch范圍盡量小,這樣才可以充分發揮java編譯器的優化能力。
至于網上得出的for-try比try-catch要快是由于cpu執行代碼時可能有環境的影響因素,因為手機可能同時執行其他線程,所以測試的時間不準確。具體來說:
原因至少有下面這些:System.currentTimeMillis()測量的只是逝去的時間,并沒有反映出cpu執行該函數真正消耗的時間。
這導致線程未被分配cpu資源時,等待cpu的時間也會被計算進去
JIT優化導致結果出現偏差。
像這種多次循環非常容易觸發JIT的優化機制,關于JIT,這里簡短的介紹一下
在Java編程語言和環境中,即時編譯器(JIT compiler,just-in-time compiler)是一個把Java的字節碼(包括需要被解釋的指令的程序)轉換成可以直接發送給處理器的指令的程序。當你寫好一個Java程序后,源語言的語句將由Java編譯器編譯成字節碼,而不是編譯成與某個特定的處理器硬件平臺對應的指令代碼(比如,Intel的Pentium微處理器或IBM的System/390處理器)。字節碼是可以發送給任何平臺并且能在那個平臺上運行的獨立于平臺的代碼。
簡單來說,JIT會將某些符合條件(比如,頻繁的循環)的字節碼被編譯成目標的機器指令直接執行,從而加快執行速度。可以通過配置-XX:+PrintCompilation參數,觀察JIT。當JIT執行優化時,會在終端輸出相應的優化信息。
類加載時間也被統計進來了。
類首次被使用時,會觸發類加載,產生了時間消耗。
由上面的分析不難看出為什么絕大多數時候tryFor會比forTry快了,JIT編譯耗時和類加載時間會被統計到第一個執行的函數forTry里面。要驗證這個也非常簡單,把兩個函數的調用順序互換,然后再進行測試。
當然,還有一點不能忽略的是System.currentTimeMillis()并不是統計cpu真正執行時間,所以可能測試的結果會有出入。可以使用JProfiler配合Intellij IDEA進行測試,下面會提到。由于這些因素影響著測試結果,從而使得測試結果撲朔迷離
所以總結一下,在沒有異常時,try-catch不影響性能,當發生異常時,才會有多余的消耗。
android try catch并不影響性能就講完了。
就這么簡單。
總結
以上是生活随笔為你收集整理的android try catch并不影响性能的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android 实现仿QQ登录可编辑下拉
- 下一篇: android 使用compareTo比