Java代碼優化建議

總結平常Java開發常見優化策略,持續更新。html

儘量使用局部變量

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

及時關閉流

Java編程過程當中,進行數據庫鏈接、I/O流操做時務必當心,在使用完畢後,及時關閉以釋放資源。由於對這些大對象的操做會形成系統大的開銷,稍有不慎,將會致使嚴重的後果。而釋放資源最好的地方就是finally代碼塊。算法

儘可能減小對變量的重複計算

明確一個概念,對方法的調用,即便方法中只有一句語句,也是有消耗的,包括建立棧幀、調用方法時保護現場、調用方法完畢時恢復現場等。因此例以下面的操做:數據庫

for (int i = 0; i < list.size(); i++){
    ...
}複製代碼

建議替換爲:編程

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

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

儘可能採用懶加載的策略,即在須要的時候才建立

例如:安全

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

建議替換爲:服務器

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

慎用異常

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

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

好比ArrayList、LinkedLlist、StringBuilder、StringBuffer、HashMap、HashSet等等,以StringBuilder爲例:工具

  • StringBuilder()     // 默認分配16個字符的空間
  • StringBuilder(int size)  // 默認分配size個字符的空間
  • StringBuilder(String str) // 默認分配16個字符+str.length()個字符空間

能夠經過類(這裏指的不只僅是上面的StringBuilder)的構造函數來設定它的初始化容量,這樣能夠明顯地提高性能。好比StringBuilder吧,length表示當前的StringBuilder能保持的字符數量。由於當StringBuilder達到最大容量的時候,它會將自身容量增長到當前的2倍再加2,不管什麼時候只要StringBuilder達到它的最大容量,它就不得不建立一個新的字符數組而後將舊的字符數組內容拷貝到新字符數組中----這是十分耗費性能的一個操做。試想,若是能預估到字符數組中大概要存放5000個字符而不指定長度,最接近5000的2次冪是4096,每次擴容加的2無論,那麼:

  • 在4096 的基礎上,再申請8194個大小的字符數組,加起來至關於一次申請了12290個大小的字符數組,若是一開始能指定5000個大小的字符數組,就節省了一倍以上的空間
  • 把原來的4096個字符拷貝到新的的字符數組中去

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

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

乘法和除法使用移位操做

例如:

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;
}複製代碼

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

循環內不要不斷建立對象引用

例如:

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罷了,可是內存中只有一份,這樣就大大節省了內存空間了。

基於效率和類型檢查的考慮,應該儘量使用array,沒法肯定數組大小時才使用ArrayList

儘可能使用HashMap、ArrayList、StringBuilder,除非線程安全須要,不然不推薦使用Hashtable、Vector、StringBuffer,後三者因爲使用同步機制而致使了性能開銷

儘可能避免隨意使用靜態變量

要知道,當某個對象被定義爲static的變量所引用,那麼gc一般是不會回收這個對象所佔有的堆內存的,如:

public class A{
    private static B b = new B();  
}複製代碼

此時靜態變量b的生命週期與A類相同,若是A類不被卸載,那麼引用B指向的B對象會常駐內存,直到程序終止

及時清除再也不須要的會話

爲了清除再也不活動的會話,許多應用服務器都有默認的會話超時時間,通常爲30分鐘。當應用服務器須要保存更多的會話時,若是內存不足,那麼操做系統會把部分數據轉移到磁盤,應用服務器也可能根據MRU(最近最頻繁使用)算法把部分不活躍的會話轉儲到磁盤,甚至可能拋出內存不足的異常。若是會話要被轉儲到磁盤,那麼必需要先被序列化,在大規模集羣中,對對象進行序列化的代價是很昂貴的。所以,當會話再也不須要時,應當及時調用HttpSession的invalidate()方法清除會話。

參考文章:

https://www.cnblogs.com/xrq730/p/4865416.html

更多信息能夠關注個人我的博客:逸竹小站逸竹小站

也歡迎關注個人公衆號:yizhuxiaozhan,二維碼:公衆號二維碼

相關文章
相關標籤/搜索