35個Java 代碼性能優化總結

来源:https://www.cnblogs.com/xiaogeng/archive/2018/06/02/9126441.html
-Advertisement-
Play Games

內容轉自網路(具體轉載哪裡忘記了,有知道的) 前言 代碼優化,一個很重要的課題。可能有些人覺得沒用,一些細小的地方有什麼好修改的,改與不改對於代碼的運行效率有什麼影響呢?這個問題我是這麼考慮的,就像大海裡面的鯨魚一樣,它吃一條小蝦米有用嗎?沒用,但是,吃的小蝦米一多之後,鯨魚就被喂飽了。代碼優化也是 ...


內容轉自網路(具體轉載哪裡忘記了,有知道的)

前言

代碼優化,一個很重要的課題。可能有些人覺得沒用,一些細小的地方有什麼好修改的,改與不改對於代碼的運行效率有什麼影響呢?這個問題我是這麼考慮的,就像大海裡面的鯨魚一樣,它吃一條小蝦米有用嗎?沒用,但是,吃的小蝦米一多之後,鯨魚就被喂飽了。代碼優化也是一樣,如果項目著眼於儘快無BUG上線,那麼此時可以抓大放小,代碼的細節可以不精打細磨;但是如果有足夠的時間開發、維護代碼,這時候就必須考慮每個可以優化的細節了,一個一個細小的優化點累積起來,對於代碼的運行效率絕對是有提升的。

 

代碼優化的目標是:

 

1、減小代碼的體積

2、提高代碼運行的效率

 

代碼優化細節

 

1、儘量指定類、方法的final修飾符

帶有final修飾符的類是不可派生的。在Java核心API中,有許多應用final的例子,例如java.lang.String,整個類都是final的。為類指定final修飾符可以讓類不可以被繼承,為方法指定final修飾符可以讓方法不可以被重寫。如果指定了一個類為final,則該類所有的方法都是final的。Java編譯器會尋找機會內聯所有的final方法,內聯對於提升Java運行效率作用重大,具體參見Java運行期優化。此舉能夠使性能平均提高50%。

 

2、儘量重用對象

特別是String對象的使用,出現字元串連接時應該使用StringBuilder/StringBuffer代替。由於Java虛擬機不僅要花時間生成對象,以後可能還需要花時間對這些對象進行垃圾回收和處理,因此,生成過多的對象將會給程式的性能帶來很大的影響。

 

3、儘可能使用局部變數

調用方法時傳遞的參數以及在調用中創建的臨時變數都保存在棧中速度較快,其他變數,如靜態變數、實例變數等,都在堆中創建,速度較慢。另外,棧中創建的變數,隨著方法的運行結束,這些內容就沒了,不需要額外的垃圾回收。

 

4、及時關閉流

Java編程過程中,進行資料庫連接、I/O流操作時務必小心,在使用完畢後,及時關閉以釋放資源。因為對這些大對象的操作會造成系統大的開銷,稍有不慎,將會導致嚴重的後果。

 

5、儘量減少對變數的重覆計算

明確一個概念,對方法的調用,即使方法中只有一句語句,也是有消耗的,包括創建棧幀、調用方法時保護現場、調用方法完畢時恢復現場等。所以例如下麵的操作:

for (int i = 0; i < list.size(); i++){...}

建議替換為:

for (int i = 0, int length = list.size(); i < length; i++){...}

這樣,在list.size()很大的時候,就減少了很多的消耗

 

6、儘量採用懶載入的策略,即在需要的時候才創建

例如:

String str = "aaa";if (i == 1){list.add(str);}

建議替換為:

if (i == 1){String str = "aaa";list.add(str);}

 

7、慎用異常

異常對性能不利。拋出異常首先要創建一個新的對象,Throwable介面的構造函數調用名為fillInStackTrace()的本地同步方法,fillInStackTrace()方法檢查堆棧,收集調用跟蹤信息。只要有異常被拋出,Java虛擬機就必須調整調用堆棧,因為在處理過程中創建了一個新的對象。異常只能用於錯誤處理,不應該用來控製程序流程。

 

