性能優化本身是一個很大的主題,涵蓋程式的方方面面,任何不慎的操作,都有可能對性能造成比較大的影響,要知道程式的性能是可以累加的,多處的性能低下,會影響整體的性能,其後果可能也是多方面的,本文總結了目前工作中,所需要知道的大部分性能優化點,一部分個人總結,一部分來自于網際網路。但整體上,都是提綱性的,并沒有列出具體的執行個體,因為寫這方面主題的達人實在太多了,是以,我得站在巨人的肩膀上,具體細節,請參考對應的連結。
遊戲:界面很卡,fps低
搜尋性能差
伺服器響應速度慢
os:界面無響應
使用者流失
項目失敗
引發災難
…
硬體
io操作
資料庫
網絡
函數調用
資料結構
程式邏輯
….
硬體性能
架構設計
核心資料結構
……
小規模修改程式,提高性能:程式的性能是可以累加的
簡單代碼設計
類和函數設計:合适的資料類型和算法
用快速排序代替冒泡排序
用二分查找代替線性查找
業務邏輯的實作
高效代碼≠更好代碼≠高品質代碼(易修改,易擴充,易維護)
80/20法則:4%的代碼占用了50%以上的運作時間
随時随地進行優化 ==>将陷入無休止的優化泥潭
開發階段前期:功能都未實作,何談優化,但在設計的時候,需要考慮到對應風險。
開發階段中後期:功能部分完善,通過現象進行子產品優化
開發階段後期:功能完善,充分考慮整體性,通過現象進行系統優化
後期優化無法滿足性能要求----架構先天不足,隻能大批量重構
過分提高性能會損害程式的可讀性和可維護性
優先實作功能,然後再進行優化
程式功能的正确性比性能更重要
真正高性能的程式設計:
more small, more fast
more simple, more fast
在進行性能優化前,確定該功能是否已基本完整。

