熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java高級技術 >> 正文

為JAVA性能而設計(3)

2013-11-23 19:41:05  來源: Java高級技術 

  作者eclipse
  
  為性能而設計 第三部分: 遠程接口[/b]
  
   [b]學習怎樣在設計 Java 類的時候避免性能冒險[/b]
  
  [b][u]概述[/u][/b]
  
  許多 Java 的通常性能問題來源於設計過程早期的類設計想法中 早在開發者開始考慮
  性能問題之前 在這個系列中 Brian Goetz 討論了一些通常的 Java 性能的冒險
  釋了怎樣在設計時間避免它們 在這篇文章中 它檢驗了遠程應用程序中的特定的性能
  問題
  
  By Brian Goetz
  
  翻譯 by SuperMMX
  
  這個系列探索一些早期的設計思想對應用程序的性能產生影響的方法 第一部分 檢查了
  一個類的對象創建行為是如何嵌入它的接口中的 特定的接口實際上要求一個類創建臨時
  對象 或者需要它的調用者來創建臨時對象 才能使用這個類 因為臨時對象的創建對 Java
  程序來說是一個性能指標 當你在設計時候 能再對你的類接口做一些回顧來檢查性能冒?
  這是值得的
  
  在第一和第二部分我集中於對象的創建 因為對許多 Java 程序來說這是一個很大的性能侍?
  但是 在分布式的應用程序中 象建立在 RMI CORBA 或者 COM 之上的程序 一個完全煌
  的性能問題就在眼前了 這篇文章探索一些針對遠程程序的性能問題 演示你怎樣才能通
  單地檢查一個類的接口來預測到分布式應用程序中的性能問題
  
  
  閱讀這個 為性能而設計 系列:
  
  第一部分: 接口事宜
  第二部分: 減少對象創建
  第三部分: 遠程接口
  
  [b][u]遠程調用的概觀[/u][/b]
  
  在分布式的應用程序中 一個運行在一個系統中的對象可以調用另一個系統中的一個對象
  的方法 這個通過很多使遠程對象表現為本地的結構的幫助而實現 要訪問一個遠程對象
  你首先要找到它 可以通過使用目錄或者命名服務來實現 象 RMI 注冊 JNDI 或者 CORBA
  命名服務
  
  當你通過目錄服務得到一個遠程對象的引用時 你並沒有得到那個對象的實際的引用
  是一個實現了和遠程對象同樣接口的stub對象的引用 當你調用一個stub對象的方法時
  對象把方法的所有參數匯集起來 把它們轉化成一個字節流的表現形式 類似於序列化
  過程 這個stub對象把匯集的參數通過網絡傳遞給一個skeleton對象 把參數分解出來
  你想調用的實際的對象的方法 然後這個方法向skeleton對象返回一個值 skeleton對象遜禱刂禱慵
  起來 把它傳送給stub對象 stub對象把它分解出來 傳遞給調用者 Phew! 一個單獨的椒
  調用要做這麼多的工作 很明顯 除去表面的相似性 一個遠程方法調用比本地方法調用
  更大
  
  以上描述浏覽了一些對於程序性能非常重要的細節 當一個遠程方法返回的不是一個原類?
  而是一個對象時 會發生什麼? 不一定 如果返回的對象是一種支持遠程方法調用的類型
  它就創建一個中stub對象和一個skeleton對象 在這種情況下需要在注冊表中查找一個遠潭韻?
  這顯然是一個高代價的操作 (遠程對象支持一種分布式的垃圾回收的形式 包括了每一個
  參與的 JVM 維護一個線程來和其他 JVM 的維護線程進行通訊 來回傳遞引用信息) 如果
  返回的對象不支持遠程調用 這個對象所有的域和引用的對象都要匯集起來 這也是一個
  代價的操作
  
  [b][u]遠程和本地方法調用的性能比較[/u][/b]
  
  遠程對象訪問的性能特征和本地的不一樣:
  遠程對象的創建比本地對象創建代價要高 不僅僅是當它不存在時要創建它 而且stub對
  和skeleton對象也要創建 還要互相感知
  
  遠程方法調用還包括網絡的傳遞 匯集起來的參數必須發送到遠程系統 而且響應也需
  匯集起來 在調用程序重新得到控制權之前發送回來 匯集 分解 網絡延時 實際的遠
  調用所導致的延遲都加在一起; 客戶端通常是等待所有這些而步驟完成 一個遠程調用也
  大地依賴於底層網絡的延時
  
  不同的數據類型有不同的匯集開支 匯集原類型相對來說花費少一些; 匯集簡單的對象
  Point 或者 String 要多一些; 匯集遠程對象要多得多 而匯集那些引用非常多的對象的
  對象(象 collection 等)要更多 這和本地調用完全矛盾 因為傳遞一個簡單對象的引用槐
  一個復雜對象的引用花費多
  
  [b][u]接口設計是關鍵[/u][/b]
  
  設計不好的遠程接口可能完全消除一個程序的性能 不幸的是 對本地對象來說好的接口
  的特性對遠程對象可能不適合 大量的臨時對象創建 就象在本系列的第一 二部分討論?
  也能阻礙分布式的應用程序 但是大量的傳遞更是一個性能問題 所以 調用一個在一個
  時對象(比如一個 Point)中返回多個值的方法比多次調用來分別得到它們可能更有效 (注
  意 這和在第二部分給本地對象岢龅慕ㄒ?I 完全相反)
  
  
  [b][u]實際遠程應用程序的一些重要的性能指導:[/u][/b]
  
  提防不必要的數據傳遞 如果一個對象要同時得到幾個相關的項 如果可能的話 在一個
  遠程調用中實現可能容易一些
  
  當調用者可能不必要保持一個遠程對象的引用時 提防返回遠程的對象
  
  當遠程對象不需要一個對象的拷貝時 提防傳遞復雜對象
  
  幸運的是 你可以通過簡單查看遠程對象的接口來找出所有的問題 要求做任何高層動作
  的方法調用序列可以從類接口中明顯看到 如果你看到一個通常的高層操作需要許多連續
  的遠程方法調用 這就是一個警告信號 可能你需要重新查看一下類接口
  
  
  [b][u]減少遠程調用代價的技巧[/u][/b]
  
  一個例子 考慮下面假定的管理一個組織目錄的應用程序: 一個遠程的 Directory 對象包
  含了 DirectoryEntry 對象的引用 表現了電話簿的入口
  [code]
  public interface Directory extends Remote {
   DirectoryEntry[] getEntries();
   void addEntry(DirectoryEntry entry);
   void removeEntry(DirectoryEntry entry);
  }
  public interface DirectoryEntry extends Remote {
   String getName();
   String getPhoneNumber();
   String getEmailAddress();
  }
  [/code]
  現在假設你想在一個 GUI email 程序中使用 Directory 的東西 程序首先調用
  getEntries() 來得到入口的列表 接著在每個入口中調用 getName() 計算結果的列表
  當用戶選擇一個時 應用程序在相應的入口調用 getEmailAdress() 來得到 email 地址
  
  在你能夠寫一封 email 之前有多少遠程方法調用必須發生? 你必須調用 getEntries() 一
  次 地址簿中每個入口調用一次 getName() 一次 getEmailAddress() 所以如果在地址
  中有 N 個入口 你必須進行 N + 次遠程調用 注意你也需要創建 N + 個遠程對象引
  用 也是一個代價很高的操作 如果你的地址簿有許多入口的話 不僅僅是打開 email 窗
  口的時候非常慢 也造成了網絡阻塞 給你的目錄服務程序造成高負載 導致可擴展性的
  問題
  
  現在考慮增強的 Directory 接口:
  [code]
  public interface Directory extends Remote {
   String[] getNames();
   DirectoryEntry[] getEntries();
   DirectoryEntry getEntryByName(String name);
   void addEntry(DirectoryEntry entry);
   void removeEntry(DirectoryEntry entry);
  }
  [/code]
  這將減少多少你的 email 程序所造成的花費呢? 現在你可以調用 DirectorygetNames()
  一次就可以同時得到所有的名字 只需要給你想要發送 email 的容器調用 getEntryByName()
  這個過程需要 個遠程方法調用 而不是 N + 和兩個遠程對象 而不是 N +
  如果地址簿有再多一點的名字 這個調用的減少在程序的響應和網絡負載和系統負載有很
  大的不同
  
  用來減少遠程調用和引用傳遞的代價的技術叫做使用次要對象標識符 使用一個對象的標
  屬性 在這個例子中 是 name 而不是傳回一個遠程對象 作為對象的一個輕量級晔斗?
  次要標識符包含了它描述的對象足夠的信息 這樣你只需要獲取你實際需要的遠程對象
  在這個目錄系統的例子中 一個人的名字是一個好的次要標識符 在另一個例子中 一個
  安全皮包管理系統 一個采購標識號可能是一個好的次要標識符
  
  另一個減少遠程調用數量的技巧是塊獲取 你可以進一步給 Directory 接口加個方法 來一
  次獲取多個需要的 DirectoryEntry 對象:
  [code]
  public interface Directory extends Remote {
   String[] getNames();
   DirectoryEntry[] getEntries();
   DirectoryEntry getEntryByName(String name);
   DirectoryEntry[] getEntriesByName(String names[]);
   void addEntry(DirectoryEntry entry);
   void removeEntry(DirectoryEntry entry);
  }
  [/code]
  現在你不僅可以得到需要的遠程 DirectoryEntry 也可以用單獨一個遠程方法調用得到
  要的所有的入口 雖然這並不減少匯集的代價 但極大地較少了網絡往返的次數 如果網
  延遲很重要的話 就可以產生一個響應更快的系統(也能減少這個網絡的
From:http://tw.wingwit.com/Article/program/Java/gj/201311/27291.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.