詳解Java代碼常見(jiàn)優(yōu)化方案
首先,良好的編碼規(guī)范非常重要。在 java 程序中,訪問(wèn)速度、資源緊張等問(wèn)題的大部分原因,都是代碼不規(guī)范造成的。
單例的使用場(chǎng)景
單例模式對(duì)于減少資源占用、提高訪問(wèn)速度等方面有很多好處,但并不是所有場(chǎng)景都適用于單例。
簡(jiǎn)單來(lái)說(shuō),單例主要適用于以下三個(gè)方面:
- 多線程場(chǎng)景,通過(guò)線程同步來(lái)控制資源的并發(fā)訪問(wèn)。
- 多線程場(chǎng)景,控制數(shù)據(jù)共享,讓多個(gè)不相關(guān)的進(jìn)程或線程之間實(shí)現(xiàn)通信(通過(guò)訪問(wèn)同一資源來(lái)控制)。
- 控制實(shí)例的產(chǎn)生,單例只實(shí)例化一次,以達(dá)到節(jié)約資源的目的;
不可隨意使用靜態(tài)變量
當(dāng)某個(gè)對(duì)象被定義為 static 變量,那么 GC 通常是不會(huì)回收這個(gè)對(duì)象所占有的內(nèi)存。
示例如下:
public class A {
private static B b = new B();
}
此時(shí)靜態(tài)變量 b 的生命周期與 A 類(lèi)同步,即如果 A 類(lèi)不卸載,b 對(duì)象會(huì)常駐內(nèi)存,直到程序終止。
創(chuàng)建 Java 對(duì)象使用注意事項(xiàng)
根據(jù)業(yè)務(wù)使用場(chǎng)景,盡量避免在循環(huán)中 new 對(duì)象。
這是因?yàn)橄到y(tǒng)要花費(fèi)時(shí)間來(lái)創(chuàng)建對(duì)象,而且還要花時(shí)間對(duì)這些對(duì)象進(jìn)行管理和垃圾回收。所以在可以控制的范圍內(nèi),盡量保證最大限度地重用對(duì)象,最好能用基本的數(shù)據(jù)類(lèi)型或數(shù)組來(lái)替代對(duì)象。
final 修飾符使用注意事項(xiàng)
final 修飾符的類(lèi)是不可派生的,即不可被繼承。在 java 核心代碼中,有很多 被 final 修飾的類(lèi),如 java.lang.String 類(lèi)。
如果一個(gè)類(lèi)是 final 的,則該類(lèi)所有方法都是 final 的。java 編譯器會(huì)尋找機(jī)會(huì)內(nèi)聯(lián)(inline)所有的 final 方法,這與具體的編譯器實(shí)現(xiàn)有關(guān)。這樣做能夠使性能平均提高 50% 。
示例如下:
class A {
public void setSize (int size) {
this.size = size;
}
private int size;
}
// setSize 方法變?yōu)?final ,性能更好
class A {
final public void setSize (int size) {
this.size = size;
}
private int size;
}
讓訪問(wèn)實(shí)例變量的 getter/setter 方法變成 final:簡(jiǎn)單的 getter/setter 方法應(yīng)該被置成 final ,這會(huì)告訴編譯器此方法不會(huì)被重載,可以變成 ”inlined” 。
局部變量使用規(guī)范
調(diào)用方法時(shí)傳遞的參數(shù)以及在調(diào)用中創(chuàng)建的臨時(shí)變量都保存在棧(Stack)中,速度較快;其他變量,如靜態(tài)變量、實(shí)例變量等,都在堆(Heap)中創(chuàng)建,速度較慢。
處理好包裝類(lèi)型和基本類(lèi)型的使用場(chǎng)所
基本類(lèi)型:byte,short,int,long,float,double,char,boolean
對(duì)應(yīng)包裝類(lèi)型:Byte,Short,Int,Long,Float,Double,Character,Boolean
基本類(lèi)型和包裝類(lèi)型在使用過(guò)程中可以相互轉(zhuǎn)換,但它們所產(chǎn)生的內(nèi)存區(qū)域是完全不同的?;绢?lèi)型的產(chǎn)生和處理都在棧中處理,包裝類(lèi)型是引用類(lèi)型,其對(duì)象是在堆中產(chǎn)生實(shí)例。
在集合類(lèi)對(duì)象,有對(duì)象方面需要的處理使用包裝類(lèi)型合適,其他情況的處理提倡使用基本類(lèi)型。
使用基本數(shù)據(jù)類(lèi)型代替對(duì)象
示例如下:
String s1 = "hello";
這種方式會(huì)創(chuàng)建一個(gè) “hello” 字符串,而且 JVM 的字符緩存池會(huì)緩存這個(gè)字符串。
String s2 = new String("hello");
這種方式除了創(chuàng)建字符串外,s2 所引用的 String 對(duì)象底層包含一個(gè) char[] 數(shù)組,其中依次存放了 h,e,l,l,o 。
synchronized 使用規(guī)范
實(shí)現(xiàn)同步需要很大的系統(tǒng)開(kāi)銷(xiāo)作為代價(jià)的,甚至可能造成死鎖。所以盡量避免無(wú)謂的同步控制。
synchronize 方法被調(diào)用時(shí)會(huì)直接把當(dāng)前對(duì)象鎖住,在該方法執(zhí)行完之前其他線程無(wú)法調(diào)用當(dāng)前對(duì)象的其它方法。比較靈活的用法是使用代碼塊同步代替在方法中同步。
finalize使用規(guī)范
不要將資源清理放在 finalize 方法中完成,這種方法也很少使用。
由于 GC 的工作量很大,尤其是回收 Young 代內(nèi)存時(shí),大都會(huì)引起應(yīng)用程序暫停。如果選擇使用 finalize 方法進(jìn)行資源清理,會(huì)導(dǎo)致 GC 負(fù)擔(dān)加大,程序運(yùn)行效率變差。
不需要線程同步,應(yīng)盡量使用 HashMap 、ArrayList
HashTable 、Vector 等使用了同步機(jī)制,導(dǎo)致降低。
HashMap 使用規(guī)范
創(chuàng)建一個(gè)比較大的 hashMap 時(shí),應(yīng)該使用帶有參數(shù)的構(gòu)造函數(shù)創(chuàng)建對(duì)象。
示例如下:
public HashMap(int initialCapacity, float loadFactor);
hash 擴(kuò)容是一件很耗費(fèi)性能的事,默認(rèn)構(gòu)造函數(shù)創(chuàng)建的對(duì)象的 initialCapacity 只有 16,loadFactor 是 0.75 ,最好準(zhǔn)確的估計(jì)所需要的最佳大小。同樣對(duì)于 Hashtable ,Vectors 也是如此。
減少對(duì)變量的重復(fù)計(jì)算
for (int i = 0; i < list.size(); i++) {...}
// 應(yīng)該改為
for (int i=0, l=list.size(); i < l; i++) {...}
避免不必要的創(chuàng)建對(duì)象
A a = new A();
if (i == 1) {
list.add(a);
}
// 應(yīng)該改為
if (i == 1) {
A a = new A();
list.add(a);
}
finally 使用規(guī)范
在 try-catch 里,使用到的資源要能夠被釋放,以避免資源泄漏,這最好在 finally 塊中去做。無(wú)論程序執(zhí)行是否有異常,finally 里的代碼總是會(huì)執(zhí)行的,這樣可以確保資源的正確關(guān)閉。
StringBuffer使用規(guī)范
StringBuffer 的無(wú)參構(gòu)造函數(shù)會(huì)創(chuàng)建一個(gè)默認(rèn) 16 的字符數(shù)組。在使用過(guò)程中,如果數(shù)組長(zhǎng)度超出 16 ,就要重新分配內(nèi)存,創(chuàng)建一個(gè)容量更大的數(shù)組,并將原先的數(shù)組復(fù)制過(guò)來(lái),再丟棄舊的數(shù)組。
在多數(shù)情況下,可以在創(chuàng)建 StringBuffer 的時(shí)候指定大小,避免了在容量不夠的時(shí)候自動(dòng)增長(zhǎng),以提高性能。
StringBuffer sb= new StringBuffer(int capacity);
顯式釋放空間讓 gc 回收對(duì)象
多數(shù)情況下,方法內(nèi)部的局部引用變量所引用的對(duì)象會(huì)隨著方法結(jié)束而變成垃圾被回收。因此在程序中無(wú)需將局部引用變量顯式設(shè)為 null 。
示例如下:
void gcTest1() {
Object obj = new Object();
……
obj = null;
}
隨著方法 gcTest1() 的執(zhí)行完成,程序中局部引用變量 obj 的作用域就結(jié)束了,這時(shí)沒(méi)有必要執(zhí)行 obj = null 。
反例如下:
void gcTest2(){
Object obj = new Object();
……
obj = null;
//耗時(shí),耗內(nèi)存操作
……
}
此時(shí)需要盡早釋放不再使用的空間,執(zhí)行 obj = null 顯式釋放局部引用變量 obj 。
二維數(shù)組使用規(guī)范
二維數(shù)據(jù)占用的內(nèi)存空間大概是一維數(shù)組的 10 倍以上。
split 使用場(chǎng)景
盡量避免使用 split 。split 使用正則表達(dá)式,效率比較低,如果是頻繁的調(diào)用將會(huì)耗費(fèi)大量資源。
如果確實(shí)需要頻繁的調(diào)用 split ,使用 apache 的 StringUtils.split(string,char) 較好 ,可以緩存結(jié)果。
ArrayList 和 LinkedList 使用規(guī)范
對(duì)于線性表及鏈表,隨機(jī)查詢的操作ArrayList 優(yōu)于 LinkedList ,LinkedList 需要移動(dòng)指針。增加刪除的操作 LinkedList 優(yōu)于 ArrayList ,ArrayList 需要移動(dòng)數(shù)據(jù)。
System.arraycopy() 使用規(guī)范
盡量使用 System.arraycopy() 復(fù)制數(shù)組,它要比通過(guò)循環(huán)來(lái)復(fù)制數(shù)組快的多。
緩存對(duì)象
將經(jīng)常使用的對(duì)象進(jìn)行緩存時(shí),可以使用數(shù)組或者 HashMap 等容器來(lái)緩存。這種方式需要自己管理這些容器,可能導(dǎo)致系統(tǒng)占用過(guò)多的緩存,性能下降。
也可以使用一些第三方的開(kāi)源工具,如 EhCache 、Oscache 進(jìn)行緩存,他們基本都實(shí)現(xiàn)了 FIFO/FLU 等緩存算法。
盡量避免非常大的內(nèi)存分配
有的問(wèn)題不是由于堆內(nèi)存不夠造成的,而是因?yàn)閮?nèi)存分配失敗造成的。(gc會(huì)進(jìn)行內(nèi)存碎片整理)
如果分配的內(nèi)存塊都必須是連續(xù)的,隨著堆越來(lái)越滿,找到較大的連續(xù)塊會(huì)越來(lái)越困難。
try/catch 使用場(chǎng)景
不要在循環(huán)中使用 try/catch 語(yǔ)句,應(yīng)該把 try/catch 放在循環(huán)最外層。
以上所述是小編給大家介紹的Java代碼常見(jiàn)優(yōu)化方案詳解整合,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
Spring?Boot?Nacos?實(shí)現(xiàn)不停服發(fā)布過(guò)程詳解
這篇文章主要為大家介紹了Spring?Boot?Nacos實(shí)現(xiàn)不停服發(fā)布過(guò)程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
java HashMap和HashTable的區(qū)別詳解
這篇文章主要介紹了java HashMap和HashTable的區(qū)別詳解的相關(guān)資料,需要的朋友可以參考下2016-12-12
Solr通過(guò)特殊字符分詞實(shí)現(xiàn)自定義分詞器詳解
最近因?yàn)楣ぷ鞯男枰?,要做一個(gè)分詞器,通過(guò)查找相關(guān)的資料最終用solr實(shí)現(xiàn)了,下面這篇文章主要給大家介紹了關(guān)于Solr通過(guò)特殊字符分詞實(shí)現(xiàn)自定義分詞器的相關(guān)資料,需要的朋友可以參考借鑒,下面隨著小編來(lái)一起看看吧。2017-09-09
定時(shí)任務(wù)注解@Scheduled不生效問(wèn)題及解決
這篇文章主要介紹了定時(shí)任務(wù)注解@Scheduled不生效問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06
SpringMVC 單文件,多文件上傳實(shí)現(xiàn)詳解
這篇文章主要介紹了SpringMVC 單文件,多文件上傳實(shí)現(xiàn)詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09
@JsonSerialize(using = LongToStringUtil.class)注解的使
這篇文章主要介紹了@JsonSerialize(using = LongToStringUtil.class)注解的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
Spring中網(wǎng)絡(luò)請(qǐng)求客戶端WebClient的使用詳解
作為替代,Spring 官方已在 Spring 5 中引入了 WebClient 作為非阻塞式 Reactive HTTP 客戶端,本文將通過(guò)樣例演示如何使用 WebClient,希望對(duì)大家有所幫助2024-04-04

