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

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

改進(jìn)對(duì)象管理方式 提高程序性能

2019-11-18 12:12:35
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

  一、java的內(nèi)存治理
  對(duì)象是功能強(qiáng)大的軟件構(gòu)造模塊,在 Java中它們有著極其廣泛的應(yīng)用。實(shí)際上,由于對(duì)象的應(yīng)用是如此廣泛,開(kāi)發(fā)者有時(shí)忘記了創(chuàng)建對(duì)象所要付出的代價(jià),結(jié)果就導(dǎo)致程序進(jìn)入了“對(duì)象攪拌器”(Object Churn)狀態(tài)。在這種狀態(tài)下,處理器的大部分時(shí)間消耗在周而復(fù)始的創(chuàng)建對(duì)象和回收被廢棄對(duì)象的操作中。
  對(duì)于熟悉C/C++的開(kāi)發(fā)者來(lái)說(shuō),內(nèi)存治理方面的簡(jiǎn)化是Java重要的特點(diǎn)。與C/C++要求程序顯式地分配和釋放內(nèi)存相反,Java答應(yīng)開(kāi)發(fā)者根據(jù)需要為對(duì)象分配空間,并確保當(dāng)程序不再需要對(duì)象時(shí),對(duì)象占用的空間會(huì)被JVM回收。這些工作都在后臺(tái)的垃圾回收進(jìn)程中進(jìn)行。
  在編程語(yǔ)言中,用垃圾收集機(jī)制來(lái)治理內(nèi)存可以回溯到計(jì)算機(jī)剛剛誕生的二十世紀(jì)六十年代。無(wú)論具體情形如何,垃圾收集的基本原理都是一樣的:先標(biāo)識(shí)出那些程序不再使用的對(duì)象,然后回收這些對(duì)象占用的內(nèi)存。
  一般地,JVM利用一種“可到達(dá)性”(Reachability)算法標(biāo)識(shí)正在使用的對(duì)象,然后回收所有其余的對(duì)象。這個(gè)過(guò)程從一組程序直接使用的變量開(kāi)始,包括每一個(gè)活動(dòng)線程的局部變量、方法調(diào)用堆棧上的參數(shù)變量以及已裝入類的靜態(tài)變量所引用的對(duì)象。所有上述幾類變量所引用的對(duì)象都被加入到“可到達(dá)”對(duì)象集合。接著,這些對(duì)象的成員變量所引用的每一個(gè)對(duì)象也被加入到可到達(dá)對(duì)象集合。這個(gè)過(guò)程重復(fù)進(jìn)行直至結(jié)束;結(jié)束時(shí),可到達(dá)對(duì)象集合中的任意對(duì)象所引用的每一個(gè)對(duì)象都已經(jīng)在可到達(dá)集合中。所有不在可到達(dá)對(duì)象集合中的對(duì)象都被認(rèn)為已經(jīng)廢棄不用,也就是可以安全地回收。
  通常,Java的垃圾收集過(guò)程無(wú)需開(kāi)發(fā)者干預(yù)。JVM周期性地運(yùn)行垃圾收集過(guò)程,或者是當(dāng)程序的線程因?yàn)榈却獠渴录源饝?yīng)垃圾收集過(guò)程運(yùn)行;或者是當(dāng)程序創(chuàng)建新對(duì)象時(shí)內(nèi)存不足,因而必須運(yùn)行垃圾收集過(guò)程。盡管垃圾收集是自動(dòng)進(jìn)行的,但了解這一過(guò)程是很重要的,因?yàn)槔占瘜⒄加煤艽笠徊糠諮ava程序的開(kāi)銷。
  除了垃圾收集的時(shí)間開(kāi)銷之外,Java中的對(duì)象還會(huì)產(chǎn)生大量的空間開(kāi)銷。對(duì)于每一個(gè)已分配的對(duì)象,JVM將加上必要的內(nèi)部信息以便垃圾收集過(guò)程順利進(jìn)行。另外,JVM還要加入一些Java語(yǔ)言規(guī)范所要求的信息,這些信息對(duì)于某些功能來(lái)說(shuō)是必不可少的,比如在任意對(duì)象上同步的功能。假如把JVM內(nèi)部為每一個(gè)對(duì)象分配的空間計(jì)入對(duì)象的占用空間,則小型Java對(duì)象要比C/C++中等價(jià)的對(duì)象大得多。下面的表格顯示了幾種不同JVM下一些簡(jiǎn)單對(duì)象的內(nèi)存占用情況,其中“內(nèi)容”列顯示用戶可訪問(wèn)內(nèi)容的大小,其余各列顯示不同JVM下對(duì)象實(shí)際占用的內(nèi)存大小,從該表可以看出JVM額外增加的內(nèi)存開(kāi)銷。
  改進(jìn)對(duì)象治理方式 提高程序性能
  上面的空間開(kāi)銷是單個(gè)對(duì)象的值,因此,對(duì)于大對(duì)象來(lái)說(shuō)空間開(kāi)銷的百分比將下降。假如程序使用了大量的小型對(duì)象,可能使性能變得很糟糕。
  
  二、盡量使用基本數(shù)據(jù)類型
  
  在Java程序中,減少對(duì)象創(chuàng)建操作最簡(jiǎn)單的策略或許就是盡可能地使用基本數(shù)據(jù)類型。然而,這個(gè)策略可適用的場(chǎng)合并不是很多,許多時(shí)候?qū)ο笥谐渥愕睦碛沙蔀槭走x的數(shù)據(jù)格式,簡(jiǎn)單地用基本數(shù)據(jù)類型來(lái)替換對(duì)象不能滿足設(shè)計(jì)要求。但是,在確實(shí)適用該策略的場(chǎng)合,它可以減少大量的開(kāi)銷。
  Java的基本數(shù)據(jù)類型包括boolean、byte、char、double、float、int、long和short。使用基本數(shù)據(jù)類型的變量不會(huì)產(chǎn)生創(chuàng)建對(duì)象的開(kāi)銷,用完之后也不需要進(jìn)行垃圾收集。對(duì)于局部方法變量,JVM將直接在堆棧分配變量空間;對(duì)于成員變量,JVM在對(duì)象使用的空間為變量分配空間。
  Java為每一種基本數(shù)據(jù)類型定義了相應(yīng)的封裝類。封裝類代表的是和基本數(shù)據(jù)類型值對(duì)應(yīng)但不可變的值,使得基本數(shù)據(jù)類型的值可以作為對(duì)象處理;使用java.util.Vector、java.util.Stack、java.util.Hashtable等工具類時(shí),這種對(duì)象非常有用。但除了這些非凡情況之外,應(yīng)當(dāng)避免使用封裝類,盡量使用基本數(shù)據(jù)類型,避免創(chuàng)建對(duì)象所需要的內(nèi)存和時(shí)間開(kāi)銷。
  
  除了標(biāo)準(zhǔn)的封裝類值外,Java類庫(kù)中還有一些類是在基本類型的基礎(chǔ)上加上一層新的語(yǔ)義和行為,java.util.Date和java.awt.Point都屬于這類例子。在大量應(yīng)用這類值的場(chǎng)合,存儲(chǔ)和傳遞對(duì)應(yīng)的基本類型值(只在必要的時(shí)候才把它們轉(zhuǎn)換成相應(yīng)的對(duì)象)能夠減少不必要的對(duì)象創(chuàng)建操作。例如,對(duì)于Point類,我們可以直接訪問(wèn)Point內(nèi)部的int值,或者把它們組合成一個(gè)long值使得方法調(diào)用只需返回一個(gè)基本類型的值。下面是一個(gè)計(jì)算中點(diǎn)的例子:
  ...
  // 用long值表示Point的例子。
   // 每一個(gè)long值的高位包含x坐標(biāo),
   // 低位包含y坐標(biāo)
   public long midpoint(long a, long b) {
     // 計(jì)算每一個(gè)坐標(biāo)上的中值
     int x = (int) (((a >> 32) + (b >> 32)) / 2);
     int y = ((int) a + (int) b) / 2;
     // 返回中點(diǎn)
   return (x << 32) + y;
   }
   ... 
  三、專用對(duì)象重用
  減少對(duì)象創(chuàng)建操作的另一途徑是重用對(duì)象。被重用的對(duì)象可能是專門用于某一特定用途,也可能在不同的時(shí)刻用于不同的目的,因此,重用對(duì)象主要包括兩種變化形式:專用對(duì)象重用,具有簡(jiǎn)單方便的特點(diǎn);自由緩沖池重用,具有最好的對(duì)象重用效果。
  最簡(jiǎn)單的對(duì)象重用情況是,一個(gè)頻繁執(zhí)行的任務(wù)需要一個(gè)或者多個(gè)起輔助作用的對(duì)象。許多應(yīng)用經(jīng)常進(jìn)行日期格式化操作,下面我們就以此為例討論專用對(duì)象重用。要從一個(gè)指定的日期值(按照前面盡量使用基本數(shù)據(jù)類型的要求,這個(gè)值是long類型)生成默認(rèn)的字符串表示形式,我們可以:
   ...
  // 生成時(shí)間的默認(rèn)字符串表示形式
  long time = ...;
  String display = DateFormat.getDateInstance().format(new Date(time));
  ... 
  這個(gè)語(yǔ)句看起來(lái)很簡(jiǎn)單,實(shí)際上卻進(jìn)行了大量復(fù)雜的對(duì)象創(chuàng)建操作。DateFormat.getDateInstance()調(diào)用創(chuàng)建了一個(gè)新的SimpleDateFormat實(shí)例,后者又要?jiǎng)?chuàng)建一系列相關(guān)的對(duì)象;然后,format調(diào)用又要?jiǎng)?chuàng)建新的StringBuffer和FieldPosition對(duì)象。在JRE 1.2.2和Windows 98下,這個(gè)簡(jiǎn)單的語(yǔ)句實(shí)際分配的內(nèi)存多達(dá)2400字節(jié)。假如這個(gè)語(yǔ)句需要頻繁地執(zhí)行,臨時(shí)生成和丟棄的對(duì)象數(shù)量是相當(dāng)可觀的。
  3.1 私有的對(duì)象
  改進(jìn)的方法是預(yù)先(一次性地)創(chuàng)建進(jìn)行格式化所需要的對(duì)象,這組對(duì)象由使用它們的代碼所擁有(專用),然后在需要時(shí)重用這些對(duì)象。例如,假如我們通過(guò)實(shí)例變量實(shí)現(xiàn)該方案,使得容器類的每一個(gè)實(shí)例擁有這些對(duì)象的唯一一份拷貝,修改后的代碼如下:...
  // 以成員變量的形式分配專用的日期格式化對(duì)象
   PRivate final Date convertDate = new Date();
   private final DateFormat convertFormat = DateFormat.getDateInstance();
   private final StringBuffer convertBuffer = new StringBuffer();
   private final FieldPosition convertField = new FieldPosition(0);
   ...
   // 生成指定日期的默認(rèn)字符串表示形式
     long time = ...;
  convertDate.setTime(time);
   convertBuffer.setLength(0);
     StringBuffer output =
       dateFormatter.format(convertDate, convertBuffer, convertField);
     String display = output.toString();
     ... 
  這個(gè)代碼片斷顯然要比原先的代碼長(zhǎng),但在每次執(zhí)行時(shí)只需創(chuàng)建一個(gè)輸出的字符串對(duì)象,因而速度要快得多。簡(jiǎn)單的測(cè)試表明,改進(jìn)后的代碼100000次迭代只需8秒,而原來(lái)代碼的相應(yīng)時(shí)間則為50秒。由于用來(lái)格式化的對(duì)象并未在一次使用后馬上釋放,所以改進(jìn)后的代碼占用了更多的內(nèi)存(或占用時(shí)間更長(zhǎng)),但假如代碼執(zhí)行非常頻繁,這個(gè)代價(jià)仍是非常合算的。
  值得指出的是,假如在一個(gè)執(zhí)行大量迭代的方法之內(nèi)有一個(gè)內(nèi)部循環(huán),上面討論的技術(shù)同樣有用。我們不一定要把循環(huán)內(nèi)用到的對(duì)象轉(zhuǎn)移到包含該方法的類之內(nèi),而是可以把這些對(duì)象的創(chuàng)建操作移到循環(huán)之外,使得創(chuàng)建操作只執(zhí)行一次。按照這種思想,代碼可以為:
   // 分配循環(huán)內(nèi)要用到的對(duì)象
   Date date = new Date();
     DateFormat formatter = DateFormat.getDateInstance();
     StringBuffer buffer= new StringBuffer();
     FieldPosition field = new FieldPosition(0);
     // 執(zhí)行循環(huán)
     for (...) {
       // 生成指定時(shí)間的字符串表示形式
       long time = ...;
       date.setTime(time);
       buffer.setLength(0);
       StringBuffer output = formatter.format(date, buffer, field);
       String display = output.toString();
     } 
  結(jié)合前面介紹的用基本類型值替代相應(yīng)對(duì)象類型值技術(shù),這種專用對(duì)象重用技術(shù)的效果更好。專用對(duì)象可以從基本類型值實(shí)例化然后傳遞給Java標(biāo)準(zhǔn)類庫(kù)中要求對(duì)象作為參數(shù)類型的方法。上面的專用Date對(duì)象就是一個(gè)很好的例子。
  3.2 多個(gè)線程公用的私有對(duì)象
  假設(shè)我們有一組私有的對(duì)象,有多個(gè)線程并發(fā)地執(zhí)行使用這些對(duì)象的代碼,因此必須避免不同的線程操作這些對(duì)象可能引起的沖突。實(shí)現(xiàn)這個(gè)目標(biāo)最簡(jiǎn)單的方法是指定其中一個(gè)對(duì)象作為整組對(duì)象的鎖,把使用這些對(duì)象的代碼封裝在一個(gè)在鎖對(duì)象上同步的塊中。盡管每次使用私有對(duì)象時(shí)都會(huì)增加加鎖操作的開(kāi)銷,但和創(chuàng)建對(duì)象的時(shí)間相比,加鎖操作的開(kāi)銷較小。
  假定以convertDate對(duì)象作為鎖,則使用這些對(duì)象的代碼應(yīng)該改為:     // 獲得私有對(duì)象的使用

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 黄平县| 志丹县| 荆州市| 田阳县| 长葛市| 浑源县| 金湖县| 新沂市| 石家庄市| 抚州市| 民丰县| 兴和县| 梨树县| 通城县| 榆中县| 澳门| 全椒县| 五大连池市| 鞍山市| 新河县| 墨竹工卡县| 牟定县| 定结县| 宁津县| 左云县| 中牟县| 平泉县| 眉山市| 银川市| 新巴尔虎右旗| 乌兰察布市| 遂川县| 大新县| 汝城县| 泸溪县| 山西省| 四平市| 武义县| 平舆县| 东光县| 乌兰县|