key points
是否考慮通過修改需求來提高性能?
是否考慮通過修改整體設計提高性能?
是否考慮通過來修改類的設計提高性能?
在開始修改前,程式是完全正确的麼?
是否在修改前是否進行性能評估?
是否記錄了每次修改後的性能的變化?
如果沒有帶來預期的性能提高,是否完全放棄所做的代碼調整?
是否對每個性能瓶頸進行不止一次的嘗試?
是否反複進行代碼調整,直到最優?
找出瓶頸,集中火力對付占用絕大部分資源的少量代碼。
途徑:
code review
通過最基本優化政策優化代碼
代碼性能測量
1) 通過性能log記錄函數調用時間,找出瓶頸點
logutil.d(tagconstant.tag_performance,"load media info into group begin......");
longstart = system.currenttimemillis();
……
logutil.d(tagconstant.tag_performance,“load media info into group end, total time: ” + (system.currenttimemillis() -start) + “ ms”);
2) 使用性能分析工具:
monkey
monkeyrunner
注:後兩個我個人沒有使用過,不做介紹,優先推薦使用traceview工具
優化本身是一個很大的主題,我這是主要是針對于android平台來說的。個人認為,優化可以分成好幾部分:
一是java文法層次通用的優化,如盡量使用局部變量(棧變量),io緩沖等。
二是通用的android性能優化,如同步改異步,各種緩存的使用等
三是應用程式内部的性能優化,如内部邏輯、資料插入及查找、資料結構的安排與組織等
以下部分針對于上述3種類型,分别進行簡要說明:
類和對象使用技巧
盡量少用new生成新對象
使用clone方法生成新對象
盡量使用局部變量棧變量
減少方法調用
使用final類和final/static/private方法
讓通路執行個體内變量的 getter/setter
方法變成final
避免不需要的 instanceof 操作
避免不需要的造型操作
盡量重用對象
不要重複初始化變量
不要過分建立對象
java io技巧
使用緩沖提高io性能
lnputstream比reader高效,outputstream比writer高效
在适當的時候用byte替代char
有緩沖的塊操作io要比緩沖的流字元io快
序列化時使用原子類型
在finally塊中關閉stream
sql語句
盡早釋放資源
異常exceptions使用技巧
避免使用異常來控制程式流程
盡可能重用異常
将trycatch 塊移出循環
線程使用技巧
在使用大量線程threading的場合使用線程池管理
防止過多的同步
同步方法而不要同步整個代碼段
在追求速度的場合用arraylist和hashmap代替vector和hashtable
使用notify而不是notifyall
不要在循環中調用 synchronized同步方法
單線程應盡量使用 hashmap,arraylist
其它常用技巧
使用移位操作替代乘除法操作可以極大地提高性能
對vector中最後位置的添加删除操作要遠遠快于埘第一個元素的添加删除操作
當複制數組時使用system.arraycop方法
使用複合指派運算符
用int而不用其它基本類型
在進行資料庫連接配接和網絡連接配接時使用連接配接池
用壓縮加快網絡傳輸速度一種常用方法是把相關檔案打包到一個jar檔案中
在資料庫應用程式中使用批處理功能
消除循環體中不必要的代碼
為vectors 和 hashtables定義初始大小
如果隻是查找單個字元的話用charat代替startswith
在字元串相加的時候使用 charat()代替startswith() 如果該字元串隻有一個字元的話
對于 boolean 值避免不必要的等式判斷
對于常量字元串用string 代替 stringbuffer
用stringtokenizer 代替 indexof 和substring
使用條件操作符替代if cond else 結構
不要在循環體中執行個體化變量
确定 stringbuffer的容量
不要總是使用取反操作符
與一個接口 進行instanceof 操作
采用在需要的時候才開始建立的政策
通過 stringbuffer 的構造函數來設定他的初始化容量可以明顯提升性能
合理使用 javautilvector
不要将數組聲明為public static final
haspmap 的周遊
array數組和 arraylist 的使用
stringbufferstringbuilder 的差別
盡量使用基本資料類型代替對象
用簡單的數值計算代替複雜的函數計算比如查表方式解決三角函數問題
使用具體類比使用接口效率高但結構彈性降低了但現代 ide都可以解決這個問題
考慮使用靜态方法
應盡可能避免使用内在的get/set 方法
避免枚舉浮點數的使用
二維數組比一維數組占用更多的記憶體空間大概是 10倍計算
sqlite
奇偶判斷
實際上,android本身的training文檔也提供給我們很多可參考的内容,以下僅枚舉一些keypoint,當然,有的内容是與上面的政策是重複的。
總體上來說,想要寫出高效代碼,我們要遵循兩條基本的原則:
不作沒有必要的工作。
盡量避免記憶體配置設定。
key point:
避免建立不必要的對象
用靜态代替虛拟
避免内部的getters/setters
對常量使用static final修飾符
使用改進的for循環文法(for-each)
在有内部類的情況考慮使用包權限來替代私有通路
避免使用浮點數
了解并使用類庫
合理利用native方法
布局優化
盡量減少android程式布局中view的層次,view層次越多,效率就越低
使用<include/>複用布局
使用viewholder、thread使listview滾動更加流暢
其它優化點
合理使用異步操作
懶加載:目前不需要的資料,不要加載,即按需加載。懶加載的範圍是廣泛的,可以是資料,可以是view,或者其它
使用緩存
圖檔緩存:包括memorycache和diskcache,推薦使用官方demo中的cache
單例資料緩存:建立一個管理資料的類,管理所有資料,當主界面消失後,由于application本身沒有實際退出,是以,資料本身也沒有釋放掉,下次啟動時,省去了加載資料的時間,當然,這并不是一個好的行為。
使用listview、gridview的view緩存
使用message自身的緩存,避免重複建立message執行個體
線程池
資料池(可參考message pool的實作方式)
資料庫優化
sql優化
建立索引
使用事務
…...
算法優化
資料結構使用
不要全部使用arraylist,合理使用linkedlist等易于插入和删除的集合
合理使用hashmap、hashset來提高查找性能
使用sparsearray、sparseintarray、sparsebooleanarray來替代某些特定的hashmap
其它政策
可以考慮延遲處理,避免在同一時間幹過多的事情
該部分的優化應該是依據程式的不同而不同,沒有萬般皆準的法則,目前從我做過的程式來看,實際上,上述的性能優化點基本上已經能夠解決很多性能問題了。
在我所做的程式中,主要的優化手段是:
程式邏輯簡化:分析代碼,去掉備援邏輯
資料結構的優化:對集合類的靈活使用,特别是hashmap的使用,極大的提高查找性能。
批量處理原則:對于需要循環調用地方,采用批量處理
性能優化本身是對代碼的重構和反思過程,通過優化性能,能找出我們很多設計、邏輯上的不足。
優化的過程往往很痛苦,但在做過這個過程後,個人在程式設計水準、設計水準上都會有很大的提高。
很多優化的思想應該做為編碼規範的一部分,需要我們反複實踐,在寫代碼時,第一反應所得出的代碼就是最優的。