亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

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

如何提高Java代碼性能的應用解析

2019-11-18 13:09:31
字體:
來源:轉載
供稿:網友

  很多算法用尾遞歸方法表示會顯得格外簡明。編譯器會自動把這種方法轉換成循環,以提高程序的性能。但在 java 語言規范中,并沒有要求一定要作這種轉換,因此,并不是所有的 Java 虛擬機(JVM)都會做這種轉換。這就意味著在 Java 語言中采用尾遞歸表示可能導致巨大的內存占用,而這并不是我們期望的結果。本文中闡述了動態編譯將會保持語言的語義,而靜態編譯則通常不會。說明了為什么這是一個重要問題,并提供了一段代碼來幫助判定您的即時(JIT)編譯器是否會在保持語言語義的同時做尾遞歸代碼轉換。
  
  尾遞歸及其轉換
  相當多的程序包含有循環,這些循環運行的時間占了程序總運行時間的很大一部分。這些循環經常要反復更新不止一個變量,而每個變量的更新又經常依靠于其它變量的值。
  
  快速跟蹤代碼
  清單 1. 把 Integer 集的 Iterator 中的元素相乘的失敗嘗試
  
  這個程序中有一個錯誤,并在運行時拋出了一個異常 ? 這個異常提供了一條有價值的線索。
  
  清單 2. 試圖捕捉不正確的調用
  
  Example2 類中覆蓋的方法 PRodUCtHelp 努力捕捉對 productHelp 的不正確調用,但卻引入了一個新的錯誤。
  
  清單 3. 動態編譯比靜態編譯更好地保持了語言的語義
  
  假如您用兩個不同的靜態編譯器編譯這段代碼,其中一個結果代碼將拋出一個異常,而另一個則不會。真讓人莫名其妙。
  
  清單 4. 您的 JIT 會在保持語言語義的同時轉換尾遞歸代碼嗎?
  
  一個告訴您如何判定您的 JIT 是否會做這種轉換的樣本。
  
  清單 5. 一個動態轉換
  
  這段代碼演示了名義上完善的編譯器將會做怎樣的轉換
  
  假如把迭代看成是尾遞歸函數,那么,就可以把這些變量看成是函數的參數。簡單提醒一下:假如一個調用的返回值被作為調用函數的值立即返回,那么,這個遞歸調用就是尾遞歸;尾遞歸不必記住調用時調用函數的上下文。
  
  由于這一特點,在尾遞歸函數和循環之間有一個很好的對應關系:可以簡單地把每個遞歸調用看作是一個循環的多次迭代。但因為所有可變的參數值都一次傳給了遞歸調用,所以比起循環來,在尾遞歸中可以更輕易地得到更新值。而且,難以使用的 break 語句也經常為函數的簡單返回所替代。
  
  但在 Java 編程中,用這種方式表示迭代將導致效率低下,因為大量的遞歸調用有導致堆棧溢出的危險。解決方案比較簡單:因為尾遞歸函數實際上只是編寫循環的一種更簡單的方式,所以就讓編譯器把它們自動轉換成循環形式。這樣您就同時利用了這兩種形式的優點。
  
  但是,盡管大家都熟知如何把一個尾遞歸函數自動轉換成一個簡單循環,Java 規范卻不要求做這種轉換。不作這種要求的原因大概是:通常在面向對象的語言中,這種轉換不能靜態地進行。相反地,這種從尾遞歸函數到簡單循環的轉換必須由 JIT 編譯器動態地進行。
  
  要理解為什么會是這樣,考慮下面一個失敗的嘗試:在 Integers 集上,把 Iterator 中的元素相乘。因為下面的程序中有一個錯誤,所以在運行時會拋出一個異常。但是,就象在本專欄以前的許多文章中已經論證的那樣,一個程序拋出的精確異常(跟很棒的錯誤類型標識符一樣)對于找到錯誤藏在程序的什么地方并沒有什么幫助,我們也不想編譯器以這種方式改變程序,以使編譯的結果代碼拋出一個不同的異常。
  
  清單 1. 一個把 Integer 集的 Iterator 中的元素相乘的失敗嘗試
  
  import java.util.Iterator;
  
  public class Example {
  
  public int product(Iterator i) {
  return productHelp(i, 0);
  }
  
  int productHelp(Iterator i, int accumulator) {
  if (i.hasNext()) {
  return productHelp(i, accumulator * ((Integer)i.next()).intValue());
  }
  else {
  return accumulator;
  }
  }
  }
  
  注重 product 方法中的錯誤。product 方法通過把 accumulator 賦值為 0 調用 productHelp。它的值應為 1。否則,在類 Example 的任何實例上調用 product 都將產生 0 值,不管 Iterator 是什么值。
  
  假設這個錯誤終于被改正了,但同時,類 Example 的一個子類也被創建了,如清單 2 所示:
  
  清單 2. 試圖捕捉象清單 1 這樣的不正確的調用
  
  import java.util.*;
  
  class Example {
  
  public int product(Iterator i) {
  return productHelp(i, 1);
  }
  
  int productHelp(Iterator i, int accumulator) {
  if (i.hasNext()) {
  return productHelp(i, accumulator * ((Integer)i.next()).intValue());
  }
  else {
  return accumulator;
  }
  }
  }
  
  // And, in a separate file:
  
  import java.util.*;
  
  public class Example2 extends Example {
  int productHelp(Iterator i, int accumulator) {
  if (accumulator < 1) {
  throw new RuntimeException("accumulator to productHelp must be >= 1");
  }
  else {
  return super.productHelp(i, accumulator);
  }
  }
  
  public static void main(String[] args) {
  LinkedList l = new LinkedList();
  l.add(new Integer(0));
  new Example2().product(l.listIterator());
  }
  }
  
  類 Example2 中的被覆蓋的 productHelp 方法試圖通過當 accumulator 小于“1”時拋出運行時異常來捕捉對 productHelp 的不正確調用。不幸的是,這樣做將引入一個新的錯誤。假如 Iterator 含有任何 0 值的實例,都將使 productHelp 在自身的遞歸調用上崩潰。
  
  現在請注重,在類 Example2 的 main 方法中,創建了 Example2 的一個實例并調用了它的 product 方法。由于傳給這個方法的 Iterator 包含一個 0,因此程序將崩潰。
  
  然而,您可以看到類 Example 的 productHelp 是嚴格尾遞歸的。假設一個靜態編譯器想把這個方法的正文轉換成一個循環,如清單 3 所示:
  
  清單 3. 靜態編譯不會優化尾調用的一個示例
  
  int productHelp(Iterator i, int accumulator) {
  while (i.hasNext()) {
  accumulator *= ((Integer)i.next()).intValue();
  }
  return accumulator;
  }
  
  于是,最初對 productHelp 的調用,結果成了對超類的方法的調用。超方法將通過簡單地在 iterator 上循環來計算其結果。不會拋出任何異常。用兩個不同的靜態編譯器來編譯這段代碼,結果是一個會拋出異常,而另一個則不會,想想這是多么讓人感到困惑。您的 JIT 會做這種轉換嗎?因此,如清單 3 中的示例所示,我們不能期望靜態編譯器會在保持語言語義的同時對 Java 代碼執行尾遞歸轉換。相反地,我們必須依靠 JIT 進行的動態編譯。JIT 會不會做這種轉換是取決于 JVM。
  
  要判定您的 JIT 會否轉換尾遞歸的一個辦法是編譯并運行如下小測試類:
  
  清單 4. 判定您的 JIT 能否轉換尾遞歸
  
  public class TailRecursionTest {
  
  private static int loop(int i) {
  return loop(i);
  }
  
  public static void main(String[] args) {
  loop(0);
  }
  }
  
  我們來考慮一下這個類的 loop 方法。這個方法只是盡可能長時間地對自身作遞歸調用。因為它永遠不會返回,也不會以任何方式影響任何外部變量,因此如清單 5 所示替換其代碼正文將保留程序的語義。
  
  清單 5. 一個動態轉換
  
  public class TailRecursionTest {
  
  private static int loop(int i) {
  while (true) {
  }
  }
  
  public static void main(String[] args) {
  loop(0);
  }
  }
  
  而且,事實上這也就是足夠完善的編譯器所做的轉換。假如您的 JIT 編譯器把尾遞歸調用轉換成迭代,這個程序將無限期地運行下去。它所需的內存很小,而且不會隨時間增加。另一方面,假如 JIT 不做這種轉換,程序將會很快耗盡堆??臻g并報告一個堆棧溢出錯誤。
  
  我在兩個 Java SDK 上運行這個程序,結果令人驚奇。在 SUN 公司的 Hotspot JVM(版本 1.3 )上運行時,發現 Hotspot 不執行這種轉換。缺省設置下,在我的機器上運行時,不到一秒鐘堆??臻g就被耗盡了。另一方面,程序在 IBM 的 JVM(版本 1.3 )上運行時卻沒有任何問題,這表明 IBM 的 JVM 以這種方式轉換代碼。
  
  總結
  記住:我們不能寄希望于我們的代碼會總是運行在會轉換尾遞歸調用的 JVM 上。因此,為了保證您的程序在所有 JVM 上都有適當的性能,您應始終努力把那些最自然地符合尾遞歸模式的代碼按迭代風格編寫。但是請注重:就象我們的示例所演示的那樣,以這種方式轉換代碼時很輕易引入錯誤,不論是由人工還是由軟件來完成這種轉換。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久99国产精品久久久久久久久| 日韩在线视频免费观看高清中文| 亚洲日本成人女熟在线观看| 国产日韩欧美中文| 色系列之999| 亚洲欧美在线一区| 69av成年福利视频| 国产精品爱久久久久久久| 成人欧美一区二区三区在线| 尤物九九久久国产精品的分类| 国产成人激情视频| 成人有码视频在线播放| 亚洲一区二区三区乱码aⅴ蜜桃女| 久久人人爽人人爽人人片av高请| 夜色77av精品影院| 欧美国产精品va在线观看| 国产成人av网| 97久久久免费福利网址| 国产97在线视频| 精品久久久久国产| 欧美极品少妇xxxxⅹ喷水| 欧美午夜精品久久久久久久| 欧美日韩亚洲系列| 国产色综合天天综合网| 国产91精品在线播放| 亚洲影院色在线观看免费| 成人写真视频福利网| 日韩av在线影院| 国产成人精品亚洲精品| 8x海外华人永久免费日韩内陆视频| 一区二区三区回区在观看免费视频| 国产精品第二页| 欧美国产日韩一区二区在线观看| 久久久久日韩精品久久久男男| 国产主播精品在线| 欧美一级电影久久| 成人性生交大片免费看小说| 亚洲美女av在线| 中文字幕精品一区久久久久| 影音先锋欧美精品| 日韩精品小视频| 91精品国产91| 日韩av在线影院| 亚洲欧美日韩国产成人| 日韩激情在线视频| 日韩有码在线视频| 亚洲一区免费网站| 日韩精品视频在线观看免费| 两个人的视频www国产精品| 欧美一级片在线播放| 成人福利网站在线观看| 亚洲视频自拍偷拍| 亚洲国产日韩欧美在线图片| 欧美一级成年大片在线观看| 欧美大人香蕉在线| 美女黄色丝袜一区| 91久久久久久久久| 久久免费福利视频| 国产午夜精品全部视频在线播放| 亚洲图片在线综合| 九九九久久久久久| 91丨九色丨国产在线| 欧美午夜精品久久久久久浪潮| 国产成人中文字幕| 国产精品久久久av| 欧日韩不卡在线视频| 日韩在线观看免费高清完整版| 日韩精品久久久久| 国产精品视频久| 久久天天躁夜夜躁狠狠躁2022| 欧美日韩中文在线| 国产精品久久久久久久7电影| 亚洲精品黄网在线观看| 欧美国产激情18| 91精品国产九九九久久久亚洲| 最近2019年日本中文免费字幕| 亚洲香蕉伊综合在人在线视看| 欧美一区二区三区图| 欧美最猛性xxxxx亚洲精品| 欧美精品国产精品日韩精品| 日韩在线观看免费全集电视剧网站| 久久成人人人人精品欧| 欧美日韩国产成人| 岛国视频午夜一区免费在线观看| 欧美放荡办公室videos4k| 欧美诱惑福利视频| 欧美一级淫片aaaaaaa视频| 亚洲第一视频网站| 在线观看视频99| 日本不卡免费高清视频| 91精品久久久久久久久久| 日韩在线观看精品| 国产999在线观看| 亚洲黄色av女优在线观看| 欧美国产日韩免费| 欧美裸体视频网站| 亚洲国产精品成人精品| 欧美一区二区影院| 91亚洲人电影| 亚洲人成电影在线观看天堂色| 欧美精品在线网站| 精品国偷自产在线| 日韩大胆人体377p| 亚洲天堂成人在线视频| 国内精品视频在线| 久久久中精品2020中文| 亚洲久久久久久久久久| 亚洲网站在线观看| 欧美另类xxx| 欧美精品一区三区| 综合久久五月天| 亚洲国产精品女人久久久| 国产精品高潮视频| 性金发美女69hd大尺寸| 亚洲国产精品一区二区久| 一区二区在线视频播放| 亚洲国产精品va| 日韩有码在线视频| 国产精品一区二区久久精品| 高清视频欧美一级| 欧美激情视频给我| 日韩有码在线观看| 亚洲精品www久久久久久广东| 国产精品在线看| 欧美视频专区一二在线观看| 97视频网站入口| 国产主播在线一区| 亚洲欧美激情四射在线日| 国产成人激情视频| 亚洲午夜精品久久久久久久久久久久| 欧美一级高清免费播放| 日韩高清免费观看| 日本不卡免费高清视频| 国产精品中文字幕久久久| 亚洲日本成人网| 国产精品久久久久久久久借妻| 亚洲国产中文字幕在线观看| 91高清视频免费观看| 在线观看久久久久久| 国产精品久久久久久婷婷天堂| 久久成人18免费网站| 日韩精品在线播放| 久久久999精品免费| 黑人精品xxx一区| 久久久久久999| 久久久久久伊人| 国产亚洲一区二区在线| 欧美成人h版在线观看| 日本国产高清不卡| 91chinesevideo永久地址| 色妞色视频一区二区三区四区| 亚洲午夜久久久久久久| 亚洲伊人久久大香线蕉av| 日韩欧美亚洲一二三区| 97精品国产97久久久久久| 日韩精品视频免费专区在线播放| 精品动漫一区二区三区| 国产一区二区丝袜| 亚洲日本中文字幕免费在线不卡| 91精品国产91久久久久久久久| 久久久久九九九九| 欧美乱妇40p| 国产视频精品免费播放| 亚洲最大成人在线|