qileilove

          blog已經轉移至github,大家請訪問 http://qaseven.github.io/

          Java遠程方法調用RMI

            前段時間,公司做web service的時候,看了一下資料,當時看見一個叫rmi的東西(遠程方法調用),最近閑著,所以看了一下 ,感覺挺簡單的!所以寫了一個例子提供給大家把!

            rmi的服務端,必須要使用接口,同時還有接口的實現類!所以下面的兩個文件是接口類和接口的實現類!

            UserDao 接口:

          1. /** 
          2.  * 遠程接口     必須繼承與Remote對象 
          3.  * @author spring sky 
          4.  * date: 2012年2月7日 10:55:05 
          5.  * Email:vipa1888@163.com 
          6.  * QQ:840950105 
          7.  */ 
          8. public interface UserDao extends Remote{  
          9.     /** 
          10.      * 簡單的測試方法 
          11.      * @param name 
          12.      */ 
          13.     public void sayName(String name) throws RemoteException;   
          14. }

            UserDaoImpl實現類

          1. /** 
          2.  *  
          3.  *  接口的實現類    必須繼承UnicastRemoteObject(單一遠程對象)   實現UserDao自己的接口 
          4.  * @author spring sky 
          5.  * date: 2012年2月7日 10:56:05 
          6.  * Email:vipa1888@163.com 
          7.  * QQ:840950105 
          8.  */ 
          9. public class UserDaoImpl extends UnicastRemoteObject implements UserDao {  
          10.     public UserDaoImpl() throws RemoteException {  
          11.     }  
          12.     @Override 
          13.     public void sayName(String name) {  
          14.         if(name!=null&&!name.equals(""))  
          15.         {  
          16.             System.out.println("我的名字是:"+name);  
          17.         }else{  
          18.             System.err.println("名字不為空....");  
          19.         }  
          20.     }  
          21. }

            對外的提供一個服務,服務中已經共享了url給外界訪問

          1. /** 
          2.  * 使用main方法啟動一個服務,用于外界環境訪問 
          3.  * @author spring sky 
          4.  * date:2012年2月7日 10:57:37 
          5.  * Email:vipa1888@163.com 
          6.  * QQ:840950105 
          7.  */ 
          8. public class StartService {  
          9.     private static final String IP = "127.0.0.1";  
          10.     private static final int PORT = 9999;  
          11.     private static final String REMOTE_NAME = "userDao";  
          12.     private static final String REMOTE_URL = "rmi://"+IP+":"+PORT+"/"+REMOTE_NAME;  
          13.     public static void main(String[] args) {  
          14.         try {  
          15.             UserDao userDao = new UserDaoImpl();    //實例化對象 
          16.             LocateRegistry.createRegistry(PORT);    //注冊端口 
          17.             Naming.bind(REMOTE_URL, userDao);       //綁定遠程服務對象 
          18.             System.out.println("遠程"+REMOTE_NAME+"啟動成功....");  
          19.         } catch (RemoteException e) {  
          20.             System.err.println("遠程對象出錯");  
          21.             e.printStackTrace();  
          22.         } catch (MalformedURLException e) {  
          23.             System.err.println("URL出錯了");  
          24.             e.printStackTrace();  
          25.         } catch (AlreadyBoundException e) {  
          26.             System.err.println("綁定的對象已經存在了");  
          27.             e.printStackTrace();  
          28.         }  
          29.     }  
          30. }

          上面是服務端的代碼,如果啟動沒有任何問題,就可以做客戶端訪問了,其實客戶端的訪問更加的簡單,只需要遠程的接口類和查詢rmi中的url就可以了!

            代碼如下:

          1. /**  
          2.  * 遠程方法調用測試  
          3.  * @author spring sky  
          4.  * date:2012年2月7日 11:12:46  
          5.  * Email:vipa1888@163.com  
          6.  * QQ:840950105  
          7.  * name:石明政  
          8.  */ 
          9. public class TestRemote {  
          10.     public static void main(String[] args) {  
          11.         try {  
          12.             //在rmi服務中查詢userdao的對象  
          13.             UserDao userDao = (UserDao) Naming.lookup("rmi://127.0.0.1:9999/userDao");     
          14.             //調用遠程服務的方法  
          15.             userDao.sayName("spring sky");  
          16.         } catch (MalformedURLException e) {  
          17.             System.err.println("URL出錯");  
          18.             e.printStackTrace();  
          19.         } catch (RemoteException e) {  
          20.             System.err.println("遠程對象出錯");  
          21.             e.printStackTrace();  
          22.         } catch (NotBoundException e) {  
          23.             System.err.println("沒有找到綁定的對象");  
          24.             e.printStackTrace();  
          25.         }  
          26.     }  
          27. }

            以上就是所有的rmi遠程調用代碼了!運行結果如下:

            好了,本人也只是簡單的了解了rmi,如果以后有項目做rmi就可以深入了! 呵呵  ,在這里我突然感覺,想web service也應該和他一樣的原理的把!

           

          posted @ 2012-02-08 16:56 順其自然EVO 閱讀(168) | 評論 (0)編輯 收藏

          Java線程池如何體現自己的用途

            Java線程池就像數據庫連接池一樣,是一個對象池。我們在使用的時候需要不斷的學習,這樣才能不斷的適應相關語言技術的變化。所有的對象池都有一個共同的目的,那就是為了提高對象的使用率,從而達到提高程序效率的目的。

            比如對于Servlet,它被設計為多線程的(如果它是單線程的,你就可以想象,當1000個人同時請求一個網頁時,在第一個人獲得請求結果之前,其它999個人都在郁悶地等待),如果為每個用戶的每一次請求都創建一個新的線程對象來運行的話,系統就會在創建線程和銷毀線程上耗費很大的開銷,大大降低系統的效率。因此,Servlet多線程機制背后有一個Java線程池在支持,線程池在初始化初期就創建了一定數量的線程對象,通過提高對這些對象的利用率,避免高頻率地創建對象,從而達到提高程序的效率的目的。

            下面實現一個最簡單的Java線程池,從中理解它的實現原理。為此我們定義了四個類,它們的用途及具體實現如下:

            Task(任務):這是個代表任務的抽象類,其中定義了一個deal()方法,繼承Task抽象類的子類需要實現這個方法,并把這個任務需要完成的具體工作在deal()方法編碼實現。Java線程池中的線程之所以被創建,就是為了執行各種各樣數量繁多的任務的,為了方便線程對任務的處理,我們需要用Task抽象類來保證任務的具體工作統一放在deal()方法里來完成,這樣也使代碼更加規范。

            Task的定義如下:

            Java代碼

          1.public abstract class Task {
          2.public enum State {
          3./* 新建 */NEW, /* 執行中 */RUNNING, /* 已完成 */FINISHED
          4.}
          5.// 任務狀態
          6.private State state = State.NEW;
          7.public void setState(State state) {
          8.this.state = state;
          9.}
          10.public State getState() {
          11.return state;
          12.}
          13.public abstract void deal();
          14.}
          15.public abstract class Task {
          16.public enum State {
          17./* 新建 */NEW, /* 執行中 */RUNNING, /* 已完成 */FINISHED
          18.}
          19.// 任務狀態
          20.private State state = State.NEW;
          21.public void setState(State state) {
          22.this.state = state;
          23.}
          24.public State getState() {
          25.return state;
          26.}
          27.public abstract void deal();
          28.}

            以上就是對Java線程池的相關代碼介紹。

          posted @ 2012-02-08 16:50 順其自然EVO 閱讀(144) | 評論 (0)編輯 收藏

          Java處理字符串搜索嵌套結構的方法

           在用Java分析HTML文本時,如果要取出有嵌套結構的節點之間的內容,不能直接用正則表達式來處理,因為Java所帶的正則表達式不支持嵌套結構的描述,雖然Perl、.Net、PHP可以支持。這時可以先用正則表達式找出節點在字符串中的位置,然后對節點進行匹配處理,取出匹配節點之間的內容,實現對嵌套結構的處理。

            例如要從

          1. <pre name="code" class="java">data=<div><div>abcd<div></div><form>
          2. <input type='button' value='submit'/></form></div></div><div>1234</div>

            中取出<div></div>之間的內容,希望返回兩個字符串

          1. <pre name="code" class="java"><div>abcd<div></div><form>
          2. <input type='button' value='submit'/></form></div><pre name="code" class="html">和1234

            源代碼如下:

            為了記錄節點在字符串中的值和位置,先定義一個類,保存這些信息:

          1. public class Tag {  
          2.       
          3.     public Tag(String value, int beginPos, int endPos) {  
          4.         super();  
          5.         this.value = value;  
          6.         this.beginPos = beginPos;  
          7.         this.endPos = endPos;  
          8.     }  
          9.     private String value;  
          10.     private int beginPos;  
          11.     private int endPos;  
          12.     public String getValue() {  
          13.         return value;  
          14.     }  
          15.     public void setValue(String value) {  
          16.         this.value = value;  
          17.     }  
          18.     public int getBeginPos() {  
          19.         return beginPos;  
          20.     }  
          21.     public void setBeginPos(int beginPos) {  
          22.         this.beginPos = beginPos;  
          23.     }  
          24.     public int getEndPos() {  
          25.         return endPos;  
          26.     }  
          27.     public void setEndPos(int endPos) {  
          28.         this.endPos = endPos;  
          29.     }  
          30.       
          31. }

            從字符串中獲取節點之間內容的函數如下:

          1.        /** 
          2.  * 獲取字符串之間的內容,如果包含嵌套,則返回最外層嵌套內容 
          3.  *  
          4.  * @param data       
          5.  * @param stag      起始節點串 
          6.  * @param etag      結束節點串 
          7.  * @return 
          8.  */ 
          9. public List<String> get(String data,String stag, String etag){  
          10.     // 存放起始節點,用于和結束節點匹配 
          11.     Stack<Tag> work = new Stack<Tag>();  
          12.     // 保存所有起始和結束節點 
          13.     List<Tag> allTags = new ArrayList<Tag>();  
          14.       
          15.     // 在元字符前加轉義符 
          16.     String nstag = stag.replaceAll("([\\*\\.\\+\\(\\]\\[\\?\\{\\}\\^\\$\\|\\\\])""\\\\$1");  
          17.     String netag = etag.replaceAll("([\\*\\.\\+\\(\\]\\[\\?\\{\\}\\^\\$\\|\\\\])""\\\\$1");  
          18.       
          19.     String reg = "((?:"+nstag+")|(?:"+netag+"))";  
          20.       
          21.     Pattern p = Pattern.compile(reg, Pattern.CASE_INSENSITIVE|Pattern.MULTILINE);  
          22.       
          23.     Matcher m = p.matcher(data);  
          24.       
          25.     while(m.find()){  
          26.         Tag tag = new Tag(m.group(0),m.start(),m.end());  
          27.         allTags.add(tag);  
          28.     }  
          29.     // 保存開始結束節點之間的內容,不含節點 
          30.     List<String> result = new ArrayList<String>();  
          31.       
          32.     for(Tag t : allTags){  
          33.         if (stag.equalsIgnoreCase(t.getValue())){  
          34.             work.push(t);  
          35.         }else if(etag.equalsIgnoreCase(t.getValue())){  
          36.             // 如果棧已空,則表示不匹配 
          37.             if (work.empty()){  
          38.                 throw new RuntimeException("pos "+t.getBeginPos()+" tag not match start tag.");  
          39.             }  
          40.             Tag otag = work.pop();  
          41.             // 如果棧為空,則匹配 
          42.             if (work.empty()){  
          43.                 String sub = data.substring(otag.getEndPos(), t.getBeginPos());  
          44.                 result.add(sub);  
          45.             }  
          46.         }  
          47.           
          48.     }  
          49.       
          50.     // 如果此時棧不空,則有不匹配發生 
          51.     if (!work.empty()){  
          52.         Tag t = work.pop();  
          53.         throw new RuntimeException("tag "+t.getValue()+ "not match.");  
          54.     }  
          55.       
          56.     return result;  
          57.       
          58. }

            函數返回節點之間內容串組成的列表。

            例如 調用 get(data,"<div>", "</div>") 返回含有兩個元素的列表,元素分別為

          <div>abcd<div></div><form><input type='button' value='>'/></form></div>, 1234

            需要注意的是如果節點含有正則表達式的元字符,需要在元字符前加轉義符\\,源代碼中第16,17行實現此功能。

          posted @ 2012-02-07 15:21 順其自然EVO 閱讀(237) | 評論 (0)編輯 收藏

          Java迷題:等于,還是不等于?

           等于還是不等于?

            看來看下面的一段代碼:

            代碼片段1

          1. public static void main(final String[] args) {    
          2.     Integer a = new Integer(100);    
          3.     Integer b = 100;    
          4.     System.out.println(a == b);     
          5. }

            這段代碼的輸出是什么?相信很多人都會很容易的猜到:false,因為a、b兩個對象的地址不同,用“==”比較時是false。恭喜你,答對了。

            再看下面的一段代碼:

            代碼片段2

          1. public static void main(final String[] args) {    
          2.     Integer a = 100;    
          3.     Integer b = 100;    
          4.     System.out.println(a == b);     
          5. }

            你可能會回答,這沒什么不一樣啊,所以還是false。很遺憾,如果你執行上面的一段代碼,結果是true。

            上面的代碼可能讓你有些意外,那好吧,再看看下面的這段代碼:

            代碼片段3

          1. public static void main(final String[] args) {    
          2.     Integer a = 156;    
          3.     Integer b = 156;    
          4.     System.out.println(a == b);     
          5. }

            結果是true嗎?很遺憾,如果你執行上面的一段代碼,結果是false。

            感到吃驚嗎?那最后再看下面的一段代碼:

            代碼片段4

          1. public static void main(final String[] args) {    
          2.     Integer a = Integer.valueOf(100);    
          3.     Integer b = 100;    
          4.     System.out.println(a == b);     
          5. }

            最后的結果,可能你已經猜到了,是true。

            為什么會這樣?

            現在我們分析一下上面的代碼??梢院苋菀椎目闯?,這一系列代碼的最終目的都是用“==”對兩個對象進行比較。Java中,如果用“==”比較兩個對象結果為true,說明這兩個對象實際上是同一個對象,false說明是兩個對象。

            現在,我們來看看為什么會出現上面的現象。

            我們先看代碼片段4:最后的運行結果是true,說明a、b兩個對象實際上是同一個對象。但是a對象是通過調用Integer的valueOf方法創建的,而b對象是通過自動裝箱創建出來的,怎么會是同一個對象呢?難道問題在字節碼那里,畢竟Java程序是依靠虛擬器運行字節碼來實現的。

            通過jdk中自帶的工具javap,解析字節碼,核心的部分摘取如下:

          1. 0:  bipush  100    
          2. 2:  invokestatic    #16//Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;   
          3. 5:  astore_1    
          4. 6:  bipush  100    
          5. 8:  invokestatic    #16//Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;

            代碼中我們只調用了一次Integer.valueOf方法,但是字節碼中出現了兩次對Integer.valueOf方法的調用。那么另一次是哪里呢?只可能在自動裝箱時調用的。因此這段代碼實際上等價于:

          1. public static void main(final String[] args) {    
          2.     Integer a = Integer.valueOf(100);    
          3.     Integer b = Integer.valueOf(100);    
          4.     System.out.println(a == b);     
          5. }

            現在問題就簡單了:看jdk源代碼,查看valueOf方法的具體實現:

          1. public static Integer valueOf(int i) {    
          2.     final int offset = 128;    
          3.     if (i >= -128 && i <= 127) { // must cache    
          4.         return IntegerCache.cache[i + offset];    
          5.     }    
          6.     return new Integer(i);    
          7. }

            看到這兒,上面的代碼就很明確了:對于-128到127的數字,valueOf返回的是緩存中的對象。所以兩次調用Integer.valueOf(100)返回的都是同一個對象。

            我們再先看代碼片段3:根據上面的分析,代碼片段3實際上等價于以下代碼:

          1. public static void main(final String[] args) {    
          2.     Integer a = Integer.valueOf(156);    
          3.     Integer b = Integer.valueOf(156);    
          4.     System.out.println(a == b);     
          5. }

            由于156不在-128到127范圍內,所以兩個對象都是通過new Integer()的方式創建的,所以最后結果為false。

            片段1和片段2就不做具體分析了,相信讀者可以自行分析。

            最后,請大家思考一下問題:通過上面的分析,了解到整數的自動裝箱是通過Integer.valueOf(int number)實現的,那么自動拆箱是如何實現的呢?

          posted @ 2012-02-06 10:54 順其自然EVO 閱讀(256) | 評論 (0)編輯 收藏

          解讀面向對象的程序設計(三)

           接著來看OOP。OOP的未來在未來三年,程序員編寫代碼的方式會發生那些變化?

            Stroustrup:在C++中,假如沒有合適的庫在背后支撐,完成任何重要的工作都可能是很復雜的。而一旦有了合適的庫,任何東西都可以被我們操控于股掌之間。因此,構造和使用程序庫的重要性與日俱增。這也暗示我們,泛型程序設計(genericprogramming)將會越來越多地被運用。只有通過GP,我們才能確保庫的通用性和高效率。我還預期在分布式計算和“組件(components)”應用領域會出現喜人的增長。就大部分程序員而言,通過使用方便適用的程序庫,這些開發工作會變得簡單明了。

            現在有一個趨勢,編譯器廠商試圖把其特有的“對象模型”和圖形界面(GUI)細節推銷給用戶。比如微軟的COM和Inprise的類屬性“properties”。對于用戶來說,這既不必要,也不情愿。我所希望看到的程序庫,應該是用標準C++打造,界面靈活,值得信賴的程序庫。

            通常,這些界面應該是平臺無關的。C++的表達能力極強,即使不使用大量的宏,也應該足以達成這一要求。就算有些地方無法百分之百的遵守這一原則,也應該將對于平臺和廠家的依賴性限制起來。這個目標的完成情況,可以反映軟件工具產業對于應用程序開發行業的關注程度。我懷疑目前對于那些獨立的、跨平臺廠商來說,并不存在相應的市場。如果能夠建立這樣的市場,也許能夠促進廠商們為客戶做出“真正有用的”產品。

            Lindholm:對于編寫代碼的開發者來說,主要的驅動力量仍將是兩個:網絡和分布式——也就是設計和開發非單機軟件的需求。大部分的應用程序將不會是孤零零地運行在單一設備上,而是運用了類似EJB和JSP之類技術的,平臺無關的分布式程序。程序員們將不得不面對分布式計算的重重險阻。這將對許多程序員所依賴的設計模式、技術和直覺構成嚴峻的挑戰。這是選擇編程語言之前必須認識到的,盡管不同語言的設計特性可能促進或者阻礙這一轉化。

            在網絡應用的增長中,一個很重要的部分是小型移動設備和特殊Internet設備的爆炸性增長。這些設備各有各的操作系統,或者只在某種特定的設備領域內有共同的操作系統。我們現在還可以一一列舉出這些設備——家庭接入設備、蜂窩電話、電子報紙、PDA、自動網絡設備等等。但是這些設備領域的數量和深入程度將會很快變得難以估量。我們都知道這個市場大得驚人,PC的興起與之相比不過小菜一碟。因此在這些設備的應用程序市場上,競爭將會相當殘酷。獲勝的重要手段之一,就是盡快進入市場。開發人員需要優秀的工具,迅速高效地撰寫和調試他們的軟件。平臺無關性也是制勝秘訣之一,它使得程序員能夠開發出支持多種設備平臺的軟件。

            我預期的另一個變化是,我們對于代碼(Java)和數據(XML)協同型應用程序的開發能力將會不斷提高。這種協同是開發強大應用程序的核心目標之一。我們從XML的迅速流行和ebXML規范的進展中,已經看到了這個趨勢。ebXML是一個針對電子商務和國際貿易的,基于XML的開放式基礎構架,由聯合國貿易促進和電子商務中心(UN/CEFACT)與結構性信息標準推進組織(OASIS)共同開發。

            我們能否期望出現一個真正的面向組件(component-oriented)的語言?它的創造者會是誰呢?

            Stroustrup:我懷疑,這個領域中之所以缺乏成果,正是因為人們——主要是那些非程序員們——對“組件”這個意義含糊的字眼寄予了太多的期望。這些人士夢想,有朝一日,組件會以某種方式把程序員趕出歷史舞臺。以后那些稱職的“設計員”只需利用預先調整好的組件,把鼠標拖一拖放一放,就把系統組合出來。對于軟件工具廠商來說,這種想法還有另一層意義,他們認為,到時候只有他們才保留有必要的技術,有能力編寫這樣的組件。

            這種想法有一個最基本的謬誤:這種組件很難獲得廣泛歡迎。一個單獨的組件或框架(framework),如果能夠滿足一個應用程序或者一個產業領域對所提出的大部分要求的話,對于其制造者來說就是劃算的產品,而且技術上也不是很困難??墒窃摦a業內的幾個競爭者很快就會發現,如果所有人都采用這些組件,那么彼此之間的產品就會變得天下大同,沒什么區別,他們將淪為簡單的辦事員,主要利潤都將鉆進那些組件/框架供應商的腰包里!

            小“組件”很有用,不過產生不了預期的杠桿效應。中型的、更通用的組件非常有用,但是構造時需要非同尋常的彈性。

            在C++中,我們綜合運用不同共享形式的類體系(classhierarchies),以及使用templates精心打造的接口,在這方面取得了一定的進展。我期待在這個領域取得一些有趣和有用的成果,不過我認為這種成果很可能是一種新的C++程序設計風格,而不是一種新的語言。

            Lindholm:編寫面向組件的應用程序,好像更多的是個投資、設計和程序員管理方面的問題,而不是一個編程語言問題。當然某些語言在這方面具有先天優勢,不過如果說有什么魔術般的新語言能夠大大簡化組件的編寫難度,那純粹是一種誤導。

            微軟已經將全部賭注押在C#上,其他語言何去何從?

            Stroustrup:C++在下一個十年里仍然將是一種主流語言。面對新的挑戰,它會奮起應對。一個創造了那么多出色系統的語言,絕不會“坐視落花流水春去也”。

            我希望微軟認識到,它在C++(我指的是ISO標準C++)上有著巨大的利益,C++是它與IT世界內其他人之間的一座橋梁,是構造大型系統和嵌入式系統的有效工具,也是滿足高性能需求的利器。其他語言,似乎更注重那些四平八穩的商用程序。

            競爭

            C#會不會獲得廣泛的接受,并且擠掉其他的語言?

            Lindholm:通常,一種語言既不會從別的語言那里獲利,也不會被擠掉。那些堅定的Fortran程序員不還用著Fortran嗎?對于個人來說,語言的選擇當然因時而異,但就整體而言,語言的種類只會遞增,也就是說,它們之間的關系是“有你有我”而不是“有你沒我”。

            對于一個新語言的接受程度,往往取決于其能力所及。Java技術被迅速接受,原因是多方面的,Internet和WorldWideWeb接口,在其他技術面前的挫折感,對于Java技術發展方向的全面影響能力,都是原因。另一個重要的原因是Java獨立于廠商,這意味著在兼容產品面前可以從容選擇。

            C#是否會獲得廣泛接受?視情況而定。總的來說,那些對于平臺無關性和廠商無關性漠不關心的程序員,可能會喜歡C#。那些跟微軟平臺捆在一起人當然可能想要尋找VB和VC的一個出色的替代品。但是對于程序跨平臺執行能力特別關注的程序員,將會堅守Java之類的語言。這種能力對于多重訪問設備(multipleaccessdevices)和分布式計算模型至關重要,而Java語言提供了一個標準的、獨立于廠商運行時環境。

            Stroustrup:C#的流行程度幾乎完全取決于微軟投入的資金多少??瓷先#的興起肯定會犧牲掉其他一些語言的利益,但是事實上未必如此。Java的蓬勃發展并沒有給C++帶來衰敗。C++的應用仍然在穩定增長(當然,已經不是爆炸性的增長了)。也許其他的語言也還能獲得自己的一席之地。

            不過,我實在看不出有什么必要再發明一種新的專有語言。特別是微軟,既生VB,何需C#?

            不同OOP語言各有什么優勢和劣勢?

            Stroustrup:C++的優點自始至終都是這么幾條:靈活、高效,而且并非專有語言。現在ISOC++標準的出現,鞏固了最后一點。

            我認為C++的高效是它最基本的優點。這種高效來自于其特有的數據和計算模型,較之Java和C#,這種模型更加貼近機器。不過,哪些程序才真正地渴望這么高的效率?這是個問題。我認為這類程序非常多。人們對于計算機的期望,永遠都超越硬件科技的發展速度。很顯然,Java和C#的設計者的想法不同,他們認為,在很多地方效率問題無關緊要。

            C++主要的缺點,歸罪于糟糕的教育(是那些始終認為C++是個純粹面向對象語言的人,和那些把C++當成C語言變體的人導致了這種情況),歸罪于不同平臺上的不一致性,歸罪于不完整、不標準的編譯器實現,歸罪于平臺無關的系統級程序庫的缺少。

            這些問題歸于一點,就是缺乏一個卓越的廠商,能夠滿足整個C++社區的需求,勇于投入大量的資金開發必要的程序庫。

            Lindholm:Java技術的成功,是因為它在合適的時間,出現在合適的地點,而且合理地選擇了語言和計算平臺的支持目標。Java并不是在所有場合都優于其他OOP語言,但是對于出現的新問題能夠解決得很出色。它面向Internet計算環境,避免了C++中晦澀的結構,成功翻越了繼承機制的惱人問題。垃圾收集機制顯著地提高了生產率,降低了復雜度。

            在網絡背景下使用虛擬機,以及有關安全性和動態加載的一系列設計選擇,迎合了正在出現的需求和愿望。這些特性使Java不僅成為現有程序員的新武器,而且也為新的程序員創造了繁榮的市場空間。

            此外,Java擁有一個標準化的、二進制形式的類庫,提供了必要的(當然并非充分的)平臺與廠商無關性。平臺與廠商無關性要求一項技術必須有清晰的規范,摒棄那些阻礙二進制標準實施的特性。C++雖然有一個ISO標準,但其實甚至對于相同系統與相同指令體系的各個平臺,也提不出一個實用的、各版本兼容的二進制標準。

            歷史上很多使用虛擬機的語言飽受責難,是因為其不夠出色的性能問題,而這要歸過于緩慢的解釋器和糟糕的垃圾收集器。Java的早期實現也因為同樣的問題受到嚴厲的批評。但是自那時起,業界向新的虛擬機實現技術投入了大量資金,取得了顯著的效果,如今在大部分場合,Java的性能跟常規的靜態編譯語言相比毫不遜色。這使得程序員在獲得平臺和廠商無關性的同時,也不必付出性能上的代價。

            C++并沒有強制使用面向對象方法,因此為了編寫出色的面向對象代碼,就要求程序員們有相當強的紀律性。很多公司就是因為這個原因放棄了C++。作為語言,Java的一個突出的優點就是強制面向對象方法,不允許非面向對象的結構。

            C#介于C++和Java之間,腳踏兩只船,因此既不夠安全,又失之復雜。

            對于公司來說,采用新的語言要付出巨大代價。雇不到好的程序員(沒人熟悉這種新語言),培訓費用高得驚人,學習過程中生產率和產品質量下降,多年的經驗隨風消逝,等等。一種語言如何克服這些障礙?

            Lindholm:說得很對,采用新東西確實常常開銷巨大。不過問題是:這個新東西是否能夠節省更多的開支,或者提供巨大的改進,獲取合理的回報?很多公司發現,轉向Java技術不論在開發的后端(盡快進入市場、快速迭代開發、維護簡單性)還是前端(跨平臺發布,適用范圍從低端設備到高端服務器的技術,安全性),都能節省大筆的開銷。

          對于新事物的接納,常常是在痛楚的壓力之下。很大程度上,這正是Java所經歷的。Java的產生,是對當時很多系統的缺陷所做出的反應。Java技術通過下面的手段減輕了開發者的痛楚:1)顧及了網絡計算方面的需求,是應運而生。2)在技術能力的抉擇上,保持良好的品位,顧及了大眾的心理。3)采用適度強制性策略推行設計決定。此外,Java技術已經成為大學教學中的主流,這同樣保證了Java開發者隊伍的不斷壯大。

            但是最重要的一點是,再沒有另一種程序設計技術,能夠像Java那樣允許程序員開發基于Internet的不同平臺之上的應用程序。Java平臺在這方面的杰出表現,已經被大量的實例證明。Java已經成為Internet上的缺省應用程序平臺,JavaAPIs也成為Internet應用程序開發的天然平臺。

            Stroustrup:微軟和Sun把大筆的金錢扔在Java、VB和C#中,并不是因為他良心發現,也不是因為他們真的相信這些語言能夠帶給程序員更美好的生活,而是利益使然。

            有一個說法,認為軟件工具廠商如果能夠把應用程序開發者的專業技術任務負擔起來,將獲取巨大的經濟利益。我對其背后的經濟分析頗為懷疑,我認為這很難成為現實,特別是當應用程序開發者使用開放的、標準化的工具時,他們可以有多種選擇,從而使上面的想法更加不可能。

            多年以前,C++就已經具有泛型能力(也就是templates和STL),有運算符重載,有枚舉類型?我們會不會在Java的未來版本中看到這些特性?Java是不是應該納入這些特性呢?

            Strousturp:從1988-89年起,C++就已經有了templates。但是我們花了不少時間來了解如何最好地運用這個工具,早期各廠家對于template的支持在品質上也有很大的差異。有些編譯器廠商動作遲緩,至少有一個主要的編譯器廠商(好像是指微軟,微軟在VisualC++4.0才開始支持template,在此之前一直聲稱template是過于復雜而又沒什么用的技術,時至今日,VisualC++對于template的支持在主流編譯器中都屬于最差的一檔——譯者注)暗中鼓勵聲名狼藉的反template宣傳,直到他們自己終于學會了這項技術為止。直到今天,對于template的支持在品質上仍然有待改進。

            你上面提到的那些特性,我認為Java(還有C#)應該,也肯定會逐漸引入。那些對于程序員來說最有用的語言特性和概念,將會逐漸集中,成為各家主流語言的必然之選。也就是說,我認為類似析構函數和模板特殊化之類的機制,遠遠比枚舉等機制重要得多。

            Lindholm:Java技術成功的原因之一,就是很清楚哪些不該做。我們得多問幾個為什么:這項特性是不是必不可少?增加它會帶來哪些開銷?運算符重載是C++中一項極其強大的特性,但是它也大大增加了C++語言的復雜度,很多人都難以招架。Java在各種可能的權衡之中,做出了明智的抉擇,找到了能力與需求之間的完美平衡點。

            當然,Java也會發展,而且最重要的是,現在是開發者們在推動發展。Java增加泛型能力這件事,很好地展示了Java是如何通過整個開發者社群的參與,在權衡中決定正確的平衡點。關于增加泛型類型(generictypes)的“Java規格申請”(JavaSpecificationRequest,JSR)已經進入JCP(JavaCommunityProcess)程序,而且已經開發了很長一段時間(參見http://java.sun.com/aboutJava/communityprocess/之JSR-014)?,F在,在JCP中,有超過80個JSRs正在討論中,這充分體現了整個體系對開發者的積極反饋和高度合作,這正是驅動Java平臺不斷進化的動力。

            發展vs.革新(Evolutionvs.Revolution)

            C++是一種發展型的語言,Java和C#似乎更像是革新型語言(它們是從頭設計的)?什么時候,革新型的語言才是必需的呢?

            Lindholm:Java技術并非憑空出世,反而更像是發展型的。Java所有的特性,在Java平臺推出之前,都至少已經存在于另一種環境之中。Java的貢獻在于,在眾多的特性和權衡中,做出了合理的選擇,使得產品既實用,又優雅。Java技術對于程序員的態度是:撫養,但不溺愛。

            Stroustrup:從技術上講,我并不認為Java和C#是什么“從頭設計的”革新型語言。倘若Java是從技術原則出發,從頭設計,大概就不會模仿C/C++那種丑陋和病態的語法了(不必驚訝,Stroustrup在很多場合表示過,C++采用C的語法形式,實在是迫于兼容性。他本人更偏愛Simula的語法——譯者)。

           我認為,只有當程序員們面對的問題發生了根本的變化的時候,或者當我們發現了全新的、極其優越的程序設計技術,又完全不能為現存語言所支持的時候,我們才需要全新的語言。問題是,我們恐怕永遠也碰不到那些“根本”、“全新”的情況。

            我以為,自從OOP問世以來,可稱為“根本”的新型程序設計技術,唯有泛型程序設計(genericprogramming)和生成式程序設計(generativeprogramming)技術,這兩項技術主要是源于C++templates技術的運用,也有一部分曾經被視為面向對象和函數式語言(functionallanguages)的次要成分,現在都變成正式、可用和可承受的技術了。我對于目前C++模板(template)程序設計的成果非常興奮。例如,像POOMA,Blitz++和MTL等程序庫,在很多地方改變了數值計算的方式。

            Java和C#的一個“賣點”,就是它們的簡單性?,F在Java是不是快失去這個賣點了?

            Stroustrup:新語言總是宣稱自己如何如何簡單,對老語言的復雜性頗多非議。其實這種所謂的“簡單性”,簡單地說,就是不成熟性。語言的復雜性,是在解決現實世界中極為煩瑣和特殊的復雜問題的過程中逐漸增加的。一個語言只要活的時間夠長,總會有某些地方逐漸復雜起來,或者是語言本身,或者是程序庫和工具。C++和Java顯然都不例外,我看C#也一樣。如果一種語言能夠度過自己的幼年時代,它會發現,自己無論是體積還是復雜性都大大增加了。

            Lindholm:Java技術的的功能在增加,需要學習的東西也在增加。不過功能的增加并不一定帶來復雜性的增加。Java技術的發展,并沒有使學習曲線更加陡峭,只是讓它繼續向右方延展了。

            標準

            標準化語言和開放型語言各自的優點和缺點何在?

            Lindholm:對于一個開放、不允許專有擴展、具有權威的強制性標準語言或者運行環境來說,不存在什么缺點。允許專有擴展就意味著允許廠商下套子綁架客戶。特別重要的是,必須讓整個平臺,而不只是其中一部分完全標準化,才能杜絕廠商們利用高層次的專有API下套子??蛻粢笥羞x擇廠商的自由,他們既要有創造性,又需要兼容性。

            Stroustrup:對于一個語言,如C/C++來說,建立正式標準(如ISO標準)最大的好處,在于可以防止某一個廠商操縱這種語言,把它當成自己的搖錢樹。多個廠商的競爭給用戶帶來的是較低的價位和較好的穩定性。

            專有語言的好處,一是流行,二是便宜(不過等你被套牢了之后,情況就會起變化),三是對于商業性需求可以做出快速的反應。

            標準化語言的特點之一是,它不能忽略特殊用戶的需求。比如我在AT&T中所考慮的東西,其規模、可靠性和效率要求,跟那些普通廠商關注的大眾軟件相比,根本不可同日而語。那些公司很自然只關注主要的需求。

            然而,多數大機構和身處前沿的公司,都有著特殊的需求。C++的設計是開放、靈活和高效的,能夠滿足我所能想象的任何需求。跟其他的現代語言相比,C++的家長式作風可謂少之又少,原因就在這。當然,不能贊賞這一點的人會詬病C++的“危險”。

            擁有正式和開放標準的語言主要是為編程工具的使用者和客戶服務的,而擁有專屬“標準”的語言,主要是為廠商服務的。

            到這,關于面向對象編程的一些知識,就給大家介紹完了。希望對你有幫助。

          posted @ 2012-02-03 10:02 順其自然EVO 閱讀(168) | 評論 (0)編輯 收藏

          Java中的軟引用,弱引用和虛引用

           在Android的圖片處理中,碰到的一個非常普遍的問題便是OOM錯誤 為此網上也有很多例子,而在之前的一篇轉載里 提到了ListView中加載圖片的ImageLoader,而其中有一處,使用到了名為SoftPreference的類 這是Java中的一個類 也就是所謂的軟引用 在查詢了相關的資料以后 會發現SoftPreference的特性,非常適合用來處理OOM引起的問題 下面是百度文庫的一篇轉載:

            SoftReference、Weak Reference和PhantomRefrence分析和比較

            本文將談一下對SoftReference(軟引用)、WeakReference(弱引用)和PhantomRefrence(虛引用)的理解,這三個類是對heap中java對象的應用,通過這個三個類可以和gc做簡單的交互。

            強引用:

            除了上面提到的三個引用之外,還有一個引用,也就是最長用到的那就是強引用。例如:

          Object o=new Object();   
          Object o1=o;  

            上面代碼中第一句是在heap堆中創建新的Object對象通過o引用這個對象,第二句是通過o建立o1到new Object()這個heap堆中的對象的引用,這兩個引用都是強引用.只要存在對heap中對象的引用,gc就不會收集該對象.如果通過如下代碼:

          o=null;   
          o1=null;

            如果顯式地設置o和o1為null,或超出范圍,則gc認為該對象不存在引用,這時就可以收集它了??梢允占⒉坏扔诰鸵粫皇占裁磿r候收集這要取決于gc的算法,這要就帶來很多不確定性。例如你就想指定一個對象,希望下次gc運行時把它收集了,那就沒辦法了,有了其他的三種引用就可以做到了。其他三種引用在不妨礙gc收集的情況下,可以做簡單的交互。

            heap中對象有強可及對象、軟可及對象、弱可及對象、虛可及對象和不可到達對象。應用的強弱順序是強、軟、弱、和虛。對于對象是屬于哪種可及的對象,由他的最強的引用決定。如下:

          String abc=new String("abc");  //1   
          SoftReference<String> abcSoftRef=new SoftReference<String>(abc);  //2   
          WeakReference<String> abcWeakRef = new WeakReference<String>(abc); //3   
          abc=null; //4   
          abcSoftRef.clear();//5

            第一行在heap對中創建內容為“abc”的對象,并建立abc到該對象的強引用,該對象是強可及的。

            第二行和第三行分別建立對heap中對象的軟引用和弱引用,此時heap中的對象仍是強可及的。

            第四行之后heap中對象不再是強可及的,變成軟可及的。同樣第五行執行之后變成弱可及的。

            SoftReference(軟引用)

            軟引用是主要用于內存敏感的高速緩存。在jvm報告內存不足之前會清除所有的軟引用,這樣以來gc就有可能收集軟可及的對象,可能解決內存吃緊問題,避免內存溢出。什么時候會被收集取決于gc的算法和gc運行時可用內存的大小。當gc決定要收集軟引用是執行以下過程,以上面的abcSoftRef為例:

            1、首先將abcSoftRef的referent設置為null,不再引用heap中的new String("abc")對象。

            2、將heap中的new String("abc")對象設置為可結束的(finalizable)。

            3、當heap中的new String("abc")對象的finalize()方法被運行而且該對象占用的內存被釋放, abcSoftRef被添加到它的ReferenceQueue中。

            注:對ReferenceQueue軟引用和弱引用可以有可無,但是虛引用必須有,參見:

          Reference(T paramT, ReferenceQueue<? super T>paramReferenceQueue)  

            被 Soft Reference 指到的對象,即使沒有任何 Direct Reference,也不會被清除。一直要到 JVM 內存不足且 沒有 Direct Reference 時才會清除,SoftReference 是用來設計 object-cache 之用的。如此一來 SoftReference 不但可以把對象 cache 起來,也不會造成內存不足的錯誤 (OutOfMemoryError)。我覺得 Soft Reference 也適合拿來實作 pooling 的技巧。

               A obj = new A();
          SoftRefenrence sr = new SoftReference(obj);

              //引用時
              if(sr!=null){
                  obj = sr.get();
              }else{
                  obj = new A();
                  sr = new SoftReference(obj);
              }


           弱引用

            當gc碰到弱可及對象,并釋放abcWeakRef的引用,收集該對象。但是gc可能需要對此運用才能找到該弱可及對象。通過如下代碼可以了明了的看出它的作用:

          String abc=new String("abc");    
          WeakReference<String> abcWeakRef = new WeakReference<String>(abc);    
          abc=null;    
          System.out.println("before gc: "+abcWeakRef.get());    
          System.gc();    
          System.out.println("after gc: "+abcWeakRef.get());  

            運行結果:

            before gc: abc
            after gc: null

            gc收集弱可及對象的執行過程和軟可及一樣,只是gc不會根據內存情況來決定是不是收集該對象。

            如果你希望能隨時取得某對象的信息,但又不想影響此對象的垃圾收集,那么你應該用 Weak Reference 來記住此對象,而不是用一般的 reference。

          A obj = new A();

              WeakReference wr = new WeakReference(obj);

              obj = null;

              //等待一段時間,obj對象就會被垃圾回收
            ...

            if (wr.get()==null) { 
            System.out.println("obj 已經被清除了 "); 
            } else { 
            System.out.println("obj 尚未被清除,其信息是 "+obj.toString());
            }
            ...
          }

            在此例中,透過 get() 可以取得此 Reference 的所指到的對象,如果返回值為 null 的話,代表此對象已經被清除。

            這類的技巧,在設計 Optimizer 或 Debugger 這類的程序時常會用到,因為這類程序需要取得某對象的信息,但是不可以 影響此對象的垃圾收集。

            PhantomRefrence(虛引用)

            虛顧名思義就是沒有的意思,建立虛引用之后通過get方法返回結果始終為null,通過源代碼你會發現,虛引用通向會把引用的對象寫進referent,只是get方法返回結果為null。先看一下和gc交互的過程在說一下他的作用。

            1 不把referent設置為null,直接把heap中的new String("abc")對象設置為可結束的(finalizable).

            2 與軟引用和弱引用不同,先把PhantomRefrence對象添加到它的ReferenceQueue中,然后在釋放虛可及的對象。

            你會發現在收集heap中的new String("abc")對象之前,你就可以做一些其他的事情。通過以下代碼可以了解他的作用。

          import java.lang.ref.PhantomReference;    
          import java.lang.ref.Reference;    
          import java.lang.ref.ReferenceQueue;    
          import java.lang.reflect.Field;    
             
          public class Test {    
              public static boolean isRun = true;    
             
              public static void main(String[] args) throws Exception {    
                  String abc = new String("abc");    
                  System.out.println(abc.getClass() + "@" + abc.hashCode());    
                  final ReferenceQueue referenceQueue = new ReferenceQueue<String>();    
                  new Thread() {    
                      public void run() {    
                          while (isRun) {    
                              Object o = referenceQueue.poll();    
                              if (o != null) {    
                                  try {    
                                      Field rereferent = Reference.class   
                                              .getDeclaredField("referent");    
                                      rereferent.setAccessible(true);    
                                      Object result = rereferent.get(o);    
                                      System.out.println("gc will collect:"   
                                              + result.getClass() + "@"   
                                              + result.hashCode());    
                                  } catch (Exception e) {    
             
                                      e.printStackTrace();    
                                  }    
                              }    
                          }    
                      }    
                  }.start();    
                  PhantomReference<String> abcWeakRef = new PhantomReference<String>(abc,    
                          referenceQueue);    
                  abc = null;    
                  Thread.currentThread().sleep(3000);    
                  System.gc();    
                  Thread.currentThread().sleep(3000);    
                  isRun = false;    
              }    
             
          }

            結果為:

            class java.lang.String@96354
            gc will collect:class java.lang.String@96354

          posted @ 2012-02-02 10:14 順其自然EVO 閱讀(3300) | 評論 (0)編輯 收藏

          解讀面向對象的程序設計(二)

             面向對象編程(ObjectOrientedProgramming,OOP,面向對象程序設計)是一種計算機編程架構。接上一篇

            OOP的一條基本原則是計算機程序是由單個能夠起到子程序作用的單元或對象組合而成。OOP達到了軟件工程的三個主要目標:重用性、靈活性和擴展性。

            一、OOP技術的歷史

            面向對象技術最初是從面向對象的程序設計開始的,它的出現以60年代simula語言為標志。80年代中后期,面向對象程序設計逐漸成熟,被計算機界理解和接受,人們又開始進一步考慮面向對象的開發問題。這就是九十年代以MicrosoftVisual系列OOP軟件的流行的背景。

            傳統的結構化分析與設計開發方法是一個線性過程,因此,傳統的結構化分析與設計方法要求現實系統的業務管理規范,處理數據齊全,用戶能全面完整地其業務需求。

            傳統的軟件結構和設計方法難以適應軟件生產自動化的要求,因為它以過程為中心進行功能組合,軟件的擴充和復用能力很差。

            對象是對現實世界實體的模擬,因面能更容易地理解需求,即使用戶和分析者之間具有不同的教育背景和工作特點,也可很好地溝通。

            區別面向對象的開發和傳統過程的開發的要素有:對象識別和抽象、封裝、多態性和繼承。

            對象(Object)是一個現實實體的抽象,由現實實體的過程或信息牲來定義。一個對象可被認為是一個把數據(屬性)和程序(方法)封裝在一起的實體,這個程序產生該對象的動作或對它接受到的外界信號的反應。這些對象操作有時稱為方法。對象是個動態的概念,其中的屬性反映了對象當前的狀態。

            類(Class)用來描述具有相同的屬性和方法的對象的集合。它定義了該集合中每個對象所共有的屬性和方法。對象是類的實例。

            由上分析不難看出,盡管OOP技術更看中用戶的對象模型,但其目的都是以編程為目的的,而不是以用戶的信息為中心的,總想把用戶的信息納入到某個用戶不感興趣的“程序對象”中。

            二、OOP的優缺點

            OOP的優點:

            使人們的編程與實際的世界更加接近,所有的對象被賦予屬性和方法,結果編程就更加富有人性化。

            OOP的缺點:

            就C++而言,由于面向更高的邏輯抽象層,使得C++在實現的時候,不得不做出性能上面的犧牲,有時候甚至是致命的(所有對象的屬性都經過內置多重指針的間接引用是其性能損失的主要原因之一;不過,筆者的局限性在于未使用過VC++外的面向對象語言,所以不是十分肯定,哈哈,有人笑出來了…)。

            在計算機速度飛速發展的今天,你可能會說,一丁點的性能犧牲沒什么大不了。是的,從面向對象的角度,使的編程的結構更加清晰完整,數據更加獨立和易于管理,性能的犧牲可以帶來這么多的好處,沒有理由不做穩賺的生意吧?

            不過,在某些對速度要求極高特殊場合,例如你做的是電信的交換系統,每秒鐘有超過百萬的人同時進行電話交換,如果,每一個數據交換過程都是一個對象,那么總的性能損失將是天文數字?。?/p>

            或者這個例子不夠貼身,再舉個例子吧。假如你受聘于一個游戲設計公司,老板希望做出來的游戲可以更多的兼顧到更多的電腦使用者,游戲每秒鐘的運行的幀可以更多,子彈和爆炸物可以更多、更華麗。那么,你會發現使用C++會使你的程序變得笨拙,無法滿足你的需求,除非你非得要你的游戲運行于奔騰四的機器上(如果不是,而你又堅持用C++的對象編程,那么請減少主角的槍的威力吧)。

            如果你是冥頑不寧的人,你說不相信OOP會有性能上的損失,那么,我記得曾看到在CSDN上關于VB和VC執行效率的討論的文章,講述的就是使用了MFC以后,執行效率甚至低于VB開發出來的東西。請各位驗證一下:如果使用的是純粹的C語言語法的話,那么一定會比在VB編出來的東西要快很多(GetTickCount函數可以查閱MSDN,如果想更加精確一些,可以使用QueryPerformanceCounter函數)。

          posted @ 2012-02-02 10:07 順其自然EVO 閱讀(149) | 評論 (0)編輯 收藏

          禮物

          http://detail.tmall.com/item.htm?spm=1.141955.216960.43&id=2224656927&prt=1327995052693&prc=3

          posted @ 2012-01-31 15:32 順其自然EVO 閱讀(159) | 評論 (0)編輯 收藏

          編碼準則與Java編譯器

            我們在寫代碼時,常常會提到兩條原則:

            1、方法要盡量短,大方法要分解成小方法;

            2、不要重復發明輪子。

            我們在強調這兩個原則的時候,往往只關注的是代碼簡潔、易維護等方便我們人的因素,其實這樣做還可以大大方便java編譯器優化代碼。

            Java編譯器優化簡介

            Java應用程序的編譯過程與靜態編譯語言(例如C或C++)不同。靜態編譯器直接把源代碼轉換成可以直接在目標平臺上執行的機器代碼,不同的硬件平臺要求不同的編譯器。Java編譯器把Java源代碼轉換成可移植的JVM字節碼。與靜態編譯器不同,javac幾乎不做什么優化,在靜態編譯語言中應當由編譯器進行的優化工作,在Java中是在程序執行的時候,由運行時執行優化。

            即時編譯

            對于證實概念的實現來說,解釋是合適的,但是早期的JVM由于太慢。下一代JVM使用即時(JIT)編譯器來提高執行速度。按照嚴格的定義,基于JIT的虛擬機在執行之前,把所有字節碼轉換成機器碼,但是以惰性方式來做這項工作:JIT只有在確定某個代碼路徑將要執行的時候,才編譯這個代碼路徑(因此有了名稱“即時編譯”)。這個技術使程序能啟動得更快,因為在開始執行之前,不需要冗長的編譯階段。

            JIT技術看起來很有前途,但是它有一些不足。JIT消除了解釋的負擔(以額外的啟動成本為代價),但是由于若干原因,代碼的優化等級仍然是一般般。為了避免Java應用程序嚴重的啟動延遲,JIT編譯器必須非常迅速,這意味著它無法把大量時間花在優化上。所以,早期的JIT編譯器在進行內聯假設(inliningassumption)方面比較保守,因為它們不知道后面可能要裝入哪個類。

            雖然從技術上講,基于JIT的虛擬機在執行字節碼之前,要先編譯字節碼,但是JIT這個術語通常被用來表示任何把字節碼轉換成機器碼的動態編譯過程--即使那些能夠解釋字節碼的過程也算。

            HotSpot動態編譯

            HotSpot執行過程組合了編譯、性能分析以及動態編譯。它沒有把所有要執行的字節碼轉換成機器碼,而是先以解釋器的方式運行,只編譯“熱門”代碼--執行得最頻繁的代碼。當HotSpot執行時,會搜集性能分析數據,用來決定哪個代碼段執行得足夠頻繁,值得編譯。只編譯執行最頻繁的代碼有幾項性能優勢:沒有把時間浪費在編譯那些不經常執行的代碼上;這樣,編譯器就可以花更多時間來優化熱門代碼路徑,因為它知道在這上面花的時間物有所值。而且,通過延遲編譯,編譯器可以訪問性能分析數據,并用這些數據來改進優化決策,例如是否需要內聯某個方法調用。為了讓事情變得更復雜,HotSpot提供了兩個編譯器:客戶機編譯器和服務器編譯器。默認采用客戶機編譯器;在啟動JVM時,您可以指定-server開關,選擇服務器編譯器。服務器編譯器針對最大峰值操作速度進行了優化,適用于需要長期運行的服務器應用程序??蛻魴C編譯器的優化目標,是減少應用程序的啟動時間和內存消耗,優化的復雜程度遠遠低于服務器編譯器,因此需要的編譯時間也更少。

            HotSpot服務器編譯器能夠執行各種樣的類。它能夠執行許多靜態編譯器中常見的標準優化,例如代碼提升(hoisting)、公共的子表達式清除、循環展開(unrolling)、范圍檢測清除、死代碼清除、數據流分析,還有各種在靜態編譯語言中不實用的優化技術,例如虛方法調用的聚合內聯。

            持續重新編譯

            HotSpot技術另一個有趣的方面是:編譯不是一個全有或者全無(all-or-nothing)的命題。在解釋代碼路徑一定次數之后,會把它重新編譯成機器碼。但是JVM會繼續進行性能分析,而且如果認為代碼路徑特別熱門,或者未來的性能分析數據認為存在額外的優化可能,那么還有可能用更高一級的優化重新編譯代碼。JVM在一個應用程序的執行過程中,可能會把相同的字節碼重新編譯許多次。為了深入了解編譯器做了什么,可以-XX:+PrintCompilation標志調用JVM,這個標志會使編譯器(客戶機或服務器)每次運行的時候打印一條短消息。

            棧上(On-stack)替換

            HotSpot開始的版本編譯的時候每次編譯一個方法。如果某個方法的累計執行次數超過指定的循環迭代次數(在HotSpot的第一版中,是10,000次),那么這個方法就被當作熱門方法,計算的方式是:為每個方法關聯一個計數器,每次執行一個后向分支時,就會遞增計數器一次。但是,在方法編譯之后,方法調用并沒有切換到編譯的版本,需要退出并重新進入方法,后續調用才會使用編譯的版本。結果就是,在某些情況下,可能永遠不會用到編譯的版本,例如對于計算密集型程序,在這類程序中所有的計算都是在方法的一次調用中完成的。重量級方法可能被編譯,但是編譯的代碼永遠用不到。

            HotSpot最近的版本采用了稱為棧上(on-stack)替換(OSR)的技術,支持在循環過程中間,從解釋執行切換到編譯的代碼(或者從編譯代碼的一個版本切換到另一個版本)。

            從java編譯、執行優化的原理可以看出,編譯器會將“熱門代碼塊”、“熱門方法”持續優化,以提高性能,再回顧我們常常強調的兩個原則:

            1、盡量寫小方法。小方法意味著功能單一、重用性高,自然會被很多地方用到,容易變成“熱門方法”.

            2、不重復發明輪子,盡量用已存在的輪子。大家共用一個“輪子”,自然就是“熱門”輪子,編譯器會知道這個輪子要好好優化,讓他賺的更快。

          posted @ 2012-01-31 13:53 順其自然EVO 閱讀(189) | 評論 (0)編輯 收藏

          測試遞歸與循環的執行效率與系統開銷

           測試遞歸與循環(這里用for)的執行效率與系統開銷,首先貼出實例問題:實現Fibonacci數列F(n)=F(n-1)+ F(n-2)

            測試環境 Eclipse

            1、首先我們用遞歸來實現

          1. package com.youfeng.test;   
          2. public class Fab {//遞歸  
          3. public static void main(String [] args){   
          4.     System.out.println(F(40));   
          5. }   
          6. public static Long F(int index){   
          7.     if(index==1||index==2){   
          8.         return 1L;  
          9.     }  
          10.     else{  
          11.         return F(index-1)+F(index-2);  
          12.     }  
          13. }  
          14. }

            2、用for循環實現

          1. package com.youfeng.test;  
          2. public class Fab2 {//循環 
          3.     public static void main(String [] args){  
          4.         System.out.println(F(40));  
          5.     }  
          6.     public static Long F(int index){  
          7.         if(index==1||index==2){  
          8.             return 1L;  
          9.         }  
          10.         else{  
          11.             Long f1=1L;  
          12.             Long f2=1L;  
          13.             Long f=0L;  
          14.             for(int i=0;i<index;i++){  
          15.                 f1=f2;  
          16.                 f2=f;  
          17.                 f=f1+f2;  
          18.             }  
          19.             return f;  
          20.         }  
          21.     }  
          22. }

            當index的值很小的時候,我們分別執行沒什么區別,執行速度我們感覺不到什么差別,但是當你把index調到足夠大時100 、200、300、1000…… for循環輕松搞定執行速度挺快。

            當使用遞歸時,你會發現明顯的卡機 卡機 ,有木有?調用系統資源管理器看看你的系統開銷吧(很可能你你打不開資源管理器,因為你卡機了)。

            總結:能不使用遞歸,盡量不要使用,盡量使用循環,效率蠻高的;

            一家之言,歡迎拍磚,實驗簡陋,很多因素沒有考慮進來,請高手指點。

          posted @ 2012-01-29 17:39 順其自然EVO 閱讀(164) | 評論 (0)編輯 收藏

          僅列出標題
          共394頁: First 上一頁 342 343 344 345 346 347 348 349 350 下一頁 Last 
          <2025年7月>
          293012345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          導航

          統計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 昌江| 通道| 绿春县| 健康| 柯坪县| 来宾市| 温宿县| 尼勒克县| 理塘县| 交城县| 合川市| 红原县| 夏河县| 大港区| 海原县| 南部县| 曲周县| 读书| 丰宁| 金沙县| 通江县| 涡阳县| 平邑县| 衡水市| 安顺市| 临安市| 策勒县| 云梦县| 松桃| 襄城县| 永胜县| 丰县| 宁明县| 辛集市| 邳州市| 漯河市| 防城港市| 长顺县| 宝鸡市| 贵南县| 伊宁市|