新聞中心
程序性能優(yōu)化是一個(gè)復(fù)雜的話(huà)題。往往需要結(jié)合具體場(chǎng)景進(jìn)行性能分析,找出瓶頸提出優(yōu)化建議。但是,假設(shè)我們平時(shí)很少關(guān)注細(xì)節(jié)的性能,那么這種情況下,優(yōu)化這些細(xì)節(jié)所帶來(lái)的收益也是相當(dāng)可觀(guān)的。接下來(lái),我們就來(lái)說(shuō)說(shuō)Java代碼細(xì)節(jié)優(yōu)化的一些小技巧。

目前創(chuàng)新互聯(lián)已為上千的企業(yè)提供了網(wǎng)站建設(shè)、域名、網(wǎng)絡(luò)空間、網(wǎng)站改版維護(hù)、企業(yè)網(wǎng)站設(shè)計(jì)、謝家集網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶(hù)導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶(hù)和合作伙伴齊心協(xié)力一起成長(zhǎng),共同發(fā)展。
?復(fù)雜的字符串連接操作使用 StringBuilder
職業(yè)生涯早期,在做字符串連接操作的時(shí)候,肯定會(huì)這么寫(xiě):String a=c+e+d,這個(gè)Java語(yǔ)法糖對(duì)于開(kāi)發(fā)者來(lái)說(shuō)太方便了。但是如果你在循環(huán)中使用“+”,那就得小心了。
String a=null;
for(int i=0;i<1000;i++) {
a=a+i;
}
我們都知道String 是不可變的,因此循環(huán)中對(duì) string 的每一次賦值都會(huì)在堆內(nèi)存中創(chuàng)建一個(gè)新的 String 對(duì)象。在一個(gè)循環(huán)體中,反復(fù)創(chuàng)建多個(gè)無(wú)用的對(duì)象,不僅會(huì)占用內(nèi)存空間,還會(huì)影響GC時(shí)間。所以說(shuō),如果在循環(huán)中遇到字符串拼接,就使用 StringBuilder 而不是“+”。
使用 ThreadPoolExecutor 避免手動(dòng)創(chuàng)建線(xiàn)程
許多初學(xué)者喜歡在編寫(xiě)代碼時(shí)創(chuàng)建線(xiàn)程,這是一種危險(xiǎn)的做法。
如果這個(gè)線(xiàn)程的創(chuàng)建需要處理大量的請(qǐng)求,很可能導(dǎo)致你的程序頻繁的創(chuàng)建和銷(xiāo)毀線(xiàn)程,頻繁的切換線(xiàn)程上下文,浪費(fèi)CPU資源,甚至?xí)谋M內(nèi)存。
因此,建議使用ThreadPoolExecutor,并配置合適的核心線(xiàn)程數(shù)和最大線(xiàn)程數(shù)。
為集合預(yù)分配適當(dāng)?shù)娜萘?/h4>
我們都知道 ArrayList,HashMap 和 ConcurrentHashMap 等集合類(lèi)是可以自動(dòng)擴(kuò)容的,但是這種自動(dòng)擴(kuò)容涉及到底層數(shù)組的復(fù)制和遷移。如果擴(kuò)容頻繁,肯定會(huì)影響程序的性能。所以如果你能估計(jì)出大概的容量,請(qǐng)直接配置初始值。
使用枚舉而不是常量類(lèi)
很多人特別喜歡在項(xiàng)目中創(chuàng)建一個(gè)常量類(lèi),如下:
public class Constant {
public static final String TOKEN_HEADER = "x-request-token";
public static final Integer CODE_SUCCESS = 0;
public static final Integer CODE_REQUEST_FAILED = 1;
public static final Integer CODE_REQUEST_RUNNING = 2;
}為什么不用枚舉呢?Enum 有強(qiáng)制的類(lèi)型驗(yàn)證。同時(shí),使用枚舉類(lèi)的性能更高。并且使用 enum 還有更大的優(yōu)勢(shì),它可以與策略模式一起使用來(lái)提高程序的可擴(kuò)展性。例如:
public enum FileType {
EXCEL(".xlsx"){
@Override
public void download(String path) {
//do download excel file logic
}
}, CSV(".csv") {
@Override
public void download(String path) {
//do download csv file logic
}
};
private String suffix;
FileType(String suffix) {
this.suffix = suffix;
}
public String getSuffix() {
return suffix;
}
public abstract void download(String path);
}如代碼所示,你可以根據(jù)需要?jiǎng)討B(tài)選擇一種策略來(lái)下載文件,直接調(diào)用FileType.EXCEL.download(),無(wú)需關(guān)心代碼細(xì)節(jié)。
使用 NIO 代替?zhèn)鹘y(tǒng) IO
傳統(tǒng)的 IO 已經(jīng)過(guò)時(shí)了。強(qiáng)烈推薦使用 NIO 代替?zhèn)鹘y(tǒng)的 IO。因?yàn)閭鹘y(tǒng)IO采用阻塞IO模型,請(qǐng)求數(shù)據(jù)后,線(xiàn)程從數(shù)據(jù)準(zhǔn)備到數(shù)據(jù)可讀都是阻塞的。
而且,傳統(tǒng)IO如果要往網(wǎng)卡寫(xiě)數(shù)據(jù),需要先把數(shù)據(jù)寫(xiě)到堆內(nèi)存,然后再把數(shù)據(jù)拷貝到堆外的一塊內(nèi)存,再?gòu)挠脩?hù)態(tài)拷貝數(shù)據(jù)到內(nèi)核狀態(tài)緩沖區(qū)。最后CPU通知DMA將數(shù)據(jù)寫(xiě)入網(wǎng)卡,一共經(jīng)歷了3次拷貝。NiO不僅采用了multiplex IO模型,還可以使用direct memory來(lái)減少數(shù)據(jù)拷貝次數(shù),從而提高性能。
使用移位操作
如果你看過(guò)一些JDK的源代碼,比如HashMap,你會(huì)發(fā)現(xiàn)代碼中有很多移位操作。因?yàn)镴DK是比較底層的代碼,對(duì)性能的追求也是極致的。在我們?nèi)粘5木幋a中,可以用移位運(yùn)算來(lái)代替一些乘除運(yùn)算,比如a >> 1 代替 a / 2,a * 16 代替 a << 4。
這個(gè)技巧也能在一定程度上提高性能,但是如果你不擅長(zhǎng),那就不要強(qiáng)求,因?yàn)楫?dāng)代計(jì)算機(jī)的性能已經(jīng)非常強(qiáng)大了,沒(méi)必要為了一個(gè)程序而犧牲代碼的可讀性。
嘗試使用單例模式
如果我們?cè)O(shè)計(jì)一個(gè)不需要考慮線(xiàn)程安全的類(lèi),請(qǐng)用單例模式來(lái)使用這個(gè)類(lèi),這樣可以節(jié)省內(nèi)存。幸運(yùn)的是,對(duì)于我們使用的spring框架,Java bean默認(rèn)是單例的。
降低鎖粒度
假設(shè)我們有一個(gè)共享文檔編輯功能,用戶(hù)會(huì)同時(shí)編輯共享文檔。為了保證文件的正確性,我們需要使用線(xiàn)程安全synchronized來(lái)保證。很多初學(xué)者可能會(huì)這樣寫(xiě)。
public class Test{
private Object lock = new Object();
public void write(String username, String fileName) {
synchronized(lock) {
//do something
}
}
}如果采用上述方式,只有一個(gè)線(xiàn)程可以進(jìn)入同步代碼塊執(zhí)行,其他線(xiàn)程只能掛起等待,即使這些線(xiàn)程可能寫(xiě)入不同的文件。我們可以通過(guò)降低鎖粒度來(lái)提高性能。
public class Test{
public void write(String username, String fileName) {
synchronized(fileName.intern()) {
//do something
}
}
}不要隨意使用靜態(tài)變量
如果你熟悉JVM基礎(chǔ)知識(shí),那么就會(huì)知道如果一個(gè)對(duì)象被定義為靜態(tài)變量,這個(gè)變量的引用就不容易被垃圾回收器回收。
public class Test{
public static A a = new A();
}靜態(tài)變量“a”的生命周期與測(cè)試類(lèi)相同。只要測(cè)試類(lèi)型沒(méi)有被卸載,“a”的引用對(duì)象就會(huì)駐留在內(nèi)存中,直到程序終止。
使用基本數(shù)據(jù)類(lèi)型
在應(yīng)用程序中使用基本數(shù)據(jù)類(lèi)型來(lái)減少內(nèi)存消耗并提高程序性能。如果可以使用 int,請(qǐng)不要使用其 Integer 包裝類(lèi)型,使用double 而不是 Double。
基本數(shù)據(jù)類(lèi)型的包裝類(lèi)實(shí)例存放在堆內(nèi)存中,每次使用都會(huì)在堆內(nèi)存中創(chuàng)建一個(gè)。如果使用基本數(shù)據(jù)類(lèi)型,數(shù)據(jù)存放在棧幀中,棧的訪(fǎng)問(wèn)速度可比堆快很多。
分享名稱(chēng):提升Java應(yīng)用程序的十個(gè)優(yōu)化技巧
分享URL:http://m.5511xx.com/article/cccihhg.html


咨詢(xún)
建站咨詢(xún)