8、不要在迴圈中使用try…catch…,應該把其放在最外層

除非不得已。如果毫無理由地這麼寫了,只要你的領導資深一點、有強迫症一點,八成就要罵你為什麼寫出這種垃圾代碼來了

 

9、如果能估計到待添加的內容長度,為底層以數組方式實現的集合、工具類指定初始長度

比如ArrayList、LinkedLlist、StringBuilder、StringBuffer、HashMap、HashSet等等,以StringBuilder為例:

(1)StringBuilder()      // 預設分配16個字元的空間

(2)StringBuilder(int size)  // 預設分配size個字元的空間

(3)StringBuilder(String str) // 預設分配16個字元+str.length()個字元空間

可以通過類(這裡指的不僅僅是上面的StringBuilder)的來設定它的初始化容量,這樣可以明顯地提升性能。比如StringBuilder吧,length表示當前的StringBuilder能保持的字元數量。因為當StringBuilder達到最大容量的時候,它會將自身容量增加到當前的2倍再加2,無論何時只要StringBuilder達到它的最大容量,它就不得不創建一個新的字元數組然後將舊的字元數組內容拷貝到新字元數組中—-這是十分耗費性能的一個操作。試想,如果能預估到字元數組中大概要存放5000個字元而不指定長度,最接近5000的2次冪是4096,每次擴容加的2不管,那麼:

(1)在4096 的基礎上,再申請8194個大小的字元數組,加起來相當於一次申請了12290個大小的字元數組,如果一開始能指定5000個大小的字元數組,就節省了一倍以上的空間

(2)把原來的4096個字元拷貝到新的的字元數組中去

這樣,既浪費記憶體空間又降低代碼運行效率。所以,給底層以數組實現的集合、工具類設置一個合理的初始化容量是錯不了的,這會帶來立竿見影的效果。但是,註意,像HashMap這種是以數組+鏈表實現的集合,別把初始大小和你估計的大小設置得一樣,因為一個table上只連接一個對象的可能性幾乎為0。初始大小建議設置為2的N次冪,如果能估計到有2000個元素,設置成new HashMap(128)、new HashMap(256)都可以。

 

10、當複製大量數據時,使用System.arraycopy()命令

 

11、乘法和除法使用移位操作

例如:

for (val = 0; val < 100000; val += 5){a = val * 8;b = val / 2;}

用移位操作可以極大地提高性能,因為在電腦底層,對位的操作是最方便、最快的,因此建議修改為:

for (val = 0; val < 100000; val += 5){a = val << 3;b = val >> 1;}

移位操作雖然快,但是可能會使代碼不太好理解,因此最好加上相應的註釋。

 

12、迴圈內不要不斷創建對象引用

例如:

for (int i = 1; i <= count; i++){Object obj = new Object();}

這種做法會導致記憶體中有count份Object對象引用存在,count很大的話,就耗費記憶體了,建議為改為:

Object obj = null;for (int i = 0; i <= count; i++) { obj = new Object(); }

這樣的話,記憶體中只有一份Object對象引用,每次new Object()的時候,Object對象引用指向不同的Object罷了,但是記憶體中只有一份,這樣就大大節省了記憶體空間了。

 

13、基於效率和類型檢查的考慮,應該儘可能使用array,無法確定數組大小時才使用ArrayList

 

14、儘量使用HashMap、ArrayList、StringBuilder,除非線程安全需要,否則不推薦使用Hashtable、Vector、StringBuffer,後三者由於使用同步機制而導致了性能開銷

 

15、不要將數組聲明為public static final

因為這毫無意義,這樣只是定義了引用為static final,數組的內容還是可以隨意改變的,將數組聲明為public更是一個安全漏洞,這意味著這個數組可以被外部類所改變

 

16、儘量在合適的場合使用單例

使用單例可以減輕載入的負擔、縮短載入的時間、提高載入的效率,但並不是所有地方都適用於單例,簡單來說,單例主要適用於以下三個方面:

(1)控制資源的使用,通過線程同步來控制資源的併發訪問

