国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

接口設計中的性能問題

2019-11-18 12:10:20
字體:
來源:轉載
供稿:網友

  一、接口設計對性能的影響
  
  
  許多開發者直到開發工作的最后一個周期才會考慮程序的性能,希望以此來徹底避免可能出現的性能問題。一些時候,他們的策略能夠成功。然而,對于性能調整的必要性和最終是否能夠成功,早期的設計決策將產生重大的影響。假如某個程序的性能可能會成為問題,那么,性能優化應當從設計和編寫代碼的第一天開始。
  
  
  本文將從程序早期設計的角度探討java程序的性能。討論圍繞一個Java程序設計中常見的問題展開:創建臨時對象。類的對象創建操作往往是在設計時確定的,假如不經全面深入的考慮,很可能為后期的性能問題留下隱患。
  
  
  性能問題的產生有著多種原因。最簡單的問題是程序員選擇了一種拙劣的算法,例如用冒泡排序對一個規模很大的數據集進行排序,又如頻繁地重新計算一個經常使用的數據,而不是緩沖這個數據。這類性能問題都很輕易發現,而且發現之后也可以很快地改正。然而,許多Java性能問題起源于一個更深層的、更難修正的地方——程序組件的接口設計。
  
  
  今天,許多程序都使用了組件。這些組件有的是程序員自己設計的,有的是從外部購入的。即使程序并不真正地依靠于預先構造的組件,面向對象的設計原則也鼓勵開發者采用組件,因為組件化設計有助于簡化程序的設計、開發和測試。盡管組件化設計在這些方面的優勢是不可否認的,但必須熟悉到,由組件實現的接口會對使用它的程序的行為和性能產生重要影響。
  
  
  讀到這里,也許有的讀者會問,接口到底對性能有哪些影響呢?類的接口定義不僅規定了類可以調用哪些函數,而且還規定了它的對象創建過程以及為了使用對象而調用方法的過程。類如何定義構造函數和方法將決定對象是否可以重用,決定它的方法是否要創建(或要求客戶程序創建)臨時對象,決定客戶程序為了使用該類而必須調用的方法數量。所有這些因素,都將對程序的性能產生影響。
  
  
  改善Java程序性能的重要措施之一是:避免不必要的對象創建操作。創建對象是一種代價昂貴的操作,減少臨時對象和交換對象(起過渡作用的對象)的創建是十分必要的。
  
  
  Java的String對象是一個不可變的對象,每次修改或者構造字符串都會有一個新的String對象被創建。在涉及文本處理的程序中(比如Web應用),String對象占了很大的比重,避免或者減少String對象成為一個重要問題。下面我們就以String對象為例,看看如何減少不必要的對象創建操作。
  二、實例:正則表達式匹配器
  
  
  假設有一個郵件服務器MailBot,它要處理郵件MIME頭中的發件日期和郵件地址信息。為減少創建String對象的操作,MailBot沒有為郵件頭中的每一行或每一個元素創建String對象,而是把這些信息放入一個字符緩沖區,然后調用正則表達式匹配器處理郵件頭中的每一行。在這個方案中,正則表達式匹配器的性能是十分重要的。下面是一種可能的正則表達式匹配器接口設計:
  
  
  
  public class AwfulRegEXPMatcher {
  
  /** 以指定的正則表達式為基礎,創建一個對指定輸入字符串操作的
  
  * 匹配器 */
  
  public AwfulRegExpMatcher(String regExp, String inputText);
  
  /** 查找輸入文本的下一次模式匹配
  
  * 返回匹配的文本,或返回null。*/
  
  public String getNextMatch();
  
  }
  
  
  
  
  
  由于這個匹配器對象和輸入文本緊密結合,每次調用它的時候,都需要創建一個新的匹配器對象。即使這個匹配器實現了高效的表達式匹配算法,在大量使用該對象的場合,它仍會對性能產生不利影響。為減少對象創建操作,以一種可重用的形式構造匹配器是十分必要的。
  
  
  下面是另一種可能的接口定義,它支持匹配器重用,但效果仍然不是很好:
  
  
  
  public class BadRegExpMatcher {
  
  public BadRegExpMatcher(String regExp);
  
  /** 匹配指定的正則表達式和輸入文本,
  
  * 如匹配成功,則返回匹配的字符串;否則
  
  * 返回null */
  
  public String match(String inputText);
  
  /** 查找下一次匹配,如不能匹配,則返回null */
  
  public String getNextMatch();
  
  }
  
  
  
  
  
  從表面上看,這個接口定義已經相當不錯,問題在哪里呢?假如只考慮功能,它沒有錯;但從性能的角度來看,它存在不少問題。首先,這個匹配器要求調用者創建一個表示待匹配文本的String對象。盡管MailBot極力避免創建String對象,但為滿足BadRegExpMatcher的要求,程序不得不創建一個String對象:
  
  
  
  BadRegExpMatcher dateMatcher = new BadRegExpMatcher(...);
  
  while (...) {
  
  ...
  
  String headerLine = new String(myBuffer, thisHeaderStart,
  
  thisHeaderEnd-thisHeaderStart);
  
  String result = dateMatcher.match(headerLine);
  
  if (result == null) { ... }
  
  }
  
  
  
  
  
  其次,即使MailBot只對是否存在匹配的字符串感愛好(不需要匹配的具體文本),匹配器也要構造一個返回字符串。也就是說,即使只用BadRegExpMatcher檢驗一下郵件頭中的日期格式是否符合要求,我們也要創建兩個String對象——輸入給匹配器的字符串,作為結果返回的字符串。兩個對象聽起來不多,但假如MailBot要為每一個郵件頭的每一行創建兩個對象,累積的性能影響仍是相當可觀的。
  
  
  值得指出的是,我們可以用返回一個輕量級的Match對象的方法來替代返回String的方法,然后在Match對象中提供getOffset()、getLength()和getMatchString()方法,但性能的改善程度仍然有限。創建String對象意味著創建一個char[]數組以及復制全部數據,創建Match對象的開銷或許比創建String對象稍小一些,但這種方案仍然要求創建中間對象,對于調用者來說這仍然是一種不小的開銷。我們看到,拙劣的組件接口設計影響了使用組件的程序。即使到了最后,我們找到了一個更好的、不要求提供String對象的正則表達式組件,那時的代碼修改工作可能也是相當復雜了。
  三、改進接口設計
  
  
  那么,如何定義BadRegExpMatcher才能避免出現上述問題呢?首先,BadRegExpMatcher不應該限制輸入參數的類型,它應該能夠接受各種便于調用者提供的輸入格式。第二,BadRegExpMatcher不應該自動生成匹配結果的String表示形式,而是應該返回足夠的信息,使得調用者能夠在必要時得到匹配結果的String表示形式。下面是改進后的接口定義:
  
  
  
  class BetterRegExpMatcher {
  
  public BetterRegExpMatcher(...);
  
  /** 支持各種輸入形式的匹配器:字符串,字符數組,字符數組的子集
  
  * 如不能匹配,則返回-1;如找到匹配結果,則返回匹配結果開始位
  
  * 置的偏移量 */
  
  public int match(String inputText);
  
  public int match(char[] inputText);
  
  public int match(char[] inputText, int offset, int length);
  
  /** 查找下一次匹配,假如有的話 */
  
  public int getNextMatch();
  
  /** 假如存在匹配,返回匹配結果的長度 */
  
  public int getMatchLength();
  
  /** 返回匹配的字符串,為想要獲得String形式返回值的調用者
  
  提供方便 */
  
  public String getMatchText();
  
  }
  
  
  
  
  
  新的接口降低了對輸入文本格式的要求。現在,MailBot可以按照如下方式調用match():
  
  
  
  int resultOffset = dateMatcher.match(myBuffer, thisHeaderStart,
  
  thisHeaderEnd-thisHeaderStart);
  
  if (resultOffset < 0) { ... }
  
  
  
  
  
  改進后的接口在不創建任何新對象的條件下,達到了預期的目標。不僅如此,新的接口設計符合Java語言接口設計“大量的簡單方法”的設計原則。
  
  
  額外的對象創建操作對性能的具體影響程度與match()方法的工作量有關。要獲得性能影響程度的上限,可以創建和測試兩個空的正則表達式匹配器(不執行任何實際操作的正則表達式匹配器)。在Sun JDK 1.3環境下,分別用上面的代碼片段調用空的BetterRegExpMatcher類和空的BadRegExpMatcher,前者的效率要比后者高出50倍。對于只支持子字符串匹配的簡單實現,BetterRegExpMatcher要比BadRegExpMatcher快5倍。
  四、交換類型
  
  
  在前面的例子中,MailBot原先擁有一個字符數組,但BadRegExpMatcher強制MailBot提供一個字符串,因此MailBot不得不把字符數組轉換成String再調用匹配器,從而導致一次應該可以避免的對象創建操作。具有諷刺意義的是,為了便于訪問輸入的文本,許多BadRegExpMatcher的實現又會馬上把String轉換成字符數組。這不僅又導致一次對象創建操作,而且它還意味著,做了那么多工作之后我們又回到了一開始就有的數據類型。無論是MailBot還是BadRegExpMatcher,兩者實際上都不需要String類型的數據,String只是各個組件之間交換數據的一種格式。
  
  
上一篇:final類

下一篇:圖解Java連接數據庫內幕

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 阳江市| 和龙市| 台中县| 自贡市| 伽师县| 抚州市| 景谷| 多伦县| 安乡县| 玉屏| 普定县| 临潭县| 玛纳斯县| 平果县| 夏邑县| 博罗县| 衡东县| 西城区| 盐津县| 无为县| 太原市| 淮阳县| 满洲里市| 林州市| 拜泉县| 浦城县| 鄂伦春自治旗| 阜康市| 收藏| 包头市| 依安县| 慈溪市| 桦川县| 天津市| 沙洋县| 荃湾区| 寿阳县| 万州区| 如东县| 西安市| 南丰县|