(2)控制實例的產生,以達到節約資源的目的

(3)控制數據的共用,在不建立直接關聯的條件下,讓多個不相關的進程或線程之間實現通信

 

17、儘量避免隨意使用靜態變數

要知道,當某個對象被定義為static的變數所引用,那麼gc通常是不會回收這個對象所占有的堆記憶體的,如:

/** * Java學習交流QQ群:589809992 我們一起學Java! */public class A{ private static B b = new B();}

此時靜態變數b的生命周期與A類相同,如果A類不被卸載,那麼引用B指向的B對象會常駐記憶體,直到程式終止

 

18、及時清除不再需要的會話

為了清除不再活動的會話,許多應用伺服器都有預設的會話超時時間,一般為30分鐘。當應用伺服器需要保存更多的會話時,如果記憶體不足,那麼操作系統會把部分數據轉移到磁碟,應用伺服器也可能根據MRU(最近最頻繁使用)演算法把部分不活躍的會話轉儲到磁碟,甚至可能拋出記憶體不足的異常。如果會話要被轉儲到磁碟,那麼必須要先被序列化,在大規模集群中,對對象進行序列化的代價是很昂貴的。因此,當會話不再需要時,應當及時調用HttpSession的invalidate()方法清除會話。

 

19、實現RandomAccess介面的集合比如ArrayList,應當使用最普通的for迴圈而不是foreach迴圈來遍歷

這是JDK推薦給用戶的。JDK API對於RandomAccess介面的解釋是:實現RandomAccess介面用來表明其支持快速隨機訪問,此介面的主要目的是允許一般的演算法更改其行為,從而將其應用到隨機或連續訪問列表時能提供良好的性能。實際經驗表明,實現RandomAccess介面的類實例,假如是隨機訪問的,使用普通for迴圈效率將高於使用foreach迴圈;反過來,如果是順序訪問的,則使用Iterator會效率更高。可以使用類似如下的代碼作判斷:

if (list instanceof RandomAccess){ for (int i = 0; i < list.size(); i++){}}else{Iterator<span font-size:16px;line-height:2;"="" style="font-family: "Microsoft YaHei"; font-size: 16px; line-height: 2;">1、整型預設的數據類型是int,long l = 12345678901234L,這個數字已經超出了int的範圍了,所以最後有一個L,表示這是一個long型數。順便,浮點型的預設類型是double,所以定義float的時候要寫成”"float f = 3.5f”

2、接下來再寫一句”int ii = l + i;”會報錯,因為long + int是一個long,不能賦值給int

 

32、公用的集合類中不使用的數據一定要及時remove掉

如果一個集合類是公用的(也就是說不是方法裡面的屬性),那麼這個集合裡面的元素是不會自動釋放的,因為始終有引用指向它們。所以,如果公用集合裡面的某些數據不使用而不去remove掉它們,那麼將會造成這個公用集合不斷增大,使得系統有記憶體泄露的隱患。

 

33、把一個基本數據類型轉為字元串,基本數據類型.toString()是最快的方式、String.valueOf(數據)次之、數據+”"最慢

把一個基本數據類型轉為一般有三種方式,我有一個Integer型數據i,可以使用i.toString()、String.valueOf(i)、i+”"三種方式,三種方式的效率如何,看一個測試:

public static void main(String[] args){ int loopTime = 50000;Integer i = 0; long startTime = System.currentTimeMillis(); for (int j = 0; j < loopTime; j++){String str = String.valueOf(i);}System.out.println("String.valueOf():" + (System.currentTimeMillis() - startTime) + "ms");startTime = System.currentTimeMillis(); for (int j = 0; j < loopTime; j++){String str = i.toString();}System.out.println("Integer.toString():" + (System.currentTimeMillis() - startTime) + "ms");startTime = System.currentTimeMillis(); for (int j = 0; j < loopTime; j++){String str = i + "";}System.out.println("i + "":" + (System.currentTimeMillis() - startTime) + "ms");}

運行結果為:

String.valueOf():11ms Integer.toString():5ms i + "":25ms

所以以後遇到把一個基本數據類型轉為String的時候,優先考慮使用toString()方法。至於為什麼,很簡單:

1、String.valueOf()方法底層調用了Integer.toString()方法,但是會在調用前做空判斷

2、Integer.toString()方法就不說了,直接調用了

3、i + “”底層使用了StringBuilder實現,先用append方法拼接,再用toString()方法獲取字元串

三者對比下來,明顯是2最快、1次之、3最慢

 

34、使用最有效率的方式去遍歷Map

遍歷Map的方式有很多,通常場景下我們需要的是遍歷Map中的Key和Value,那麼推薦使用的、效率最高的方式是:

public static void main(String[] args){HashMap<string, string="">();hm.put("111", "222");Set<map.entry<string, string="">> iter = entrySet.iterator(); while (iter.hasNext()){Map.Entry<string, string="">
<span font-size:16px;line-height:2;"="" data-filtered="filtered" style="font-family: 微軟雅黑; font-size: 12px; white-space: normal; ">
</map.entry<string,>

<string, string=""><map.entry<string, string=""><string, string=""></map.entry


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • RabbitMQ基礎教程之使用進階篇 相關博文,推薦查看: I. 背景 前一篇基本使用篇的博文中,介紹了rabbitmq的三種使用姿勢,可以知道如何向RabbitMQ發送消息以及如何消費,但遺留下幾個疑問,本篇則主要希望弄清楚這幾點 Exchange聲明的問題(是否必須聲明,如果不聲明會怎樣) Ex ...
  • 推薦寫法 具體解釋可以往後看。 原理 1. 每一個 執行文件,都自動創建一個 對象,同時, 對象會創建一個叫 的屬性,初始化的值是 。即: 2. 是引用 的值 3. 模塊導出的時候,真正導出的執行是 ,而不是 1與2的demo 3的demo 為了驗證真正導出的是 而不是 ,我們對 修改如下: 的輸出 ...
  • 心血來潮,手機上導出的圖片全部按日期放在不同文件夾,很是麻煩,想放在一起方便瀏覽,手動操作費時費力,想到bat命令,不是很熟,看到python欣喜不已,很是方便 遞歸遍歷文件,剪切出來,刪除空文件夾 不足:未考慮各種異常 ...
  • 通過上篇文章,大家已經能夠清楚的瞭解到treeMap插入結點的過程,那麼本篇文章就來分析下TreeMap刪除一個結點時,內部數據結構發生了怎樣的變化。 TreeMap刪除某個結點的源碼分析 1 /** 2 * 刪除節點,並平衡紅黑樹的操作 3 * 4 * @Param Entry<K,V> p 要刪 ...
  • 要求: 1、創建三個游戲人物,分別是: 蒼井井,女,18,初始戰鬥力1000 東尼木木,男,20,初始戰鬥力1800 波多多,女,19,初始戰鬥力2500 2、游戲場景,分別: 草叢戰鬥,消耗200戰鬥力 自我修煉,增長100戰鬥力 多人游戲,消耗500戰鬥力 ...
  • 在生活中我們時常會遇到同步的問題,而且大多數的實際問題都是線程的同步問題 我這裡以生活中的火車售票來進行舉例: 假設現在我們總共有1000張票要進行出售,共有10個出售點,那麼當售票到最後只有一張票時,每個售票點如何去處理這唯一的一張票?或者對於某一張票而言,假設它正在售票站1售票的程式執行過程中, ...
  • Java的迴圈跳轉語句一:Java迴圈跳轉語句之 break;二:Java迴圈跳轉語句之 continue; ...
  • 首先要談的是,今天的話題所聊的程式員包含哪些人? 在中國,寫程式,不僅僅是一種興趣,更多的時候,還是一種普通職業和謀生工具 大公司有厲害的程式員,優秀的架構師,但大量的小公司也有很多普通的程式員。在我這些年的工作經歷中,也越來越深刻的感受到普通程式員的影響和力量。對於高階程式員,所謂八仙過海各有神通 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...