隨筆-17  評論-0  文章-4  trackbacks-0
            2008年12月6日
          運(yùn)行看結(jié)果,慢慢了解
          import java.lang.ref.ReferenceQueue;
          import java.lang.ref.SoftReference;
          import java.lang.ref.WeakReference;


          public class Testone {
          public static void main(String args[]){
          A a=new A();
          //a.test();
          //SoftReference sr = new SoftReference(a);
          ReferenceQueue<A> rq = new ReferenceQueue<A>();
          WeakReference<A> wr = new WeakReference<A>(a, rq);
          a = null;
          System.out.println(wr.get());
          System.out.println(rq.poll());
          System.gc();
          System.runFinalization();
          System.out.println(wr.get());
          System.out.println(rq.poll());
          if (wr != null) {
          a = (A)wr.get();
          System.out.println("asdasdas");
          a.test();
          }
          else{
          a = new A();
          System.out.println("123123");
          a.test();
          a = null;
          wr = new WeakReference<A>(a);
          }

          }
          }
          class A{
          void test(){
          System.out.println("A.test()");
          }
          }
          posted @ 2008-12-06 21:14 竹子 閱讀(457) | 評論 (0)編輯 收藏

          垃圾回收與強(qiáng)引用,軟引用,弱引用,幻引用的關(guān)系(一)

          Java 2 平臺引入了 java.lang.ref 包,其中包括的類可以讓您引用對象,而不將它們留在內(nèi)存中。這些類還提供了與垃圾收集器(garbage collector)之間有限的交互。Peter Haggar 在本文中分析了 SoftReference、WeakReference 和 PhantomReference 類的功能和行為,并就這些類的使用給出了一些編程風(fēng)格上的建議。
          當(dāng)在 Java 2 平臺中首次引入 java.lang.ref 包(其中包含 SoftReference、WeakReference 和 PhantomReference 類)時,它的實(shí)用性顯然被過分夸大了。它包含的類可能是有用的,但這些類具有的某些局限性會使它們顯得不是很有吸引力,而且其應(yīng)用程序也將特別局限于解決一類特定的問題。

          垃圾收集概述
          引用類的主要功能就是能夠引用仍可以被垃圾收集器回收的對象。在引入引用類之前,我們只能使用強(qiáng)引用(strong reference)。舉例來說,下面一行代碼顯示的就是強(qiáng)引用 obj:


          Object obj = new Object();




          obj 這個引用將引用堆中存儲的一個對象。只要 obj 引用還存在,垃圾收集器就永遠(yuǎn)不會釋放用來容納該對象的存儲空間。

          當(dāng) obj 超出范圍或被顯式地指定為 null 時,垃圾收集器就認(rèn)為沒有對這個對象的其它引用,也就可以收集它了。然而您還需要注意一個重要的細(xì)節(jié):僅憑對象可以被收集并不意味著垃圾收集器的一次指定運(yùn)行就能夠回收它。由于各種垃圾收集算法有所不同,某些算法會更頻繁地分析生存期較短的對象,而不是較老、生存期較長的對象。因此,一個可供收集的對象可能永遠(yuǎn)也不會被回收。如果程序在垃圾收集器釋放對象之前結(jié)束,這種情況就可能會出現(xiàn)。因此,概括地說,您永遠(yuǎn)無法保證可供收集的對象總是會被垃圾收集器收集。

          這些信息對于您分析引用類是很重要的。由于垃圾收集有著特定的性質(zhì),所以引用類實(shí)際上可能沒有您原來想像的那么有用,盡管如此,它們對于特定問題來說還是很有用的類。軟引用(soft reference)、弱引用(weak reference)和虛引用(phantom reference)對象提供了三種不同的方式來在不妨礙收集的情況下引用堆對象。每種引用對象都有不同的行為,而且它們與垃圾收集器之間的交互也有所不同。此外,這幾個新的引用類都表現(xiàn)出比典型的強(qiáng)引用“更弱”的引用形式。而且,內(nèi)存中的一個對象可以被多個引用(可以是強(qiáng)引用、軟引用、弱引用或虛引用)引用。在進(jìn)一步往下討論之前,讓我們來看看一些術(shù)語:

          強(qiáng)可及對象(strongly reachable):可以通過強(qiáng)引用訪問的對象。


          軟可及對象(softly reachable):不是強(qiáng)可及對象,并且能夠通過軟引用訪問的對象。


          弱可及對象(weakly reachable):不是強(qiáng)可及對象也不是軟可及對象,并且能夠通過弱引用訪問的對象。


          虛可及對象(phantomly reachable):不是強(qiáng)可及對象、軟可及對象,也不是弱可及對象,已經(jīng)結(jié)束的,可以通過虛引用訪問的對象。


          清除:將引用對象的 referent 域設(shè)置為 null,并將引用類在堆中引用的對象聲明為可結(jié)束的。
          SoftReference 類
          SoftReference 類的一個典型用途就是用于內(nèi)存敏感的高速緩存。SoftReference 的原理是:在保持對對象的引用時保證在 JVM 報告內(nèi)存不足情況之前將清除所有的軟引用。關(guān)鍵之處在于,垃圾收集器在運(yùn)行時可能會(也可能不會)釋放軟可及對象。對象是否被釋放取決于垃圾收集器的算法以及垃圾收集器運(yùn)行時可用的內(nèi)存數(shù)量。 

          WeakReference 類
          WeakReference 類的一個典型用途就是規(guī)范化映射(canonicalized mapping)。另外,對于那些生存期相對較長而且重新創(chuàng)建的開銷也不高的對象來說,弱引用也比較有用。關(guān)鍵之處在于,垃圾收集器運(yùn)行時如果碰到了弱可及對象,將釋放 WeakReference 引用的對象。然而,請注意,垃圾收集器可能要運(yùn)行多次才能找到并釋放弱可及對象。

          PhantomReference 類
          PhantomReference 類只能用于跟蹤對被引用對象即將進(jìn)行的收集。同樣,它還能用于執(zhí)行 pre-mortem 清除操作。PhantomReference 必須與 ReferenceQueue 類一起使用。需要 ReferenceQueue 是因?yàn)樗軌虺洚?dāng)通知機(jī)制。當(dāng)垃圾收集器確定了某個對象是虛可及對象時,PhantomReference 對象就被放在它的 ReferenceQueue 上。將 PhantomReference 對象放在 ReferenceQueue 上也就是一個通知,表明 PhantomReference 對象引用的對象已經(jīng)結(jié)束,可供收集了。這使您能夠剛好在對象占用的內(nèi)存被回收之前采取行動。  

          垃圾收集器和引用交互
          垃圾收集器每次運(yùn)行時都可以隨意地釋放不再是強(qiáng)可及的對象占用的內(nèi)存。如果垃圾收集器發(fā)現(xiàn)了軟可及對象,就會出現(xiàn)下列情況:

          SoftReference 對象的 referent 域被設(shè)置為 null,從而使該對象不再引用 heap 對象。


          SoftReference 引用過的 heap 對象被聲明為 finalizable。


          當(dāng) heap 對象的 finalize() 方法被運(yùn)行而且該對象占用的內(nèi)存被釋放,SoftReference 對象就被添加到它的 ReferenceQueue(如果后者存在的話)。
          如果垃圾收集器發(fā)現(xiàn)了弱可及對象,就會出現(xiàn)下列情況:

          WeakReference 對象的 referent 域被設(shè)置為 null,從而使該對象不再引用 heap 對象。


          WeakReference 引用過的 heap 對象被聲明為 finalizable。


          當(dāng) heap 對象的 finalize() 方法被運(yùn)行而且該對象占用的內(nèi)存被釋放時,WeakReference 對象就被添加到它的 ReferenceQueue(如果后者存在的話)。
          如果垃圾收集器發(fā)現(xiàn)了虛可及對象,就會出現(xiàn)下列情況:

          PhantomReference 引用過的 heap 對象被聲明為 finalizable。


          與軟引用和弱引用有所不同,PhantomReference 在堆對象被釋放之前就被添加到它的 ReferenceQueue。(請記住,所有的 PhantomReference 對象都必須用經(jīng)過關(guān)聯(lián)的 ReferenceQueue 來創(chuàng)建。)這使您能夠在堆對象被回收之前采取行動。
          請考慮清單 1 中的代碼。

          清單 1. 使用 WeakReference 及 ReferenceQueue 的示例代碼
          //Create a strong reference to an object
          MyObject obj = new MyObject(); //1

          //Create a reference queue
          ReferenceQueue rq = new ReferenceQueue(); //2

          //Create a weakReference to obj and associate our reference queue
          WeakReference wr = new WeakReference(obj, rq); //3



          行 //1 創(chuàng)建 MyObject 對象,而行 //2 則創(chuàng)建 ReferenceQueue 對象。行 //3 創(chuàng)建引用其引用對象 MyObject 的 WeakReference 對象,還創(chuàng)建它的 ReferenceQueue。請注意,每個對象引用(obj、rq 及 wr)都是強(qiáng)引用。要利用這些引用類,您必須取消對 MyObject 對象的強(qiáng)引用,方法是將 obj 設(shè)置為 null。前面說過,如果不這樣做,對象 MyObject 永遠(yuǎn)都不會被回收,引用類的任何優(yōu)點(diǎn)都會被削弱。

          每個引用類都有一個 get() 方法,而 ReferenceQueue 類有一個 poll() 方法。get() 方法返回對被引用對象的引用。在 PhantomReference 上調(diào)用 get() 總是會返回 null。這是因?yàn)?nbsp;PhantomReference 只用于跟蹤收集。poll() 方法返回已被添加到隊(duì)列中的引用對象,如果隊(duì)列中沒有任何對象,它就返回 null。因此,執(zhí)行清單 1 之后再調(diào)用 get() 和 poll() 的結(jié)果可能是:


          wr.get(); //returns reference to MyObject
          rq.poll(); //returns null




          現(xiàn)在我們假定垃圾收集器開始運(yùn)行。由于 MyObject 對象沒有被釋放,所以 get() 和 poll() 方法將返回同樣的值;obj 仍然保持對該對象進(jìn)行強(qiáng)引用。實(shí)際上,對象布局還是沒有改變,和圖 1 所示的差不多。然而,請考慮下面的代碼:


          obj = null;
          System.gc(); //run the collector







          現(xiàn)在,調(diào)用 get() 和 poll() 將產(chǎn)生與前面不同的結(jié)果:


          wr.get(); //returns null
          rq.poll(); //returns a reference to the WeakReference object




          這種情況表明,MyObject 對象(對它的引用原來是由 WeakReference 對象進(jìn)行的)不再可用。這意味著垃圾收集器釋放了 MyObject 占用的內(nèi)存,從而使 WeakReference 對象可以被放在它的 ReferenceQueue 上。這樣,您就可以知道當(dāng) WeakReference 或 SoftReference 類的 get() 方法返回 null 時,就有一個對象被聲明為 finalizable,而且可能(不過不一定)被收集。只有當(dāng) heap 對象完全結(jié)束而且其內(nèi)存被回收后,WeakReference 或 SoftReference 才會被放到與其關(guān)聯(lián)的 ReferenceQueue 上。清單 2 顯示了一個完整的可運(yùn)行程序,它展示了這些原理中的一部分。這段代碼本身就頗具說明性,它含有很多注釋和打印語句,可以幫助您理解。

          清單 2. 展示引用類原理的完整程序
          import java.lang.ref.*;
          class MyObject
          {
          protected void finalize() throws Throwable
          {
          System.out.println("In finalize method for this object: " +
          this);
          }
          }

          class ReferenceUsage
          {
          public static void main(String args[])
          {
          hold();
          release();
          }

          public static void hold()
          {
          System.out.println("Example of incorrectly holding a strong " +
          "reference");
          //Create an object
          MyObject obj = new MyObject();
          System.out.println("object is " + obj);

          //Create a reference queue
          ReferenceQueue rq = new ReferenceQueue();

          //Create a weakReference to obj and associate our reference queue
          WeakReference wr = new WeakReference(obj, rq);

          System.out.println("The weak reference is " + wr);

          //Check to see if it´s on the ref queue yet
          System.out.println("Polling the reference queue returns " +
          rq.poll());
          System.out.println("Getting the referent from the " +
          "weak reference returns " + wr.get());

          System.out.println("Calling GC");
          System.gc();
          System.out.println("Polling the reference queue returns " +
          rq.poll());
          System.out.println("Getting the referent from the " +
          "weak reference returns " + wr.get());
          }

          public static void release()
          {
          System.out.println("");
          System.out.println("Example of correctly releasing a strong " +
          "reference");
          //Create an object
          MyObject obj = new MyObject();
          System.out.println("object is " + obj);

          //Create a reference queue
          ReferenceQueue rq = new ReferenceQueue();

          //Create a weakReference to obj and associate our reference queue
          WeakReference wr = new WeakReference(obj, rq);

          System.out.println("The weak reference is " + wr);

          //Check to see if it´s on the ref queue yet
          System.out.println("Polling the reference queue returns " +
          rq.poll());
          System.out.println("Getting the referent from the " +
          "weak reference returns " + wr.get());

          System.out.println("Set the obj reference to null and call GC");
          obj = null;
          System.gc();
          System.out.println("Polling the reference queue returns " +
          rq.poll());
          System.out.println("Getting the referent from the " +
          "weak reference returns " + wr.get());
          }
          }




          用途和風(fēng)格
          這些類背后的原理就是避免在應(yīng)用程序執(zhí)行期間將對象留在內(nèi)存中。相反,您以軟引用、弱引用或虛引用的方式引用對象,這樣垃圾收集器就能夠隨意地釋放對象。當(dāng)您希望盡可能減小應(yīng)用程序在其生命周期中使用的堆內(nèi)存大小時,這種用途就很有好處。您必須記住,要使用這些類,您就不能保留對對象的強(qiáng)引用。如果您這么做了,那就會浪費(fèi)這些類所提供的任何好處。

          另外,您必須使用正確的編程風(fēng)格以檢查收集器在使用對象之前是否已經(jīng)回收了它,如果已經(jīng)回收了,您首先必須重新創(chuàng)建該對象。這個過程可以用不同的編程風(fēng)格來完成。選擇錯誤的風(fēng)格會導(dǎo)致出問題。請考慮清單 3 中從 WeakReference 檢索被引用對象的代碼風(fēng)格:

          清單 3. 檢索被引用對象的風(fēng)格
          obj = wr.get();
          if (obj == null)
          {
          wr = new WeakReference(recreateIt()); //1
          obj = wr.get(); //2
          }
          //code that works with obj




          研究了這段代碼之后,請看看清單 4 中從 WeakReference 檢索被引用對象的另一種代碼風(fēng)格:

          清單 4. 檢索被引用對象的另一種風(fēng)格
          obj = wr.get();
          if (obj == null)
          {
          obj = recreateIt(); //1
          wr = new WeakReference(obj); //2
          }
          //code that works with obj




          請比較這兩種風(fēng)格,看看您能否確定哪種風(fēng)格一定可行,哪一種不一定可行。清單 3 中體現(xiàn)出的風(fēng)格不一定在所有情況下都可行,但清單 4 的風(fēng)格就可以。清單 3 中的風(fēng)格不夠好的原因在于,if 塊的主體結(jié)束之后 obj 不一定是非空值。請考慮一下,如果垃圾收集器在清單 3 的行 //1 之后但在行 //2 執(zhí)行之前運(yùn)行會怎樣。recreateIt() 方法將重新創(chuàng)建該對象,但它會被 WeakReference 引用,而不是強(qiáng)引用。因此,如果收集器在行 //2 在重新創(chuàng)建的對象上施加一個強(qiáng)引用之前運(yùn)行,對象就會丟失,wr.get() 則返回 null。

          清單 4 不會出現(xiàn)這種問題,因?yàn)樾?nbsp;//1 重新創(chuàng)建了對象并為其指定了一個強(qiáng)引用。因此,如果垃圾收集器在該行之后(但在行 //2 之前)運(yùn)行,該對象就不會被回收。然后,行 //2 將創(chuàng)建對 obj 的 WeakReference。在使用這個 if 塊之后的 obj 之后,您應(yīng)該將 obj 設(shè)置為 null,從而讓垃圾收集器能夠回收這個對象以充分利用弱引用。清單 5 顯示了一個完整的程序,它將展示剛才我們描述的風(fēng)格之間的差異。(要運(yùn)行該程序,其運(yùn)行目錄中必須有一個“temp.fil”文件。

          清單 5. 展示正確的和不正確的編程風(fēng)格的完整程序。
          import java.io.*;
          import java.lang.ref.*;

          class ReferenceIdiom
          {
          public static void main(String args[]) throws FileNotFoundException
          {
          broken();
          correct();
          }

          public static FileReader recreateIt() throws FileNotFoundException
          {
          return new FileReader("temp.fil");
          }

          public static void broken() throws FileNotFoundException
          {
          System.out.println("Executing method broken");
          FileReader obj = recreateIt();
          WeakReference wr = new WeakReference(obj);

          System.out.println("wr refers to object " + wr.get());

          System.out.println("Now, clear the reference and run GC");
          //Clear the strong reference, then run GC to collect obj.
          obj = null;
          System.gc();

          System.out.println("wr refers to object " + wr.get());

          //Now see if obj was collected and recreate it if it was.
          obj = (FileReader)wr.get();
          if (obj == null)
          {
          System.out.println("Now, recreate the object and wrap it
          in a WeakReference");
          wr = new WeakReference(recreateIt());
          System.gc(); //FileReader object is NOT pinned...there is no
          //strong reference to it. Therefore, the next
          //line can return null.
          obj = (FileReader)wr.get();
          }
          System.out.println("wr refers to object " + wr.get());
          }

          public static void correct() throws FileNotFoundException
          {
          System.out.println("");
          System.out.println("Executing method correct");
          FileReader obj = recreateIt();
          WeakReference wr = new WeakReference(obj);

          System.out.println("wr refers to object " + wr.get());

          System.out.println("Now, clear the reference and run GC");
          //Clear the strong reference, then run GC to collect obj
          obj = null;
          System.gc();

          System.out.println("wr refers to object " + wr.get());

          //Now see if obj was collected and recreate it if it was.
          obj = (FileReader)wr.get();
          if (obj == null)
          {
          System.out.println("Now, recreate the object and wrap it
          in a WeakReference");
          obj = recreateIt();
          System.gc(); //FileReader is pinned, this will not affect
          //anything.
          wr = new WeakReference(obj);
          }
          System.out.println("wr refers to object " + wr.get());
          }
          }




          總結(jié)
          如果使用得當(dāng),引用類還是很有用的。然而,由于它們所依賴的垃圾收集器行為有時候無法預(yù)知,所以其實(shí)用性就會受到影響。能否有效地使用它們還取決于是否應(yīng)用了正確的編程風(fēng)格;關(guān)鍵在于您要理解這些類是如何實(shí)現(xiàn)的以及如何對它們進(jìn)行編程。
          =================================================================================

          Java 對象的狀態(tài)有:

              * 已創(chuàng)建(created)
              * 強(qiáng)可達(dá)(strong reachable)
              * 不可見(invisible)
              * 不可達(dá)(unreachable)
              * 已收集(collected)
              * 終化(finalized)
              * 已回收(deallocated) 

          Java對象生命周期的狀態(tài)轉(zhuǎn)換: {image:img=objectstatus.jpg|width=400} 引用對象
          三種新的引用類型:

              * 軟引用(soft reference)
              * 弱引用(weak reference)
              * 幻引用(phantom reference) 

          強(qiáng)可達(dá)(Strong Reachable)
          定義: ~An object is strong reachable if it can be reached by some thread without traversing any reference objects. A newly-created object is strong reachable by the thread that created it.~
          處于強(qiáng)可達(dá)狀態(tài)的對象, 在任何情況下都不會被回收掉. 軟可達(dá)(Softly Reachable)
          定義:~An object is softly reachable if it is not strongly reachable but can be reached by traversing a soft reference.~
          含義是:當(dāng)對象不處于強(qiáng)可達(dá)狀態(tài), 并且可以通過軟引用進(jìn)行訪問時, 即處于軟可達(dá)狀態(tài).
          當(dāng)程序申請內(nèi)存的時候, 垃圾收集器會判斷是否開始回收處于軟可達(dá)狀態(tài)的對象, 如果決定回收某個對象, 那么垃圾收集器會清除所有指向該對象的軟引用, 如果任何處于其它軟可達(dá)狀態(tài)的對象可以通過強(qiáng)引用訪問該對象, 那么指向這些對象的軟引用也會被清除掉. 垃圾收集器在決定哪些軟可達(dá)狀態(tài)的對象被收集時, 采用"最久未被使用"原則, 或稱"最不常使用"原則. 垃圾收集器也保證在OutOfMemeryError產(chǎn)生以前, 所有的軟引用都被清除.

              * 產(chǎn)生和使用一個軟引用 

          // createSoftReference sr = new SoftReference(new SomeObject());// getSomeObject o = (SomeObject) sf.get();// create in a reference queue;ReferenceQueue queue = new ReferenceQueue();SoftReference sr = new SoftReference(new SomeObject(), queue);

          弱可達(dá)(Weakly Reachable)
          定義:~An Object is weakly reachable if it is neither strongly nor softly reachable but can be reached by traversing a weak reference.~
          垃圾收集器會一次清除所有弱引用. 幻可達(dá)(Phantomly Reachable)
          定義:~An object is phantomly reachable if it is neither strongly, softly, nor weakly reachable, it has been finalized, and some phantom reference refers to it.~
          幻引用不能直接創(chuàng)建. 必須通過向引用隊(duì)列等級的途徑來創(chuàng)建:

          ReferenceQueue queue = new ReferenceQueue();PhantomReference pr = new PhantomReference (new SomeObject(), queue);

          你不可能從幻引用再次得到對象, pr.get()永遠(yuǎn)返回null. 另外, 必須調(diào)用Reference.clear()手工清除幻引用. All About ReferenceObjects No InterWiki reference defined in properties for Wiki called '[http'!)]
          Reference Objects No InterWiki reference defined in properties for Wiki called '[http'!)]
          Reference Objects and Garbage Collection No InterWiki reference defined in properties for Wiki called '[http'!)]
          \[Jike Thread\?Soft, Weak, and Phantom References|http://www-124.ibm.com/pipermail/jikesrvm-core/2003-May/000365.html]


          Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1492810
          posted @ 2008-12-06 21:13 竹子 閱讀(488) | 評論 (0)編輯 收藏
            2008年12月2日

          T-SQL 編碼標(biāo)準(zhǔn)

          發(fā)布日期: 4/15/2005 | 更新日期: 4/15/2005

          Brian Walker

          可能讓人覺得很奇怪,但好像的確沒有什么正式的”T-SQL 編碼標(biāo)準(zhǔn)。早在 1999 年末的時候,我驚喜地發(fā)現(xiàn) John Hindmarsh 提出的 SQL Server 7.0 標(biāo)準(zhǔn),我在 2000 2 月的社論中對他的某些建議進(jìn)行了總結(jié)。(2000 2 月以及本月的下載中都包括了 John 原來的標(biāo)準(zhǔn)。)后來,Ron Talmage 撰寫了一系列專欄文章,提出了他對各種最佳方法的建議,當(dāng)然,SQL Server 小組也已正式發(fā)布了 SQL Server 最佳方法分析器 (SQLBPA)。現(xiàn)在,一位具有超過 25 年經(jīng)驗(yàn)的數(shù)據(jù)庫管理員和應(yīng)用程序開發(fā)員 Brian Walker 又提出了他的建議和提示。

          *

          進(jìn)行 T-SQL 編程時常常會忽略編碼標(biāo)準(zhǔn),但這些標(biāo)準(zhǔn)卻是開發(fā)小組順利開展工作的關(guān)鍵工具。這里介紹的編碼標(biāo)準(zhǔn)是我多年的開發(fā)成果。它們當(dāng)然還沒有得到普遍接受,而且不可否認(rèn),有些標(biāo)準(zhǔn)帶有主觀色彩。我的目的實(shí)際上更多的是為了提高大家的意識,而不是吹捧自己是 T-SQL 樣式方面的仲裁者:最重要的是要建立某些合理的編碼標(biāo)準(zhǔn)并遵循這些標(biāo)準(zhǔn)。您在這篇文章中會發(fā)現(xiàn)有關(guān) T-SQL 編程的一系列不同的編碼標(biāo)準(zhǔn)、技巧和提示。它們并未以任何特定的優(yōu)先級或重要性順序列出。

          讓我們從格式開始。表面上,T-SQL 代碼的格式似乎并不重要,但一致的格式可以使您的同事(不論是同一小組的成員還是更大范圍的 T-SQL 開發(fā)團(tuán)隊(duì)的成員)更輕松地瀏覽和理解您的代碼。T-SQL 語句有一個結(jié)構(gòu),遵循一目了然的結(jié)構(gòu)使您可以更輕松地查找和確認(rèn)語句的不同部分。統(tǒng)一的格式還使您可以更輕松地在復(fù)雜 T-SQL 語句中增刪代碼段,使調(diào)試工作變得更容易。下面是 SELECT 語句的格式示例:

                 SELECT C.Name
          , E.NameLast
          , E.NameFirst
          , E.Number
          , ISNULL(I.Description,'NA') AS Description
          FROM tblCompany AS C
          JOIN tblEmployee AS E
          ON C.CompanyID = E.CompanyID
          LEFT JOIN tblCoverage AS V
          ON E.EmployeeID = V.EmployeeID
          LEFT JOIN tblInsurance AS I
          ON V.InsuranceID = I.InsuranceID
          WHERE C.Name LIKE @Name
          AND V.CreateDate > CONVERT(smalldatetime,
          '01/01/2000')
          ORDER BY C.Name
          , E.NameLast
          , E.NameFirst
          , E.Number
          , ISNULL(I.Description,'NA')
          SELECT @Retain = @@ERROR, @Rows = @@ROWCOUNT
          IF @Status = 0 SET @Status = @Retain
          

          ?一個嵌套代碼塊中的語句使用四個空格的縮進(jìn)。(上述代碼中的多行 SELECT 語句是一個 SQL 語句。)在同一語句中開始新行時,使 SQL 關(guān)鍵字右對齊。將代碼編輯器配置為使用空格,而不是使用制表符。這樣,不管使用何種程序查看代碼,格式都是一致的。

          ?大寫所有的 T-SQL 關(guān)鍵字,包括 T-SQL 函數(shù)。變量名稱及光標(biāo)名稱使用混和大小寫。數(shù)據(jù)類型使用小寫。

          ?表名別名要簡短,但意義要盡量明確。通常,使用大寫的表名作為別名,使用 AS 關(guān)鍵字指定表或字段的別名。

          ?當(dāng)一個 T-SQL 語句中涉及到多個表時,始終使用表名別名來限定字段名。這使其他人閱讀起來更清楚,避免了含義模糊的引用。

          ?當(dāng)相關(guān)數(shù)字出現(xiàn)在連續(xù)的代碼行中時(例如一系列 SUBSTRING 函數(shù)調(diào)用),將它們排成列。這樣容易瀏覽數(shù)字列表。

          ?使用一個(而不是兩個)空行分隔 T-SQL 代碼的邏輯塊,只要需要就可以使用。

          ?聲明 T-SQL 局部變量(例如 @lngTableID)時,使用適當(dāng)?shù)臄?shù)據(jù)類型聲明和一致的大寫。

          ?始終指定字符數(shù)據(jù)類型的長度,并確保允許用戶可能需要的最大字符數(shù),因?yàn)槌鲎畲箝L度的字符會丟失。

          ?始終指定十進(jìn)制數(shù)據(jù)類型的精度和范圍,否則,將默認(rèn)為未指定精度和整數(shù)范圍。

          ?使用錯誤處理程序,但要記住行首 (BOL) 中的錯誤檢查示例不會象介紹的那樣起作用。用來檢查 @@ERROR 系統(tǒng)函數(shù)的 T-SQL 語句 (IF) 實(shí)際上在進(jìn)程中清除了 @@ERROR 值,無法再捕獲除零之外的任何值。(即使示例起作用,它們也只能捕獲最后發(fā)生的一個錯誤,而不是您更想捕獲的第一個錯誤。)必須使用 SET 或 SELECT 立即捕獲錯誤代碼,如前面示例所示。如果狀態(tài)變量仍然為零,應(yīng)轉(zhuǎn)換到狀態(tài)變量。

          ?避免使用“未聲明的”功能,例如系統(tǒng)表中未聲明的列、T-SQL 語句中未聲明的功能或者未聲明的系統(tǒng)存儲過程或擴(kuò)展的存儲過程。

          ?不要依賴任何隱式的數(shù)據(jù)類型轉(zhuǎn)換。例如,不能為數(shù)字變量賦予字符值,而假定 T-SQL 會進(jìn)行必要的轉(zhuǎn)換。相反,在為變量賦值或比較值之前,應(yīng)使用適當(dāng)?shù)?CONVERT 函數(shù)使數(shù)據(jù)類型相匹配。另一個示例:雖然 T-SQL 會在進(jìn)行比較之前對字符表達(dá)式進(jìn)行隱式且自動的 RTRIM,但不能依賴此行為,因?yàn)榧嫒菪约墑e設(shè)置非字符表達(dá)式會使情況復(fù)雜化。

          ?不要將空的變量值直接與比較運(yùn)算符(符號)比較。如果變量可能為空,應(yīng)使用 IS NULL 或 IS NOT NULL 進(jìn)行比較,或者使用 ISNULL 函數(shù)。

          ?不要使用 STR 函數(shù)進(jìn)行舍入,此函數(shù)只能用于整數(shù)。如果需要十進(jìn)制值的字符串形式,應(yīng)先使用 CONVERT 函數(shù)(轉(zhuǎn)至不同的范圍)或 ROUND 函數(shù),然后將其轉(zhuǎn)換為字符串。也可以使用 CEILING 和 FLOOR 函數(shù)。

          ?使用數(shù)學(xué)公式時要小心,因?yàn)?T-SQL 可能會將表達(dá)式強(qiáng)制理解為一個不需要的數(shù)據(jù)類型。如果需要十進(jìn)制結(jié)果,應(yīng)在整數(shù)常量后加點(diǎn)和零 (.0)。

          ?決不要依賴 SELECT 語句會按任何特定順序返回行,除非在 ORDER BY 子句中指定了順序。

          ?通常,應(yīng)將 ORDER BY 子句與 SELECT 語句一起使用。可預(yù)知的順序(即使不是最方便的)比不可預(yù)知的順序強(qiáng),尤其是在開發(fā)或調(diào)試過程中。(部署到生產(chǎn)環(huán)境中之前,可能需要刪除 ORDER BY 子句。)在返回行的順序無關(guān)緊要的情況下,可以忽略 ORDER BY 的開銷。

          ?不要在 T-SQL 代碼中使用雙引號。應(yīng)為字符常量使用單引號。如果沒有必要限定對象名稱,可以使用(非 ANSI SQL 標(biāo)準(zhǔn))括號將名稱括起來。

          ?在 SQL Server 2000 中,盡量使用表變量來代替臨時表。如果表變量包含大量數(shù)據(jù),請注意索引非常有限(只有主鍵索引)。

          ?先在例程中創(chuàng)建臨時表,最后再顯式刪除臨時表。將 DDL 與 DML 語句混合使用有助于處理額外的重新編譯活動。

          ?要認(rèn)識到臨時表并不是不可使用,適當(dāng)?shù)厥褂盟鼈兛梢允鼓承├谈行В纾?dāng)需要重復(fù)引用大型表或常用表中的某個數(shù)據(jù)集時。但是,對于一次性事件,最好使用導(dǎo)出表。

          ?使用表值 UDF 時要小心,因?yàn)樵谧兞浚ǘ皇浅A浚┲袀鬟f某個參數(shù)時,如果在 WHERE 子句中使用該參數(shù),會導(dǎo)致表掃描。還要避免在一個查詢中多次使用相同的表值 UDF。但是,表值 UDF 確實(shí)具有某些非常方便的動態(tài)編譯功能。[相關(guān)資料:參閱 Tom Moreau 2003 11 月份生成序列號專欄中的使用 UDF 填充表變量。-編者按]

          ?幾乎所有的存儲過程都應(yīng)在開始時設(shè)置 SET NOCOUNT ON,而在結(jié)束時設(shè)置 SET NOCOUNT OFF。[SET NOCOUNT ON 使 SQL Server 無需在執(zhí)行存儲過程的每個語句后向客戶端發(fā)送 DONE_IN_PROC 消息。- 編者按] 此標(biāo)準(zhǔn)同樣適用于觸發(fā)器。

          ?只要在例程中使用多個數(shù)據(jù)庫修改語句,包括在一個循環(huán)中多次執(zhí)行一個語句,就應(yīng)考慮聲明顯式事務(wù)。

          ?使用基于光標(biāo)的方法或臨時表方法之前,應(yīng)先尋找基于集的解決方案來解決問題。基于集的方法通常更有效。

          ?與臨時表一樣,光標(biāo)并不是不可使用。對小型數(shù)據(jù)集使用 FAST_FORWARD 光標(biāo)通常要優(yōu)于其他逐行處理方法,尤其是在必須引用幾個表才能獲得所需的數(shù)據(jù)時。在結(jié)果集中包括“合計”的例程通常要比使用光標(biāo)執(zhí)行的速度快。如果開發(fā)時間允許,基于光標(biāo)的方法和基于集的方法都可以嘗試一下,看哪一種方法的效果更好。

          ?使用包含序號(從 1 到 N)的表很方便。

          ?理解 CROSS JOIN 的工作原理并加以利用。例如,您可以在工作數(shù)據(jù)表和序號表之間有效地使用 CROSS JOIN,結(jié)果集中將包含每個工作數(shù)據(jù)與序號組合的記錄。

          ?我的結(jié)束語是:T-SQL 代碼往往很簡潔,因此如果某個代碼塊看起來很難處理或重復(fù)內(nèi)容較多,那么可能存在一種更簡單,更好的方法。

          結(jié)論

          如果您對我的建議有任何看法,歡迎隨時向我發(fā)送電子郵件進(jìn)行討論,也可以就其他問題提出您的建議。我希望您將此作為談話的開場白。

          其他信息:摘自 Karen 2000 2 月份的社論

          在標(biāo)準(zhǔn)開發(fā)的前沿陣地上,有一股以 SQL Server 數(shù)據(jù)庫管理員 John Hindmarsh 為首的獨(dú)立的新生力量。MCT、MCSE 和 MCDBA 都是最值得您花時間去研究的。John 的貢獻(xiàn)是撰寫了一份詳細(xì)的白皮書,概述了他對各種 SQL Server 相關(guān)標(biāo)準(zhǔn)提出的建議。我所知道的其他唯一提出類似建議的文章是 Andrew Zanevsky 的《Transact-SQL Programming》(ISBN 1-56592-401-0) 中的“Format and Style”一章。Andrew、SQL Server Professional 的投稿人 Tom Moreau 和 Paul Munkenbeck 以及 John 的朋友兼同事 Stephen James 都為 John 的白皮書做出過貢獻(xiàn)。下面是 John 為編寫存儲過程提供的建議示例:

          使用 SQL-92 標(biāo)準(zhǔn)連接句法。

          為了提高性能,應(yīng)優(yōu)先使用連接,然后使用子查詢或嵌套查詢。

          確保變量和參數(shù)的類型和大小與表數(shù)據(jù)列相匹配。

          確保使用所有變量和參數(shù),或者全部刪除。

          盡可能將臨時對象放置在本地。

          只使用在存儲過程中創(chuàng)建的臨時表。

          檢查輸入?yún)?shù)的有效性。

          優(yōu)先使用 SELECT...INTO,然后使用 INSERT...SELECT,以避免大量死鎖。

          維護(hù)工作需要的邏輯單元;在可以縮短的情況下,不要創(chuàng)建大量或長時間運(yùn)行的進(jìn)程。

          不要在任何代碼中使用 SELECT *。

          在過程中使用縮進(jìn)、塊、制表符和空格(參閱示例腳本)。

          T-SQL 語句要大寫。

          在過程中添加大量注釋,確保可以識別進(jìn)程。在有助于澄清處理步驟的地方使用行注釋。

          包括事務(wù)管理,除非要從 MTS 進(jìn)程中調(diào)用過程。(為 MTS 進(jìn)程編寫?yīng)毩⒌倪^程。)

          監(jiān)視 @@TRANCOUNT 以確定事務(wù)的責(zé)任級別。

          避免使用 GOTO,錯誤處理程序中除外。

          避免使用嵌套過程。

          避免隱式解析對象名稱,確保所有對象都?xì)w dbo 所有。

          下載 412BRIAN.ZIP

          鏈接至www.microsoft.com/downloads/details.aspx?displayla%20ng=en&familyid=B352EB1F-D3CA-44EE-893E-9E07339C1F22&displaylang=en

          有關(guān) SQL Server Professional 和 Pinnacle Publishing 的詳細(xì)信息,請訪問其 Web 站點(diǎn) http://www.pinpub.com/

          注意:這不是 Microsoft Corporation 的 Web 站點(diǎn)。Microsoft 對該 Web 站點(diǎn)上的內(nèi)容不承擔(dān)任何責(zé)任。

          本文轉(zhuǎn)載自 2004 年 12 月份的 SQL Server Professional。除非另行說明,否則版權(quán)所有 2004 Pinnacle Publishing, Inc.。保留所有權(quán)利。SQL Server Professional 是 Pinnacle Publishing 獨(dú)立發(fā)行的刊物。未經(jīng) Pinnacle Publishing, Inc. 事先同意,不得以任何方式使用或復(fù)制本文的任何部分(評論文章中的簡短引用除外)。如需與 Pinnacle Publishing, Inc. 聯(lián)系,請撥打 1-800-788-1900。

          © 2005 Microsoft Corporation 版權(quán)所有。保留所有權(quán)利。使用規(guī)定。

          原文出處:http://www.microsoft.com/china/msdn/library/data/sqlserver/sp04l9.mspx?mfr=true

          posted @ 2008-12-02 10:50 竹子 閱讀(356) | 評論 (0)編輯 收藏
            1      選擇最有效率的表名順序(只在基于規(guī)則的優(yōu)化器中有效)

          ORACLE 的解析器按照從右到左的順序處理FROM子句中的表名,FROM子句中寫在最后的表(基礎(chǔ)表 driving table)將被最先處理,在FROM子句中包含多個表的情況下,你必須選擇記錄條數(shù)最少的表作為基礎(chǔ)表。如果有3個以上的表連接查詢, 那就需要選擇交叉表(intersection table)作為基礎(chǔ)表, 交叉表是指那個被其他表所引用的表.

          2      WHERE子句中的連接順序.:

          ORACLE采用自下而上的順序解析WHERE子句,根據(jù)這個原理,表之間的連接必須寫在其他WHERE條件之前, 那些可以過濾掉最大數(shù)量記錄的條件必須寫在WHERE子句的末尾.

          3      SELECT子句中避免使用 * ‘:

          ORACLE在解析的過程中, 會將'*' 依次轉(zhuǎn)換成所有的列名, 這個工作是通過查詢數(shù)據(jù)字典完成的, 這意味著將耗費(fèi)更多的時間

          4      減少訪問數(shù)據(jù)庫的次數(shù):

          ORACLE在內(nèi)部執(zhí)行了許多工作: 解析SQL語句, 估算索引的利用率, 綁定變量 , 讀數(shù)據(jù)塊等;

          5      SQL*Plus , SQL*FormsPro*C中重新設(shè)置ARRAYSIZE參數(shù), 可以增加每次數(shù)據(jù)庫訪問的檢索數(shù)據(jù)量 ,建議值為200

          6      使用DECODE函數(shù)來減少處理時間:

          使用DECODE函數(shù)可以避免重復(fù)掃描相同記錄或重復(fù)連接相同的表.

          7      整合簡單,無關(guān)聯(lián)的數(shù)據(jù)庫訪問:

          如果你有幾個簡單的數(shù)據(jù)庫查詢語句,你可以把它們整合到一個查詢中(即使它們之間沒有關(guān)系)

          8      刪除重復(fù)記錄:

          最高效的刪除重復(fù)記錄方法 ( 因?yàn)槭褂昧?/span>ROWID)例子:

          DELETE FROM EMP E WHERE E.ROWID > (SELECT MIN(X.ROWID)

          FROM EMP X WHERE X.EMP_NO = E.EMP_NO);

          9      TRUNCATE替代DELETE

          當(dāng)刪除表中的記錄時,在通常情況下, 回滾段(rollback segments ) 用來存放可以被恢復(fù)的信息. 如果你沒有COMMIT事務(wù),ORACLE會將數(shù)據(jù)恢復(fù)到刪除之前的狀態(tài)(準(zhǔn)確地說是恢復(fù)到執(zhí)行刪除命令之前的狀況) 而當(dāng)運(yùn)用TRUNCATE, 回滾段不再存放任何可被恢復(fù)的信息.當(dāng)命令運(yùn)行后,數(shù)據(jù)不能被恢復(fù).因此很少的資源被調(diào)用,執(zhí)行時間也會很短. (譯者按: TRUNCATE只在刪除全表適用,TRUNCATEDDL不是DML)

          10 盡量多使用COMMIT

          只要有可能,在程序中盡量多使用COMMIT, 這樣程序的性能得到提高,需求也會因?yàn)?/span>COMMIT所釋放的資源而減少:

          COMMIT所釋放的資源:

          a. 回滾段上用于恢復(fù)數(shù)據(jù)的信息.

          b. 被程序語句獲得的鎖

          c. redo log buffer 中的空間

          d. ORACLE為管理上述3種資源中的內(nèi)部花費(fèi)

          11 Where子句替換HAVING子句:

          避免使用HAVING子句, HAVING 只會在檢索出所有記錄之后才對結(jié)果集進(jìn)行過濾. 這個處理需要排序,總計等操作. 如果能通過WHERE子句限制記錄的數(shù)目,那就能減少這方面的開銷. (oracle)onwherehaving這三個都可以加條件的子句中,on是最先執(zhí)行,where次之,having最后,因?yàn)?/span>on是先把不符合條件的記錄過濾后才進(jìn)行統(tǒng)計,它就可以減少中間運(yùn)算要處理的數(shù)據(jù),按理說應(yīng)該速度是最快的,where也應(yīng)該比having快點(diǎn)的,因?yàn)樗^濾數(shù)據(jù)后才進(jìn)行sum,在兩個表聯(lián)接時才用on的,所以在一個表的時候,就剩下wherehaving比較了。在這單表查詢統(tǒng)計的情況下,如果要過濾的條件沒有涉及到要計算字段,那它們的結(jié)果是一樣的,只是where可以使用rushmore技術(shù),而having就不能,在速度上后者要慢如果要涉及到計算的字段,就表示在沒計算之前,這個字段的值是不確定的,根據(jù)上篇寫的工作流程,where的作用時間是在計算之前就完成的,而having就是在計算后才起作用的,所以在這種情況下,兩者的結(jié)果會不同。在多表聯(lián)接查詢時,onwhere更早起作用。系統(tǒng)首先根據(jù)各個表之間的聯(lián)接條件,把多個表合成一個臨時表后,再由where進(jìn)行過濾,然后再計算,計算完后再由having進(jìn)行過濾。由此可見,要想過濾條件起到正確的作用,首先要明白這個條件應(yīng)該在什么時候起作用,然后再決定放在那里

          12 減少對表的查詢:

          在含有子查詢的SQL語句中,要特別注意減少對表的查詢.例子:

               SELECT TAB_NAME FROM TABLES WHERE (TAB_NAME,DB_VER) = ( SELECT

          TAB_NAME,DB_VER FROM TAB_COLUMNS WHERE VERSION = 604)

          13 通過內(nèi)部函數(shù)提高SQL效率.

          復(fù)雜的SQL往往犧牲了執(zhí)行效率. 能夠掌握上面的運(yùn)用函數(shù)解決問題的方法在實(shí)際工作中是非常有意義的

          14 使用表的別名(Alias)

          當(dāng)在SQL語句中連接多個表時, 請使用表的別名并把別名前綴于每個Column.這樣一來,就可以減少解析的時間并減少那些由Column歧義引起的語法錯誤.

          15 EXISTS替代IN、用NOT EXISTS替代NOT IN

          在許多基于基礎(chǔ)表的查詢中,為了滿足一個條件,往往需要對另一個表進(jìn)行聯(lián)接.在這種情況下, 使用EXISTS(NOT EXISTS)通常將提高查詢的效率. 在子查詢中,NOT IN子句將執(zhí)行一個內(nèi)部的排序和合并. 無論在哪種情況下,NOT IN都是最低效的 (因?yàn)樗鼘ψ硬樵冎械谋韴?zhí)行了一個全表遍歷). 為了避免使用NOT IN ,我們可以把它改寫成外連接(Outer Joins)NOT EXISTS.

          例子:

          (高效)SELECT * FROM EMP (基礎(chǔ)表) WHERE EMPNO > 0 AND EXISTS (SELECT X' FROM DEPT WHERE DEPT.DEPTNO = EMP.DEPTNO AND LOC = MELB')

          (低效)SELECT * FROM EMP (基礎(chǔ)表) WHERE EMPNO > 0 AND DEPTNO IN(SELECT DEPTNO FROM DEPT WHERE LOC = MELB')

          16 識別'低效執(zhí)行'SQL語句:

          雖然目前各種關(guān)于SQL優(yōu)化的圖形化工具層出不窮,但是寫出自己的SQL工具來解決問題始終是一個最好的方法:

          SELECT EXECUTIONS , DISK_READS, BUFFER_GETS,

          ROUND((BUFFER_GETS-DISK_READS)/BUFFER_GETS,2) Hit_radio,

          ROUND(DISK_READS/EXECUTIONS,2) Reads_per_run,

          SQL_TEXT

          FROM V$SQLAREA

          WHERE EXECUTIONS>0

          AND BUFFER_GETS > 0

          AND (BUFFER_GETS-DISK_READS)/BUFFER_GETS < 0.8

          ORDER BY 4 DESC;

          17 用索引提高效率:

          索引是表的一個概念部分,用來提高檢索數(shù)據(jù)的效率,ORACLE使用了一個復(fù)雜的自平衡B-tree結(jié)構(gòu). 通常,通過索引查詢數(shù)據(jù)比全表掃描要快. 當(dāng)ORACLE找出執(zhí)行查詢和Update語句的最佳路徑時, ORACLE優(yōu)化器將使用索引. 同樣在聯(lián)結(jié)多個表時使用索引也可以提高效率. 另一個使用索引的好處是,它提供了主鍵(primary key)的唯一性驗(yàn)證.。那些LONGLONG RAW數(shù)據(jù)類型, 你可以索引幾乎所有的列. 通常, 在大型表中使用索引特別有效. 當(dāng)然,你也會發(fā)現(xiàn), 在掃描小表時,使用索引同樣能提高效率. 雖然使用索引能得到查詢效率的提高,但是我們也必須注意到它的代價. 索引需要空間來存儲,也需要定期維護(hù), 每當(dāng)有記錄在表中增減或索引列被修改時, 索引本身也會被修改. 這意味著每條記錄的INSERT , DELETE , UPDATE將為此多付出4 , 5 次的磁盤I/O . 因?yàn)樗饕枰~外的存儲空間和處理,那些不必要的索引反而會使查詢反應(yīng)時間變慢.。定期的重構(gòu)索引是有必要的.

          ALTER INDEX <INDEXNAME> REBUILD <TABLESPACENAME>

          18 EXISTS替換DISTINCT

          當(dāng)提交一個包含一對多表信息(比如部門表和雇員表)的查詢時,避免在SELECT子句中使用DISTINCT. 一般可以考慮用EXIST替換, EXISTS 使查詢更為迅速,因?yàn)?/span>RDBMS核心模塊將在子查詢的條件一旦滿足后,立刻返回結(jié)果. 例子:

                 (低效):

          SELECT DISTINCT DEPT_NO,DEPT_NAME FROM DEPT D , EMP E

          WHERE D.DEPT_NO = E.DEPT_NO

          (高效):

          SELECT DEPT_NO,DEPT_NAME FROM DEPT D WHERE EXISTS ( SELECT ‘X'

          FROM EMP E WHERE E.DEPT_NO = D.DEPT_NO);

          19 sql語句用大寫的;因?yàn)?/span>oracle總是先解析sql語句,把小寫的字母轉(zhuǎn)換成大寫的再執(zhí)行

          20 java代碼中盡量少用連接符“+”連接字符串!

          21 避免在索引列上使用NOT 通常, 

          我們要避免在索引列上使用NOT, NOT會產(chǎn)生在和在索引列上使用函數(shù)相同的影響. 當(dāng)ORACLE”遇到”NOT,他就會停止使用索引轉(zhuǎn)而執(zhí)行全表掃描.

          22 避免在索引列上使用計算.

          WHERE子句中,如果索引列是函數(shù)的一部分.優(yōu)化器將不使用索引而使用全表掃描.

          舉例:

          低效:

          SELECT … FROM DEPT WHERE SAL * 12 > 25000;

          高效:

          SELECT … FROM DEPT WHERE SAL > 25000/12;

          23 >=替代>

          高效:

          SELECT * FROM EMP WHERE DEPTNO >=4

          低效:

          SELECT * FROM EMP WHERE DEPTNO >3

          兩者的區(qū)別在于, 前者DBMS將直接跳到第一個DEPT等于4的記錄而后者將首先定位到DEPTNO=3的記錄并且向前掃描到第一個DEPT大于3的記錄.

          24 UNION替換OR (適用于索引列)

          通常情況下, UNION替換WHERE子句中的OR將會起到較好的效果. 對索引列使用OR將造成全表掃描. 注意, 以上規(guī)則只針對多個索引列有效. 如果有column沒有被索引, 查詢效率可能會因?yàn)槟銢]有選擇OR而降低. 在下面的例子中, LOC_ID REGION上都建有索引.

          高效:

          SELECT LOC_ID , LOC_DESC , REGION

          FROM LOCATION

          WHERE LOC_ID = 10

          UNION

          SELECT LOC_ID , LOC_DESC , REGION

          FROM LOCATION

          WHERE REGION = “MELBOURNE”

          低效:

          SELECT LOC_ID , LOC_DESC , REGION

          FROM LOCATION

          WHERE LOC_ID = 10 OR REGION = “MELBOURNE”

          如果你堅持要用OR, 那就需要返回記錄最少的索引列寫在最前面.

          25 IN來替換OR 

          這是一條簡單易記的規(guī)則,但是實(shí)際的執(zhí)行效果還須檢驗(yàn),在ORACLE8i下,兩者的執(zhí)行路徑似乎是相同的. 

          低效:

          SELECT…. FROM LOCATION WHERE LOC_ID = 10 OR LOC_ID = 20 OR LOC_ID = 30

          高效

          SELECT… FROM LOCATION WHERE LOC_IN IN (10,20,30);

          26 避免在索引列上使用IS NULLIS NOT NULL

          避免在索引中使用任何可以為空的列,ORACLE將無法使用該索引.對于單列索引,如果列包含空值,索引中將不存在此記錄. 對于復(fù)合索引,如果每個列都為空,索引中同樣不存在此記錄. 如果至少有一個列不為空,則記錄存在于索引中.舉例: 如果唯一性索引建立在表的A列和B列上, 并且表中存在一條記錄的A,B值為(123,null) , ORACLE將不接受下一條具有相同A,B值(123,null)的記錄(插入). 然而如果所有的索引列都為空,ORACLE將認(rèn)為整個鍵值為空而空不等于空. 因此你可以插入1000 條具有相同鍵值的記錄,當(dāng)然它們都是空! 因?yàn)榭罩挡淮嬖谟谒饕兄?/span>,所以WHERE子句中對索引列進(jìn)行空值比較將使ORACLE停用該索引.

          低效: (索引失效)

          SELECT … FROM DEPARTMENT WHERE DEPT_CODE IS NOT NULL;

          高效: (索引有效)

          SELECT … FROM DEPARTMENT WHERE DEPT_CODE >=0;

          27 總是使用索引的第一個列:

          如果索引是建立在多個列上, 只有在它的第一個列(leading column)where子句引用時,優(yōu)化器才會選擇使用該索引. 這也是一條簡單而重要的規(guī)則,當(dāng)僅引用索引的第二個列時,優(yōu)化器使用了全表掃描而忽略了索引

          28 UNION-ALL 替換UNION ( 如果有可能的話)

          當(dāng)SQL 語句需要UNION兩個查詢結(jié)果集合時,這兩個結(jié)果集合會以UNION-ALL的方式被合并, 然后在輸出最終結(jié)果前進(jìn)行排序. 如果用UNION ALL替代UNION, 這樣排序就不是必要了. 效率就會因此得到提高. 需要注意的是,UNION ALL 將重復(fù)輸出兩個結(jié)果集合中相同記錄. 因此各位還是要從業(yè)務(wù)需求分析使用UNION ALL的可行性. UNION 將對結(jié)果集合排序,這個操作會使用到SORT_AREA_SIZE這塊內(nèi)存. 對于這塊內(nèi)存的優(yōu)化也是相當(dāng)重要的. 下面的SQL可以用來查詢排序的消耗量

          低效:

          SELECT ACCT_NUM, BALANCE_AMT

          FROM DEBIT_TRANSACTIONS

          WHERE TRAN_DATE = '31-DEC-95'

          UNION

          SELECT ACCT_NUM, BALANCE_AMT

          FROM DEBIT_TRANSACTIONS

          WHERE TRAN_DATE = '31-DEC-95'

          高效:

          SELECT ACCT_NUM, BALANCE_AMT

          FROM DEBIT_TRANSACTIONS

          WHERE TRAN_DATE = '31-DEC-95'

          UNION ALL

          SELECT ACCT_NUM, BALANCE_AMT

          FROM DEBIT_TRANSACTIONS

          WHERE TRAN_DATE = '31-DEC-95'

          29 WHERE替代ORDER BY

          ORDER BY 子句只在兩種嚴(yán)格的條件下使用索引.

          ORDER BY中所有的列必須包含在相同的索引中并保持在索引中的排列順序.

          ORDER BY中所有的列必須定義為非空.

          WHERE子句使用的索引和ORDER BY子句中所使用的索引不能并列.

          例如:

          DEPT包含以下列:

          DEPT_CODE PK NOT NULL

          DEPT_DESC NOT NULL

          DEPT_TYPE NULL

          低效: (索引不被使用)

          SELECT DEPT_CODE FROM DEPT ORDER BY DEPT_TYPE

          高效: (使用索引)

          SELECT DEPT_CODE FROM DEPT WHERE DEPT_TYPE > 0

          30 避免改變索引列的類型.:

          當(dāng)比較不同數(shù)據(jù)類型的數(shù)據(jù)時, ORACLE自動對列進(jìn)行簡單的類型轉(zhuǎn)換.

          假設(shè) EMPNO是一個數(shù)值類型的索引列.

          SELECT … FROM EMP WHERE EMPNO = ‘123'

          實(shí)際上,經(jīng)過ORACLE類型轉(zhuǎn)換, 語句轉(zhuǎn)化為:

          SELECT … FROM EMP WHERE EMPNO = TO_NUMBER(‘123')

          幸運(yùn)的是,類型轉(zhuǎn)換沒有發(fā)生在索引列上,索引的用途沒有被改變.

          現(xiàn)在,假設(shè)EMP_TYPE是一個字符類型的索引列.

          SELECT … FROM EMP WHERE EMP_TYPE = 123

          這個語句被ORACLE轉(zhuǎn)換為:

          SELECT … FROM EMP WHERETO_NUMBER(EMP_TYPE)=123

          因?yàn)閮?nèi)部發(fā)生的類型轉(zhuǎn)換, 這個索引將不會被用到! 為了避免ORACLE對你的SQL進(jìn)行隱式的類型轉(zhuǎn)換, 最好把類型轉(zhuǎn)換用顯式表現(xiàn)出來. 注意當(dāng)字符和數(shù)值比較時, ORACLE會優(yōu)先轉(zhuǎn)換數(shù)值類型到字符類型

          31 需要當(dāng)心的WHERE子句:

          某些SELECT 語句中的WHERE子句不使用索引. 這里有一些例子.

          在下面的例子里, (1)!=' 將不使用索引. 記住, 索引只能告訴你什么存在于表中, 而不能告訴你什么不存在于表中. (2) ||'是字符連接函數(shù). 就象其他函數(shù)那樣, 停用了索引. (3) +'是數(shù)學(xué)函數(shù). 就象其他數(shù)學(xué)函數(shù)那樣, 停用了索引. (4)相同的索引列不能互相比較,這將會啟用全表掃描.

          32 a. 如果檢索數(shù)據(jù)量超過30%的表中記錄數(shù).使用索引將沒有顯著的效率提高.

          b. 在特定情況下, 使用索引也許會比全表掃描慢, 但這是同一個數(shù)量級上的區(qū)別. 而通常情況下,使用索引比全表掃描要塊幾倍乃至幾千倍!

          33 避免使用耗費(fèi)資源的操作:

          帶有DISTINCT,UNION,MINUS,INTERSECT,ORDER BYSQL語句會啟動SQL引擎

          執(zhí)行耗費(fèi)資源的排序(SORT)功能. DISTINCT需要一次排序操作, 而其他的至少需要執(zhí)行兩次排序. 通常, 帶有UNION, MINUS , INTERSECTSQL語句都可以用其他方式重寫. 如果你的數(shù)據(jù)庫的SORT_AREA_SIZE調(diào)配得好, 使用UNION , MINUS, INTERSECT也是可以考慮的, 畢竟它們的可讀性很強(qiáng)

          34 優(yōu)化GROUP BY:

          提高GROUP BY 語句的效率, 可以通過將不需要的記錄在GROUP BY 之前過濾掉.下面兩個查詢返回相同結(jié)果但第二個明顯就快了許多.

          低效:

          SELECT JOB , AVG(SAL)

          FROM EMP

          GROUP JOB

          HAVING JOB = ‘PRESIDENT'

          OR JOB = ‘MANAGER'

          高效:

          SELECT JOB , AVG(SAL)

          FROM EMP

          WHERE JOB = ‘PRESIDENT'

          OR JOB = ‘MANAGER'

          GROUP JOB

          posted @ 2008-12-02 10:42 竹子 閱讀(208) | 評論 (0)編輯 收藏
            2008年11月20日
           


          l l 數(shù)據(jù)庫回滾段是否足夠?


          l l 是否需要建立ORACLE數(shù)據(jù)庫索引、聚集、散列?


          l l 系統(tǒng)全局區(qū)(SGA)大小是否足夠?


          l l SQL語句是否高效?


          2、2、數(shù)據(jù)倉庫系統(tǒng)(Data Warehousing),這種信息系統(tǒng)的主要任務(wù)是從ORACLE的海量數(shù)據(jù)中進(jìn)行查詢,得到數(shù)據(jù)之間的某些規(guī)律。數(shù)據(jù)庫管理員需要為這種類型的ORACLE數(shù)據(jù)庫著重考慮下述參數(shù):


          l l 是否采用B*-索引或者bitmap索引?


          l l 是否采用并行SQL查詢以提高查詢效率?


          l l 是否采用PL/SQL函數(shù)編寫存儲過程?


          l l 有必要的話,需要建立并行數(shù)據(jù)庫提高數(shù)據(jù)庫的查詢效率


          SQL語句的調(diào)整原則


          SQL語言是一種靈活的語言,相同的功能可以使用不同的語句來實(shí)現(xiàn),但是語句的執(zhí)行效率是很不相同的。程序員可以使用EXPLAIN PLAN語句來比較各種實(shí)現(xiàn)方案,并選出最優(yōu)的實(shí)現(xiàn)方案。總得來講,程序員寫SQL語句需要滿足考慮如下規(guī)則:


          1、1、盡量使用索引。試比較下面兩條SQL語句:


          語句A:SELECT dname, deptno FROM dept WHERE deptno NOT IN


          (SELECT deptno FROM emp);


          語句B:SELECT dname, deptno FROM dept WHERE NOT EXISTS


          (SELECT deptno FROM emp WHERE dept.deptno = emp.deptno);


          這兩條查詢語句實(shí)現(xiàn)的結(jié)果是相同的,但是執(zhí)行語句A的時候,ORACLE會對整個emp表進(jìn)行掃描,沒有使用建立在emp表上的deptno索引,執(zhí)行語句B的時候,由于在子查詢中使用了聯(lián)合查詢,ORACLE只是對emp表進(jìn)行的部分?jǐn)?shù)據(jù)掃描,并利用了deptno列的索引,所以語句B的效率要比語句A的效率高一些。


          2、2、選擇聯(lián)合查詢的聯(lián)合次序。考慮下面的例子:


          SELECT stuff FROM taba a, tabb b, tabc c


          WHERE a.acol between :alow and :ahigh


          AND b.bcol between :blow and :bhigh


          AND c.ccol between :clow and :chigh


          AND a.key1 = b.key1


          AMD a.key2 = c.key2;


          這個SQL例子中,程序員首先需要選擇要查詢的主表,因?yàn)橹鞅硪M(jìn)行整個表數(shù)據(jù)的掃描,所以主表應(yīng)該數(shù)據(jù)量最小,所以例子中表A的acol列的范圍應(yīng)該比表B和表C相應(yīng)列的范圍小。


          3、3、在子查詢中慎重使用IN或者NOT IN語句,使用where (NOT) exists的效果要好的多。


          4、4、慎重使用視圖的聯(lián)合查詢,尤其是比較復(fù)雜的視圖之間的聯(lián)合查詢。一般對視圖的查詢最好都分解為對數(shù)據(jù)表的直接查詢效果要好一些。


          5、5、可以在參數(shù)文件中設(shè)置SHARED_POOL_RESERVED_SIZE參數(shù),這個參數(shù)在SGA共享池中保留一個連續(xù)的內(nèi)存空間,連續(xù)的內(nèi)存空間有益于存放大的SQL程序包。


          6、6、ORACLE公司提供的DBMS_SHARED_POOL程序可以幫助程序員將某些經(jīng)常使用的存儲過程“釘”在SQL區(qū)中而不被換出內(nèi)存,程序員對于經(jīng)常使用并且占用內(nèi)存很多的存儲過程“釘”到內(nèi)存中有利于提高最終用戶的響應(yīng)時間。


          CPU參數(shù)的調(diào)整


          CPU是服務(wù)器的一項(xiàng)重要資源,服務(wù)器良好的工作狀態(tài)是在工作高峰時CPU的使用率在90%以上。如果空閑時間CPU使用率就在90%以上,說明服務(wù)器缺乏CPU資源,如果工作高峰時CPU使用率仍然很低,說明服務(wù)器CPU資源還比較富余。


          使用操作相同命令可以看到CPU的使用情況,一般UNIX操作系統(tǒng)的服務(wù)器,可以使用sar –u命令查看CPU的使用率,NT操作系統(tǒng)的服務(wù)器,可以使用NT的性能管理器來查看CPU的使用率。


          數(shù)據(jù)庫管理員可以通過查看v$sysstat數(shù)據(jù)字典中“CPU used by this session”統(tǒng)計項(xiàng)得知ORACLE數(shù)據(jù)庫使用的CPU時間,查看“OS User level CPU time”統(tǒng)計項(xiàng)得知操作系統(tǒng)用戶態(tài)下的CPU時間,查看“OS System call CPU time”統(tǒng)計項(xiàng)得知操作系統(tǒng)系統(tǒng)態(tài)下的CPU時間,操作系統(tǒng)總的CPU時間就是用戶態(tài)和系統(tǒng)態(tài)時間之和,如果ORACLE數(shù)據(jù)庫使用的CPU時間占操作系統(tǒng)總的CPU時間90%以上,說明服務(wù)器CPU基本上被ORACLE數(shù)據(jù)庫使用著,這是合理,反之,說明服務(wù)器CPU被其它程序占用過多,ORACLE數(shù)據(jù)庫無法得到更多的CPU時間。


          數(shù)據(jù)庫管理員還可以通過查看v$sesstat數(shù)據(jù)字典來獲得當(dāng)前連接ORACLE數(shù)據(jù)庫各個會話占用的CPU時間,從而得知什么會話耗用服務(wù)器CPU比較多。


          出現(xiàn)CPU資源不足的情況是很多的:SQL語句的重解析、低效率的SQL語句、鎖沖突都會引起CPU資源不足。


          1、數(shù)據(jù)庫管理員可以執(zhí)行下述語句來查看SQL語句的解析情況:


          SELECT * FROM V$SYSSTAT


          WHERE NAME IN


          ('parse time cpu', 'parse time elapsed', 'parse count (hard)');


          這里parse time cpu是系統(tǒng)服務(wù)時間,parse time elapsed是響應(yīng)時間,用戶等待時間


          waite time = parse time elapsed – parse time cpu


          由此可以得到用戶SQL語句平均解析等待時間=waite time / parse count。這個平均等待時間應(yīng)該接近于0,如果平均解析等待時間過長,數(shù)據(jù)庫管理員可以通過下述語句


          SELECT SQL_TEXT, PARSE_CALLS, EXECUTIONS FROM V$SQLAREA


          ORDER BY PARSE_CALLS;


          來發(fā)現(xiàn)是什么SQL語句解析效率比較低。程序員可以優(yōu)化這些語句,或者增加ORACLE參數(shù)SESSION_CACHED_CURSORS的值。


          2、數(shù)據(jù)庫管理員還可以通過下述語句:


          SELECT BUFFER_GETS, EXECUTIONS, SQL_TEXT FROM V$SQLAREA;


          查看低效率的SQL語句,優(yōu)化這些語句也有助于提高CPU的利用率。


          3、3、數(shù)據(jù)庫管理員可以通過v$system_event數(shù)據(jù)字典中的“latch free”統(tǒng)計項(xiàng)查看ORACLE數(shù)據(jù)庫的沖突情況,如果沒有沖突的話,latch free查詢出來沒有結(jié)果。如果沖突太大的話,數(shù)據(jù)庫管理員可以降低spin_count參數(shù)值,來消除高的CPU使用率。


          內(nèi)存參數(shù)的調(diào)整


          內(nèi)存參數(shù)的調(diào)整主要是指ORACLE數(shù)據(jù)庫的系統(tǒng)全局區(qū)(SGA)的調(diào)整。SGA主要由三部分構(gòu)成:共享池、數(shù)據(jù)緩沖區(qū)、日志緩沖區(qū)。


          1、 1、 共享池由兩部分構(gòu)成:共享SQL區(qū)和數(shù)據(jù)字典緩沖區(qū),共享SQL區(qū)是存放用戶SQL命令的區(qū)域,數(shù)據(jù)字典緩沖區(qū)存放數(shù)據(jù)庫運(yùn)行的動態(tài)信息。數(shù)據(jù)庫管理員通過執(zhí)行下述語句:


          select (sum(pins - reloads)) / sum(pins) "Lib Cache" from v$librarycache;


          來查看共享SQL區(qū)的使用率。這個使用率應(yīng)該在90%以上,否則需要增加共享池的大小。數(shù)據(jù)庫管理員還可以執(zhí)行下述語句:


          select (sum(gets - getmisses - usage - fixed)) / sum(gets) "Row Cache" from v$rowcache;


          查看數(shù)據(jù)字典緩沖區(qū)的使用率,這個使用率也應(yīng)該在90%以上,否則需要增加共享池的大小。


          2、 2、 數(shù)據(jù)緩沖區(qū)。數(shù)據(jù)庫管理員可以通過下述語句:


          SELECT name, value FROM v$sysstat WHERE name IN ('db block gets', 'consistent gets','physical reads');


          來查看數(shù)據(jù)庫數(shù)據(jù)緩沖區(qū)的使用情況。查詢出來的結(jié)果可以計算出來數(shù)據(jù)緩沖區(qū)的使用命中率=1 - ( physical reads / (db block gets + consistent gets) )。


          這個命中率應(yīng)該在90%以上,否則需要增加數(shù)據(jù)緩沖區(qū)的大小。


          3、 3、 日志緩沖區(qū)。數(shù)據(jù)庫管理員可以通過執(zhí)行下述語句:


          select name,value from v$sysstat where name in ('redo entries','redo log space requests');查看日志緩沖區(qū)的使用情況。查詢出的結(jié)果可以計算出日志緩沖區(qū)的申請失敗率:

          申請失敗率=requests/entries,申請失敗率應(yīng)該接近于0,否則說明日志緩沖區(qū)開設(shè)太小,需要增加ORACLE數(shù)據(jù)庫的日志緩沖區(qū)。
          posted @ 2008-11-20 09:47 竹子 閱讀(369) | 評論 (0)編輯 收藏
          本文描述了Oracle 的查詢優(yōu)化程序,它是數(shù)據(jù)的關(guān)鍵組件,能讓Oracle用戶獲得極佳的執(zhí)行性能。Oracle 的查詢優(yōu)化技術(shù)功能上無與倫比,本文詳細(xì)討論了查詢優(yōu)化的所有重要領(lǐng)域。

            簡介

            什么是查詢優(yōu)化程序?

            查詢優(yōu)化對于關(guān)系數(shù)據(jù)庫的性能,特別是對于執(zhí)行復(fù)雜SQL 語句的性能而言至關(guān)重要。查詢優(yōu)化程序確定執(zhí)行每一次查詢的最佳策略。

            例如,查詢優(yōu)化程序選擇對于指定的查詢是否使用索引,以及在聯(lián)接多個表時采用哪一種聯(lián)接技術(shù)。這類決策對SQL 語句的執(zhí)行性能有很大的影響,查詢優(yōu)化對于每一種應(yīng)用程序都是關(guān)鍵技術(shù),應(yīng)用程序涉及的范圍從操作系統(tǒng)到數(shù)據(jù)倉庫,從分析系統(tǒng)到內(nèi)容管理系統(tǒng)。查詢優(yōu)化程序?qū)τ?span id="wmqeeuq" class="t_tag" onclick="tagshow(event)" href="tag.php?name=%D3%A6%D3%C3">應(yīng)用程序和最終用戶是完全透明的。

            由于應(yīng)用程序可能生成非常復(fù)雜的SQL 語句, 查詢優(yōu)化程序必須精心構(gòu)建、功能強(qiáng)大,以保障良好的執(zhí)行性能。例如,查詢優(yōu)化程序可轉(zhuǎn)換SQL 語句,使復(fù)雜的語句轉(zhuǎn)換成為等價的但執(zhí)行性能更好的SQL 語句。查詢優(yōu)化程序的典型特征是基于開銷。在基于開銷的優(yōu)化策略中,對于給定查詢生成多個執(zhí)行計劃,然后對每個計劃估算開銷。查詢優(yōu)化程序選用估算開銷最低的計劃。

            Oracle 在查詢優(yōu)化方面提供了什么?

            Oracle 的優(yōu)化程序可稱是業(yè)界最成功的優(yōu)化程序。基于開銷的優(yōu)化程序自1992 年隨Oracle7 推出后,通過10 年的豐富的實(shí)際用戶經(jīng)驗(yàn),不斷得到提高和改進(jìn)。好的查詢優(yōu)化程序不是基于純粹的理論假設(shè)及謂詞在實(shí)驗(yàn)室中開發(fā)出來的,而是通過適合實(shí)際用戶需求開發(fā)和磨合出來的。

            Oracle 的查詢優(yōu)化程序比任何其他查詢優(yōu)化程序在數(shù)據(jù)庫應(yīng)用程序的應(yīng)用都要多,而且Oracle 的優(yōu)化程序一直由于實(shí)際應(yīng)用的反饋而得到改進(jìn)。

            Oracle 的優(yōu)化程序包含4 大主要部分(本文將在以下章節(jié)詳細(xì)討論這些部分):

            SQL 語句轉(zhuǎn)換:在查詢優(yōu)化中Oracle 使用一系列精深技術(shù)對SQL 語句進(jìn)行轉(zhuǎn)換。查詢優(yōu)化的這一步驟的目的是將原有的SQL 語句轉(zhuǎn)換成為語義相同而處理效率更高的SQL 語句。

            執(zhí)行計劃選擇:對于每個SQL 語句, 優(yōu)化程序選擇一個執(zhí)行計劃(可使用Oracle 的EXPLAIN PLAN 工具或通過Oracle 的“v$sql_plan” 視圖查看)。執(zhí)行計劃描述了執(zhí)行SQL 時的所有步驟,如訪問表的順序;如何將這些表聯(lián)接在一起;以及是否通過索引來訪問這些表。優(yōu)化程序?yàn)槊總€SQL 語句設(shè)計許多可能的執(zhí)行計劃,并選出最好的一個。

            開銷模型與統(tǒng)計:Oracle 的優(yōu)化程序依賴于執(zhí)行SQL 語句的所有單個操作的開銷估算。想要優(yōu)化程序能選出最好的執(zhí)行計劃,需要最好的開銷估算方法。開銷估算需要詳細(xì)了解某些知識,這些知識包括:明白每個查詢所需的I/O、CPU 和內(nèi)存資源以及數(shù)據(jù)庫對象相關(guān)的統(tǒng)計信息(表、索引和物化視圖),還有有關(guān)硬件服務(wù)平臺的性能信息。收集這些統(tǒng)計和性能信息的過程應(yīng)高效并且高度自動化。

            動態(tài)運(yùn)行時間優(yōu)化:并不是SQL 執(zhí)行的每個方面都可以事先進(jìn)行優(yōu)化。Oracle 因此要根據(jù)當(dāng)前數(shù)據(jù)庫負(fù)載對查詢處理策略進(jìn)行動態(tài)調(diào)整。該動態(tài)優(yōu)化的目標(biāo)是獲得優(yōu)化的執(zhí)行性能,即使每個查詢可能不能夠獲得理想的CPU 或內(nèi)存資源。Oracle 另有一個原來的優(yōu)化程序,即基于規(guī)則的優(yōu)化程序。該優(yōu)化程序僅向后兼容,在Oracle 的下個版本將不再得到支持。絕大多數(shù)Oracle 用戶目前使用基于開銷的優(yōu)化程序。所有主要的應(yīng)用程序供應(yīng)商(如Oracle 應(yīng)用程序、SAP 和Peoplesoft,僅列出這幾家)以及大量近來開發(fā)的客戶應(yīng)用程序都使用基于開銷的優(yōu)化程序來獲得優(yōu)良的執(zhí)行性能,故本文僅講述基于開銷的優(yōu)化程序。

            SQL 語句轉(zhuǎn)換

            使用SQL 語句表示復(fù)雜查詢可以有多種方式。提交到數(shù)據(jù)庫的SQL 語句類型通常是最終用戶或應(yīng)用程序可以最簡單的方式生成的SQL 類型。但是這些人工編寫或機(jī)器生成的查詢公式不一定是執(zhí)行查詢最高效的SQL 語句。例如,由應(yīng)用程序生成的查詢通常含有一些無關(guān)緊要的條件,這些條件可以去掉。或者,有些從某查詢謂詞出的附加條件應(yīng)當(dāng)添加到該SQL 語句中。SQL 轉(zhuǎn)換語句的目的是將給定的SQL 語句轉(zhuǎn)換成語義相同(即返回相同結(jié)果的SQL 語句)并且性能更好的SQL 語句。

            所有的這些轉(zhuǎn)換對應(yīng)用程序及最終用戶完全透明。SQL 語句轉(zhuǎn)換在查詢優(yōu)化過程中自動實(shí)現(xiàn)。

            Oracle 實(shí)現(xiàn)了多種SQL 語句轉(zhuǎn)換。這些轉(zhuǎn)換大概可分成兩類:

            試探查詢轉(zhuǎn)換:在可能的情況下對進(jìn)來的SQL 語句都會進(jìn)行這種轉(zhuǎn)換。這種轉(zhuǎn)換能夠提供相同或較好的查詢性能,所以O(shè)racle 知道實(shí)施這種轉(zhuǎn)換不會降低執(zhí)行性能。 基于開銷的查詢轉(zhuǎn)換:Oracle 使用基于開銷的方法進(jìn)行幾類查詢轉(zhuǎn)換。借助這種方法,轉(zhuǎn)換后的查詢會與原查詢相比較,然后Oracle 的優(yōu)化程序從中選出最佳執(zhí)行策略。

            以下部分將討論Oracle 轉(zhuǎn)換技術(shù)的幾個示例。這些示例并非是權(quán)威的,僅用于幫助讀者理解該關(guān)鍵轉(zhuǎn)換技術(shù)及其益處。

            試探查詢轉(zhuǎn)換

            簡單視圖合并

            可能最簡單的查詢轉(zhuǎn)換是視圖合并。對于包含視圖的查詢,通常可以通過把視圖定義與查詢“合并”來將視圖從查詢中去掉。例如,請看下面的非常簡單的視圖及查詢。

            CREATE VIEW TEST_VIEW AS SELECT ENAME, DNAME, SAL FROM EMP E, DEPT D WHERE E.DEPTNO = D.DEPTNO;
            SELECT ENAME, DNAME FROM TEST_VIEW WHERE SAL > 10000;

            如果不加任何轉(zhuǎn)換,處理該查詢的唯一方法是將EMP 的所有行聯(lián)接到DEPT 表的所有行,然后篩選有適當(dāng)?shù)腟AL 的值的那些行。

            如果使用視圖合并,上述查詢可以轉(zhuǎn)換為:

            SELECT ENAME, DNAME FROM EMP E, DEPT D WHERE E.DEPTNO = D.DEPTNO AND E.SAL > 10000;

            處理該轉(zhuǎn)換后的查詢時,可以在聯(lián)接EMP 和DEPT 表前使用謂詞‘SAL>10000’。這一轉(zhuǎn)換由于減少了聯(lián)接的數(shù)據(jù)量而大大提高了查詢的執(zhí)行性能。即便在這樣一個非常簡單的示例里,查詢轉(zhuǎn)換的益處和重要性也顯而易見。

            復(fù)雜視圖合并

            許多視圖合并操作都是直截了當(dāng)?shù)模缫陨鲜纠5牵^復(fù)雜的視圖,如包含GROUP BY 或DISTINCT 操作符的視圖合并起來就不那么容易了。Oracle 為合并這類復(fù)雜視圖提供了一些高級技術(shù)。

            請看以下帶有GROUP BY 語句的視圖。在該示例中,視圖計算每個部門的平均工資。

            CREATE VIEW AVG_SAL_VIEW AS SELECT DEPTNO, AVG(SAL) AVG_SAL_DEPT FROM EMP GROUP BY DEPTNO

            查詢的目的是要找出Oakland 每個部門的平均工資:

            SELECT DEPT.NAME, AVG_SAL_DEPT FROM DEPT, AVG_SAL_VIEW WHERE DEPT.DEPTNO = AVG_SAL_VIEW.DEPTNO AND DEPT.LOC = 'OAKLAND'

            可以轉(zhuǎn)換為:

            SELECT DEPT.NAME, AVG(SAL) FROM DEPT, EMP WHERE DEPT.DEPTNO = EMP.DEPTNO AND DEPT.LOC = 'OAKLAND' GROUP BY DEPT.ROWID, DEPT.NAME

            該特殊轉(zhuǎn)換的執(zhí)行性能優(yōu)點(diǎn)立即顯現(xiàn):該轉(zhuǎn)換把EMP 數(shù)據(jù)在分組聚合前進(jìn)行聯(lián)接和篩選,而不是在聯(lián)接前將EMP 表的所有數(shù)據(jù)分組聚合。

            子查詢“展平”

            Oracle 有一些轉(zhuǎn)換能將不同類型的子查詢轉(zhuǎn)變?yōu)槁?lián)接、半聯(lián)接或反聯(lián)接。作為該領(lǐng)域內(nèi)的技術(shù)示例,我們來看下面這個查詢,找出有工資超過10000 的員工的那些部門:

            SELECT D.DNAME FROM DEPT D WHERE D.DEPTNO IN (SELECT E.DEPTNO FROM EMP E WHERE E.SAL > 10000)

            存在一系列可以優(yōu)化本查詢的執(zhí)行計劃。Oracle 會考慮這些可能的不同轉(zhuǎn)換,基于開銷選出最佳計劃。

            如果不進(jìn)行任何轉(zhuǎn)換,這一查詢的執(zhí)行計劃如下:

            OPERATION OBJECT_NAME OPTIONS
            SELECT STATEMENT
            FILTER
            TABLE ACCESS DEPT FULL
            TABLE ACCESS EMP FULL

            按照該執(zhí)行計劃,將掃描DEPT 表的每一行查找所有滿足子查詢條件的EMP 記錄。通常,這不是一種高效的執(zhí)行策略。然而,查詢轉(zhuǎn)換可以實(shí)現(xiàn)效率更高的計劃。

            該查詢的可能計劃之一是將查詢作為“半聯(lián)接”來執(zhí)行。“半聯(lián)接”是一種特殊類型的聯(lián)接,它消除了聯(lián)接中來自內(nèi)表的冗余值(這實(shí)際上就是該子查詢的原本的語義)。在該示例中,優(yōu)化程序選擇了一個散列半聯(lián)接,盡管Oracle 也支持排序-合并以及嵌套-循環(huán)半聯(lián)接:

            OPERATION OBJECT_NAME OPTIONS
            SELECT STATEMENT
            HASH JOIN SEMI
            TABLE ACCESS DEPT FULL
            TABLE ACCESS EMP FULL

            由于SQL 沒有用于半聯(lián)接的直接語法,此轉(zhuǎn)換過的查詢不能使用標(biāo)準(zhǔn)的SQL 來表示。但是,轉(zhuǎn)換后的偽SQL 將是:

            SELECT DNAME FROM EMP E, DEPT D WHERE D.DEPTNO E.DEPTNO AND E.SAL > 10000;

            另一個可能的計劃是優(yōu)化程序可以決定將DEPT 表作為聯(lián)接的內(nèi)表。在這種情況下,查詢作為通常的聯(lián)接來執(zhí)行,但對EMP 表進(jìn)行特別排序,以消除冗余的部門號:

            OPERATION OBJECT_NAME OPTIONS
            SELECT STATEMENT
            HASH JOIN
            SORT UNIQUE
            TABLE ACCESS EMP FULL
            TABLE ACCESS DEPT FULL

            轉(zhuǎn)換后的SQL 語句為:

            SELECT D.DNAME FROM (SELECT DISTINCT DEPTNO FROM EMP) E, DEPT D WHERE E.DEPTNO = D.DEPTNO AND E.SAL > 10000;

            與視圖合并一樣,子查詢展平也是獲得良好查詢執(zhí)行性能的基本優(yōu)化辦法。

            傳遞謂詞生成

            在某些查詢中,由于表間的聯(lián)接關(guān)系,一個表中的謂詞可以轉(zhuǎn)化為另一個表中的謂詞。Oracle 會以這種方式演繹出新的謂詞,這類謂詞被稱為傳遞謂詞。例如,來看一個查詢,找出定貨當(dāng)天運(yùn)出的所有商品:

            SELECT COUNT(DISTINCT O_ORDERKEY) FROM ORDER, LINEITEM WHERE O_ORDERKEY = L_ORDERKEY AND O_ORDERDATE = L_SHIPDATE AND O_ORDERDATE BETWEEN '1-JAN-2002' AND '31-JAN-2002'

            利用傳遞性,該ORDER 表中的謂詞也可以用于LINEITEM 表:

            SELECT COUNT(DISTINCT O_ORDERKEY) FROM ORDER, LINEITEM WHERE
          posted @ 2008-11-20 09:46 竹子 閱讀(442) | 評論 (0)編輯 收藏
            2008年9月11日
          1、父子二人經(jīng)過五星級飯店門口,看到一輛十分豪華的進(jìn)口轎車。兒子不屑地對他的父親說:「坐這種車的人,肚子里一定沒有學(xué)問!」父親則輕描淡寫地回答:「說這種話的人,口袋里一定沒有錢!」
              
              (注:你對事情的看法,是不是也反映出你內(nèi)心真正的態(tài)度?)
              
              2、晚飯后,母親和女兒一塊兒洗碗盤,父親和兒子在客廳看電視。突然,廚房里傳來打破盤子的響聲,然后一片沉寂。是兒子望著他父親,說道:「一定是媽媽打破的。」「你怎么知道?」「她沒有罵人。」
              
              (注:我們習(xí)慣以不同的標(biāo)準(zhǔn)來看人看己,以致往往是責(zé)人以嚴(yán),待己以寬。)
              
              3、有兩個臺灣觀光團(tuán)到日本伊豆半島旅游,路況很壞,到處都是坑洞。其中一位導(dǎo)游連聲抱歉,說路面簡直像麻子一樣。說而另一個導(dǎo)游卻詩意盎然地對游客說:諸位先生女士,我們現(xiàn)在走的這條道路,正是赫赫有名的伊豆迷人酒窩大道。」
              
              (注:雖是同樣的情況,然而不同的意念,就會產(chǎn)生不同的態(tài)度。思想是何等奇妙的事,如何去想,決定權(quán)在你。)
              
              4、同樣是小學(xué)三年級的學(xué)生,在作文中說他們將來的志愿是當(dāng)小丑。中國的老師斥之為:「胸?zé)o大志,孺子不可教也!」而外國的老師則會說:「愿你把歡笑帶給全世界!」
              
              (注:身為長輩的我們,不但容易要求多于鼓勵,更狹窄的界定了成功的定義。)
              
              5、在故宮博物院中,有一個太太不耐煩地對她先生說:「我說你為甚么走得這么慢。原來你老是停下來看這些東西。」
              
              (注:有人只知道在人生的道路上狂奔,結(jié)果失去了觀看兩旁美麗花朵的機(jī)會。)
              
              6、妻子正在廚房炒菜。丈夫在她旁邊一直嘮叨不停:慢些。小心!火太大了。趕快把魚翻過來。快鏟起來,油放太多了!把豆腐整平一下!「哎呀!」妻子脫口而出,「我懂得怎樣炒菜。」「你當(dāng)然懂,太太,」丈夫平靜地答道:「我只是要讓你知道,我在開車時,你在旁邊喋喋不休,我的感覺如何。」
              
              (注:學(xué)會體諒他人并不困難,只要你愿意認(rèn)真地站在對方的角度和立場看問題。)
              
              7、一輛載滿乘客的公共汽車沿著下坡路快速前進(jìn)著,有一個人後面緊緊地追趕著這輛車子。一個乘客從車窗中伸出頭來對追車子的人說:“老兄!算啦,你追不上的!”“我必須追上它,”這人氣喘吁吁地說:“我是這輛車的司機(jī)!”
              
              (注:有些人必須非常認(rèn)真努力,因?yàn)椴贿@樣的話,後果就十分悲慘了!然而也正因?yàn)楸仨毴σ愿埃瑵撛诘谋灸芎筒粸槿酥奶刭|(zhì)終將充份展現(xiàn)出來。)
              
              8、甲:「新搬來的鄰居好可惡,昨天晚上三更半夜、夜深人靜之時然跑來猛按我家的門鈴。」
              
               乙:「的確可惡!你有沒有馬上報警?」
              
               甲:「沒有。我當(dāng)他們是瘋子,繼續(xù)吹我的小喇叭。」
              
              (事出必有因,如果能先看到自己的不是,答案就會不一樣在你面對沖突和爭執(zhí)時,先想一想是否心中有虧,或許很快就能釋懷了)
              
              9、某日,張三在山間小路開車,正當(dāng)他悠哉地欣賞美麗風(fēng)景時,突然迎面開來一輛貨車,而且滿囗黑牙的司機(jī)還搖下窗戶對他大罵一聲:“豬!”
              
              張三越想越納悶,也越想越氣,於是他也搖下車窗回頭大罵:“你才是豬!”
              
              才剛罵完,他便迎頭撞上一群過馬路的豬。
              
                (不要錯誤的詮釋別人的好意,那只會讓自己吃虧,并且使別人受辱。在不明所以之前,先學(xué)會按捺情緒,耐心觀察,以免事後生發(fā)悔意。)
              
              10、小男孩問爸爸:“是不是做父親的總比做兒子的知道得多?”
              
              爸爸回答:“當(dāng)然啦!”
              
              小男孩問:“電燈是誰發(fā)明的?”
              
              爸爸:“是愛迪生。”
              
              小男孩又問:“那愛迪生的爸爸怎麼沒有發(fā)明電燈?”
              
              (很奇怪,喜歡倚老賣老的人,特別容易栽跟斗。權(quán)威往往只是一個經(jīng)不起考驗(yàn)的空殼子,尤其在現(xiàn)今這個多元開放的時代。)
              
              11、小明洗澡時不小心吞下一小塊肥皂,他的媽媽慌慌張張地打電話向家庭醫(yī)生求助。醫(yī)生說:“我現(xiàn)在還有幾個病人在,可能要半小時後才能趕過去。”
              
              小明媽媽說:“在你來之前,我該做甚麼?”
              
              醫(yī)生說:“給小明喝一杯白開水,然後用力跳一跳,你就可以讓小明用嘴巴吹泡泡消磨時間了。”
              
              (take it easy,放輕松放輕松些,生活何必太緊張?事情既然已經(jīng)發(fā)生了,何不坦然自在的面對。擔(dān)心不如寬心,窮緊張不如窮開心。)
              
              12、一把堅實(shí)的大鎖掛在大門上,一根鐵桿費(fèi)了九牛二虎之力,還是無法將它撬開。鑰匙來了,他瘦小的身子鉆進(jìn)鎖孔,只輕輕一轉(zhuǎn),大鎖就“啪”地一聲打開了。
              
              鐵桿奇怪地問:“為什麼我費(fèi)了那麼大力氣也打不開,而你卻輕而易舉地就把它打開了呢?”
              
              鑰匙說:“因?yàn)槲易盍私馑男摹?#8221; 

          問題一
          如果你家附近有一家餐廳,東西又貴又難吃,桌上還爬著蟑螂,你會因?yàn)樗芙芊奖悖鸵欢佟⒃俣毓馀R嗎?
          回答:你一定會說,這是什么爛問題,誰那么笨,花錢買罪受?
          可同樣的情況換個場合,自己或許就做類似的蠢事。不少男女都曾經(jīng)抱怨過他們的情人或配偶品性不端,三心二意,不負(fù)責(zé)任。明知在一起沒什么好的結(jié)果,怨恨已經(jīng)比愛還多,但卻不知道為什么還是要和他攪和下去,分不了手。說穿了,只是為了不甘,為了習(xí)慣,這不也和光臨餐廳一樣?
          ――做人,為什么要過于執(zhí)著?!
          問題二
          如果你不小心丟掉100塊錢,只知道它好像丟在某個你走過的地方,你會花200塊錢的車費(fèi)去把那100塊找回來嗎?
          回答:一個超級愚蠢的問題。
          可是,相似的事情卻在人生中不斷發(fā)生。做錯了一件事,明知自己有問題,卻死也不肯認(rèn)錯,反而花加倍的時間來找藉口,讓別人對自己的印象大打折扣。被人罵了一句話,卻花了無數(shù)時間難過,道理相同。為一件事情發(fā)火,不惜損人不利已,不惜血本,不惜時間,只為報復(fù),不也一樣無聊?
          失去一個人的感情,明知一切已無法挽回,卻還是那么傷心,而且一傷心就是好幾年,還要借酒澆愁,形銷骨立。其實(shí)這樣一點(diǎn)用也沒有,只是損失更多。
          ――做人,干嗎為難自己?!
          問題三
          你會因?yàn)榇蜷_報紙發(fā)現(xiàn)每天都有車禍,就不敢出門嗎?
          回答:這是個什么爛問題?當(dāng)然不會,那叫因噎廢食。
          然而,有不少人卻曾說:現(xiàn)在的離婚率那么高,讓我都不敢談戀愛了。說得還挺理所當(dāng)然。也有不少女人看到有關(guān)的諸多報道,就對自己的另一半憂心忡忡,這不也是類似的反應(yīng)?
          所謂樂觀,就是得相信:雖然道路多艱險,我還是那個會平安過馬路的人,只要我小心一點(diǎn),不必害怕過馬路。
          ――做人,先要相信自己。
          問題四
          你相信每個人隨便都可以成功立業(yè)嗎?
          回答:當(dāng)然不會相信。
          但據(jù)觀察,有人總是在聽完成功人士絞盡腦汁的建議,比如說,多讀書,多練習(xí)之后,問了另一個問題?那不是很難?
          我們都想在3分鐘內(nèi)學(xué)好英文,在5分鐘內(nèi)解決所有難題,難道成功是那么容易的嗎?改變當(dāng)然是難的。成功只因不怕困難,所以才能出類拔萃。
          有一次坐在出租車上,聽見司機(jī)看到自己前后都是高檔車,兀自感嘆:唉,為什么別人那么有錢,我的錢這么難賺?我心血來潮,問他:你認(rèn)為世上有什么錢是好賺的?他答不出來,過了半晌才說:好像都是別人的錢比較好賺。
          其實(shí)任何一個成功者都是艱辛取得。我們實(shí)在不該抱怨命運(yùn)。
          ――做人,依靠自己!
          問題五
          你認(rèn)為完全沒有打過籃球的人,可以當(dāng)很好的籃球教練嗎?
          回答:當(dāng)然不可能,外行不可能領(lǐng)導(dǎo)內(nèi)行。
          可是,有許多人,對某個行業(yè)完全不了解,只聽到那個行業(yè)好賺錢,就馬上開起業(yè)來了。
          我看過對穿著沒有任何口味、或根本不在乎穿著的人,夢想?yún)s是開間服裝店;不知道電腦怎么開機(jī)的人,卻想在網(wǎng)上賺錢,結(jié)果道聽途說,卻不反省自己是否專業(yè)能力不足,只抱怨時不我與。
          ――做人,量力而行。
          問題六
          相似但不相同的問題:你是否認(rèn)為,籃球教練不上籃球場,閉著眼睛也可以主導(dǎo)一場完美的利?
          回答:有病啊,當(dāng)然是不可能的。
          可是卻有不少朋友,自己沒有時間打理,卻拼命投資去開咖啡館,開餐廳,開自己根本不懂的公司,火燒一樣急著把辛苦積攢的積蓄花掉,去當(dāng)一個稀里糊涂的投資人。虧的總是比賺的多,卻覺得自己是因?yàn)檫\(yùn)氣不好,而不是想法出了問題。
          ――做人,記得反省自己。
          問題七
          你寧可永遠(yuǎn)后悔,也不愿意試一試自己能否轉(zhuǎn)敗為勝?
          解答:恐怕沒有人會說:對,我就是這樣的孬種吧。
          然而,我們卻常常在不該打退堂鼓時拼命打退堂鼓,為了恐懼失敗而不敢嘗試成功。
          以關(guān)穎珊贏得2000年世界花樣滑冰冠軍時的精彩表現(xiàn)為例:她一心想贏得第一名,然而在最后一場比賽前,她的總積分只排名第三位,在最后的自選曲項(xiàng)目上,她選擇了突破,而不是少出錯。在4分鐘的長曲中,結(jié)合了最高難度的三周跳,并且還大膽地連跳了兩次。她也可能會敗得很難看,但是她畢竟成功了。
          她說:因?yàn)槲也幌氲鹊绞。藕蠡谧约哼€有潛力沒發(fā)揮。
          一個中國偉人曾說;勝利的希望和有利情況的恢復(fù),往往產(chǎn)生于再堅持一下的努力之中。
          ――做人,何妨放手一搏。
          問題八
          你的時間無限,長生不老,所以最想做的事,應(yīng)該無限延期?
          回答:不,傻瓜才會這樣認(rèn)為。
          然而我們卻常說,等我老了,要去環(huán)游世界;等我退休,就要去做想做的事情;等孩子長大了,我就可以……
          我們都以為自己有無限的時間與精力。其實(shí)我們可以一步一步實(shí)現(xiàn)理想,不必在等待中徒耗生命。如果現(xiàn)在就能一步一步努力接近,我們就不會活了半生,卻出現(xiàn)自己最不想看到的結(jié)局。
          ――做人,要活在當(dāng)下。

          posted @ 2008-09-11 21:01 竹子 閱讀(184) | 評論 (0)編輯 收藏
            2008年9月8日

           
          做為一名大四的學(xué)生,我面試過不少的單位,有成功的也有失敗的,但是對我來說所有的失敗在某種意義上都是一種成功,特別是我下面寫的這些,寫這篇文章的時候,我已經(jīng)簽了南京的一家軟件公司,但是想起今年2月21日我面試蘇州TW的IT公司的經(jīng)歷聯(lián)想到我們現(xiàn)在學(xué)習(xí)編程的一些情況我真的深有感觸,這次面試使我深深的體會到了失敗但也收獲了很多。我要說的將分成三部分,1.是我面試的具體經(jīng)過2.是由面試想到的3.現(xiàn)今我應(yīng)該做的。當(dāng)然這些話很大程度上是我個人的意見,不可能完全得到大家的贊同,所以在某些觀點(diǎn)上如果哪位朋友覺得跟我的有很大出入,請不要介意,也不要對我攻擊,就當(dāng)我沒有說過,歡迎和我聯(lián)系共同探討這些問題!我的E-Mail:fvifnmmmm@126.com

          1.面試經(jīng)過


            大約在年前我接到了TW瑞晟(Realtek)蘇州公司的面試通知,通知我2月21日到蘇州工業(yè)園區(qū)面試,接到面試后的幾天我把一些專業(yè)課溫習(xí)了一遍,特別是C++和數(shù)據(jù)結(jié)構(gòu),由于大學(xué)幾年里,我一直專研這些方面,加上通過了高級程序員的考試,對于一些常用的算法我差不多也達(dá)到了爛熟于胸的地步,當(dāng)時的感覺是如果問了我這些方面的問題我應(yīng)該是沒有問題的!

            21日那天我被安排在4:30面試,由一位技術(shù)人員單獨(dú)給我面試,在問了一些簡單的問題之后他給我出了一道編程題目,題目是這樣的:

            (由于具體面試的題目比較煩瑣,我將其核心思想提取出來分解成了兩個獨(dú)立的簡單的問題,有可能問題分解的不當(dāng),請大家見諒,實(shí)際面試了一個的問題但比其復(fù)雜很多,而且涉及一些高等數(shù)學(xué)變換) 

            1) 寫一個函數(shù)計算當(dāng)參數(shù)為n(n很大)時的值 1-2+3-4+5-6+7......+n 

            哼,我的心里冷笑一聲!沒想到這么簡單,我有點(diǎn)緊張的心情頓時放松起來!

            于是很快我給出我的解法: 

          long fn(long n) 

          long temp=0; 
          int i,flag=1; 
          if(n<=0) 

          printf("error: n must > 0); 
          exit(1); 

          for(i=1;i<=n;i++) 

          temp=temp+flag*i; 
          flag=(-1)*flag; 

          return temp; 

            搞定!當(dāng)我用期待的目光看著面試官的時候,他微笑著跟我說,執(zhí)行結(jié)果肯定是沒有問題!但當(dāng)n很大的時候我這個程序執(zhí)行效率很低, 在嵌入式系統(tǒng)的開發(fā)中,程序的運(yùn)行效率很重要,能讓CPU少執(zhí)行一條指令都是好的,他讓我看看這個程序還有什么可以修改的地方,把程序優(yōu)化一下!聽了這些話,我的心情當(dāng)時變的有點(diǎn)沉重,沒想到他的要求很嚴(yán)格,之后我對程序進(jìn)行了嚴(yán)格的分析,給出了改進(jìn)了的方案! 

          long fn(long n) 

          long temp=0; 
          int j=1,i=1,flag=1; 
          if(n<=0) 

          printf("error: n must > 0); 
          exit(1); 

          while(j<=n) 

          temp=temp+i; 
          i=-i; 
          i>0?i++:i--; 
          j++; 

          return temp; 

            雖然我不敢保證我這個算法是最優(yōu)的,但是比起上一個程序,我將所有涉及到乘法指令的語句改為執(zhí)行加法指令,既達(dá)到要題目的要求而且運(yùn)算時間上縮短了很多!而代價僅僅是增加了一個整型變量!但是我現(xiàn)在的信心已經(jīng)受了一點(diǎn)打擊,我將信將疑的看者面試官,他還是微笑著跟我說:“不錯,這個程序確實(shí)在效率上有的很大的提高!”我心里一陣暗喜!但他接著說這個程序仍然不能達(dá)到他的要求,要我給出更優(yōu)的方案!天啊!還有優(yōu)化!我當(dāng)時真的有點(diǎn)崩潰了,想了一會后,我請求他給出他的方案!然后他很爽快的給出了他的程序!
          long fn(long n) 

          if(n<=0) 

          printf("error: n must > 0); 
          exit(1); 

          if(0==n%2) 
          return (n/2)*(-1); 
          else 
          return (n/2)*(-1)+n; 

            搞笑,當(dāng)時我目瞪口呆,沒想到他是這個意思,這么簡單的代碼我真的不會寫嗎,但是我為什么沒有往那方面上想呢!他說的沒有錯,在n很大很大的時候這三個程序運(yùn)行時間的差別簡直是天壤之別!當(dāng)我剛想開口說點(diǎn)什么的時候,他卻先開口了:“不要認(rèn)為CPU運(yùn)算速度快就把所有的問題都推給它去做,程序員應(yīng)該將代碼優(yōu)化再優(yōu)化,我們自己能做的決不要讓CPU做,因?yàn)镃PU是為用戶服務(wù)的,不是為我們程序員服務(wù)的!”多么精辟的語言,我已經(jīng)不想再說什么了!接著是第二個問題: 

            2),他要求我用一種技巧性的編程方法來用一個函數(shù)實(shí)現(xiàn)兩個函數(shù)的功能n為如:

          fn1(n)=n/2!+n/3!+n/4!+n/5!+n/6! 

          fn2(n)=n/5!+n/6!+n/7!+n/8!+n/9! 

            現(xiàn)在用一個函數(shù)fn(int n,int flag)實(shí)現(xiàn),當(dāng)flag為0時,實(shí)現(xiàn)fn1功能,如果flag為1時實(shí)現(xiàn)fn2功能!

            他的要求還是效率,效率,效率!說實(shí)在話,如果我心情好的話我應(yīng)該能給出一種比較好的算法,但我那時真的沒有什么心思再想了,我在紙上胡亂畫了一些諸如6!=6*5!的公式后直截了當(dāng)?shù)母f要他給出他的答案!面試官也沒有說什么,給出了他的思路:

            定義一個二維數(shù)組 float t[2][5]存入[2!,3!,4!,5!,6!},{5!,6!,7!,8!,9!]然后給出一個循環(huán): 

          for(i=0;i<6;i++) 

          temp=temp+n/t[flag]; 

            最后得到計算值!呵呵,典型的空間換時間的算法!

            這些總共花了50分鐘的時間,還有十分鐘我就跟他很隨意的聊聊天,聊了一些編程以及生活的問題,那時的我已經(jīng)很放松了,因?yàn)槲抑肋@次面試結(jié)果只有一個:失敗。5:30的時候面試官要我等通知,于是我離開了他們公司。這就是面試的整個經(jīng)過! 

          .由面試想到的 

            真的是很失敗啊!我記得那天下好大的雨,氣溫也很低,我邊走邊想,從5:30一直走到7:30,全身都濕透了,又冷又餓,但是我只是一直走,腦子里面充滿了疑惑,我也想讓雨把自己淋醒!看到這里有些朋友可能覺得那些面試題目不算什么如果讓自己做的話肯定能全部答對,我肯定相信你,因?yàn)槲覐奈磻岩蛇^中國程序員的能力,我認(rèn)為中國有世界上最好的程序員,我也從未認(rèn)為自己是高手,所以我做不出來不代表中國程序員比TW或者別的地方的程序員差,所以我就從我的角度,我的所見所想來談一些感想: 

            不錯全世界都有優(yōu)秀的程序員,中國也不例外,但是我疑惑的是:到底中國和TW或者國外的優(yōu)秀的程序員的比例到底是多少?TW我不知道,中國100個程序員里有幾個是優(yōu)秀的呢?我根本算不上,從上面的表現(xiàn)就足以說明一切了!是1個?5個?10個?50個?這個數(shù)字我不敢亂猜,恐遭網(wǎng)友一頓痛罵,那么我們國內(nèi)有多少人學(xué)習(xí)計算機(jī)呢?拿我們學(xué)校來說,計算機(jī)97級4個班,98級5個班,99級10個班,2000級17個班,人多了,老師怎么辦?我們學(xué)校的做法是讓研究生上課,然后呢?補(bǔ)考一抓一大把,大把大把的補(bǔ)考費(fèi)落入了學(xué)校的口袋,還說現(xiàn)在的學(xué)生素質(zhì)低!真是好笑,我都不知道學(xué)校這么做是為了什么,為國內(nèi)培養(yǎng)大量的程序員嗎?學(xué)生們能真正學(xué)到計算機(jī)知識嗎?好了,我敢講,在我們學(xué)校學(xué)習(xí)編程學(xué)生和優(yōu)秀程序員(注意我指的是優(yōu)秀,只會編幾個糟爛程序的人算不上)的比例應(yīng)該是100:0.1 。在這種比例下雖然我們中國學(xué)習(xí)編程的人鋪天蓋地,但是想想有多少個人能真正為中國軟件業(yè)發(fā)展作出貢獻(xiàn),有多少人能真正寫出優(yōu)秀的程序名揚(yáng)海外! 

            我從學(xué)習(xí)編程以來,不管是自學(xué)還是老師指導(dǎo),從來都是解決問題就好,編出程序來就行,我的疑惑是:我們有真正的強(qiáng)調(diào)過程序的效率,程序的質(zhì)量嗎?我們有仔細(xì)分析過我們寫的東西,看看有沒有可以改進(jìn)的地方,看看有沒有簡單的方法來達(dá)到同樣的目的呢?我問心自問,我發(fā)現(xiàn),我從來沒有對我寫出來的程序進(jìn)行過優(yōu)化,最多就是進(jìn)行詳細(xì)的測試,然后Debug,但是這就足夠了嗎?這些天我偶爾發(fā)現(xiàn)我曾經(jīng)寫過的一個游戲,那是一年前我剛加入www.vcroad.net做為其中一員時候,感覺應(yīng)該拿點(diǎn)東西出來,然后花了一個星期的時間寫出來的!程序不算復(fù)雜,但是用到了不少數(shù)據(jù)結(jié)構(gòu)的東西,也用到了一些精彩的算法,加上windows的界面和游戲的可玩性,寫完后受到了不少好評,我當(dāng)時真的很佩服自己!但是現(xiàn)在看呢:沒有一句注釋,好多丑陋的函數(shù)名比如:void chushihua(),好多沒有必要的變量,可以用簡單語句完成工作的我使用華麗的算法,大量使用全局變量.....,說不好聽的話,六百多行的程序除了能運(yùn)行之外就是一陀屎!如果一年前我能聽到一些反面意見的話,大概我能早一點(diǎn)覺悟,但是自從原代碼在網(wǎng)站發(fā)布以來聽到的都是贊美之詞,沒有一個人向我提出程序改進(jìn)的意見,這又說明了一個什么問題呢?很值得思考啊!

          還有一個疑惑是:我們說的和做的真的一樣嗎?我在學(xué)校的時候曾經(jīng)受學(xué)院指派承辦過一個計算機(jī)大賽,請了一個老師出決賽的題目,主要是一些算法題目,這個老師可能是我上大學(xué)以來唯一敬佩的老師了,從程序調(diào)試到打分,對于每個程序都仔細(xì)分析其時間效率和空間效率,然后綜合打分,四十個人的卷子,老師從下午三點(diǎn)一直調(diào)試到晚上十點(diǎn),在有些寫的精彩的語句后還加上批注。我真是高興很遇到這樣的老師并且和他做深入的交流,但在事后,卻發(fā)生了一件不愉快的事,在比賽中獲得第二名的學(xué)生找到我,說他程序全部調(diào)試成功應(yīng)該給他滿分,并且應(yīng)該得第一,我說不過他,最后調(diào)出了他的原程序和第一名的原程序?qū)Ρ龋诲e,兩個程序都運(yùn)行的很好,這時,那個同學(xué)開口了:“我的程序?qū)懙氖趾喗菝髁耍瑑H僅數(shù)行就完成了題目要求,而他的卻寫了一大堆,為什么給他的分多過給我的分。”我當(dāng)時很是氣憤,如果不是老師負(fù)責(zé)的話,那么現(xiàn)在第一名和第二名的位置真的要互調(diào)了,拜托,不是程序的行數(shù)越少程序的質(zhì)量就越高,我記得我跟他大談這方面的道理,最后說服他了!哈哈,但是我,只能說說而已,我不知道還有多少人一樣,說起來頭頭是道,但心里卻壓根就從未重視過它! 

          .我打算做的! 
            其實(shí)那天我想到的遠(yuǎn)不止上面那么多,但是我不想再說了,因?yàn)槲也孪肟催@篇文章的網(wǎng)友大概都有一肚子的感想,一肚子的抱怨,借用這篇文章發(fā)泄可不是我想達(dá)到的目的,在上面我把自己罵的一文不值也不是妄自菲薄,但是在某些方面我真的做錯了,或者說是偏離了正確方向,現(xiàn)在是矯正方向和重整旗鼓的時候了,就象我前面說過的,我相信中國有世界上最好的程序員,我也相信我的水平不會一直保持現(xiàn)狀,我現(xiàn)在就收拾起牢騷真正的實(shí)干起來! 
          真的很巧,就寫到這里的時候我在網(wǎng)上偶爾發(fā)現(xiàn)了這篇手冊,我不知道這預(yù)示著什么,但是我想如果我照下面這個基本原則一直踏實(shí)做下去,我一定會實(shí)現(xiàn)我的理想---一名優(yōu)秀的軟件設(shè)計師! 

            (下面這些文字不是我的原創(chuàng),是我偶爾在網(wǎng)上發(fā)現(xiàn)的,我真的很幸運(yùn)能看到這些,這篇文章也隨著下面的文字而結(jié)束,我真心的希望您能從這篇文章中得到啟發(fā),這篇文章歡迎大家隨意轉(zhuǎn)載,您可以不寫作者是誰,但是請您寫上www.vcroad.net原創(chuàng),謝謝您的支持) 

            作者:金蝶中間件公司CTO袁紅崗 

            不知不覺做軟件已經(jīng)做了十年,有成功的喜悅,也有失敗的痛苦,但總不敢稱自己是高手,因?yàn)楹臀倚哪恐姓嬲母呤謧儽绕饋恚€差的太遠(yuǎn)。世界上并沒有成為高手的捷徑,但一些基本原則是可以遵循的。 

            1. 扎實(shí)的基礎(chǔ)。數(shù)據(jù)結(jié)構(gòu)、離散數(shù)學(xué)、編譯原理,這些是所有計算機(jī)科學(xué)的基礎(chǔ),如果不掌握他們,很難寫出高水平的程序。據(jù)我的觀察,學(xué)計算機(jī)專業(yè)的人比學(xué)其他專業(yè)的人更能寫出高質(zhì)量的軟件。程序人人都會寫,但當(dāng)你發(fā)現(xiàn)寫到一定程度很難再提高的時候,就應(yīng)該想想是不是要回過頭來學(xué)學(xué)這些最基本的理論。不要一開始就去學(xué)OOP,即使你再精通OOP,遇到一些基本算法的時候可能也會束手無策。 

            2. 豐富的想象力。不要拘泥于固定的思維方式,遇到問題的時候要多想幾種解決問題的方案,試試別人從沒想過的方法。豐富的想象力是建立在豐富的知識的基礎(chǔ)上,除計算機(jī)以外,多涉獵其他的學(xué)科,比如天文、物理、數(shù)學(xué)等等。另外,多看科幻電影也是一個很好的途徑。 

            3. 最簡單的是最好的。這也許是所有科學(xué)都遵循的一條準(zhǔn)則,如此復(fù)雜的質(zhì)能互換原理在愛因斯坦眼里不過是一個簡單得不能再簡單的公式:E=mc2。簡單的方法更容易被人理解,更容易實(shí)現(xiàn),也更容易維護(hù)。遇到問題時要優(yōu)先考慮最簡單的方案,只有簡單方案不能滿足要求時再考慮復(fù)雜的方案。 

            4. 不鉆牛角尖。當(dāng)你遇到障礙的時候,不妨?xí)簳r遠(yuǎn)離電腦,看看窗外的風(fēng)景,聽聽輕音樂,和朋友聊聊天。當(dāng)我遇到難題的時候會去玩游戲,而且是那種極暴力的打斗類游戲,當(dāng)負(fù)責(zé)游戲的那部分大腦細(xì)胞極度亢奮的時候,負(fù)責(zé)編程的那部分大腦細(xì)胞就得到了充分的休息。當(dāng)重新開始工作的時候,我會發(fā)現(xiàn)那些難題現(xiàn)在竟然可以迎刃而解。 

            5. 對答案的渴求。人類自然科學(xué)的發(fā)展史就是一個渴求得到答案的過程,即使只能知道答案的一小部分也值得我們?nèi)ジ冻觥V灰銏远ㄐ拍睿欢ㄒ业絾栴}的答案,你才會付出精力去探索,即使最后沒有得到答案,在過程中你也會學(xué)到很多東西。 

            6. 多與別人交流。三人行必有我?guī)煟苍S在一次和別人不經(jīng)意的談話中,就可以迸出靈感的火花。多上上網(wǎng),看看別人對同一問題的看法,會給你很大的啟發(fā)。 

            7. 良好的編程風(fēng)格。注意養(yǎng)成良好的習(xí)慣,代碼的縮進(jìn)編排,變量的命名規(guī)則要始終保持一致。大家都知道如何排除代碼中錯誤,卻往往忽視了對注釋的排錯。注釋是程序的一個重要組成部分,它可以使你的代碼更容易理解,而如果代碼已經(jīng)清楚地表達(dá)了你的思想,就不必再加注釋了,如果注釋和代碼不一致,那就更加糟糕。 

            8. 韌性和毅力。這也許是"高手"和一般程序員最大的區(qū)別。A good programming is 99 weat and 1?ffee。高手們并不是天才,他們是在無數(shù)個日日夜夜中磨練出來的。成功能給我們帶來無比的喜悅,但過程卻是無比的枯燥乏味。你不妨做個測試,找個10000以內(nèi)的素數(shù)表,把它們?nèi)汲聛恚缓笤贆z查三遍,如果能夠不間斷地完成這一工作,你就可以滿足這一條。

           

          posted @ 2008-09-08 13:32 竹子 閱讀(188) | 評論 (0)編輯 收藏
            2008年8月21日

          一、引言

            Java虛擬機(jī)(JVM)的類裝載就是指將包含在類文件中的字節(jié)碼裝載到JVM中, 并使其成為JVM一部分的過程。JVM的類動態(tài)裝載技術(shù)能夠在運(yùn)行時刻動態(tài)地加載或者替換系統(tǒng)的某些功能模塊, 而不影響系統(tǒng)其他功能模塊的正常運(yùn)行。本文將分析JVM中的類裝載系統(tǒng),探討JVM中類裝載的原理、實(shí)現(xiàn)以及應(yīng)用。

            二、Java虛擬機(jī)的類裝載實(shí)現(xiàn)與應(yīng)用

            2.1 裝載過程簡介

            所謂裝載就是尋找一個類或是一個接口的二進(jìn)制形式并用該二進(jìn)制形式來構(gòu)造代表這個類或是這個接口的class對象的過程,其中類或接口的名稱是給定了的。當(dāng)然名稱也可以通過計算得到,但是更常見的是通過搜索源代碼經(jīng)過編譯器編譯后所得到的二進(jìn)制形式來構(gòu)造。

            在Java中,類裝載器把一個類裝入Java虛擬機(jī)中,要經(jīng)過三個步驟來完成:裝載、鏈接和初始化,其中鏈接又可以分成校驗(yàn)、準(zhǔn)備和解析三步,除了解析外,其它步驟是嚴(yán)格按照順序完成的,各個步驟的主要工作如下:

            裝載:查找和導(dǎo)入類或接口的二進(jìn)制數(shù)據(jù);
            鏈接:執(zhí)行下面的校驗(yàn)、準(zhǔn)備和解析步驟,其中解析步驟是可以選擇的;
            校驗(yàn):檢查導(dǎo)入類或接口的二進(jìn)制數(shù)據(jù)的正確性;
            準(zhǔn)備:給類的靜態(tài)變量分配并初始化存儲空間;
            解析:將符號引用轉(zhuǎn)成直接引用;
            初始化:激活類的靜態(tài)變量的初始化Java代碼和靜態(tài)Java代碼塊。

            至于在類裝載和虛擬機(jī)啟動的過程中的具體細(xì)節(jié)和可能會拋出的錯誤,請參看《Java虛擬機(jī)規(guī)范》以及《深入Java虛擬機(jī)》,它們在網(wǎng)絡(luò)上面的資源地址是:
            
          http://java.sun.com/docs/books/vmspec/2nd-edition/html/Preface.doc.html
            
          http://www.artima.com/insidejvm/ed2/index.html
            由于本文的討論重點(diǎn)不在此就不再多敘述。

            2.2 裝載的實(shí)現(xiàn)

            JVM中類的裝載是由ClassLoader和它的子類來實(shí)現(xiàn)的,Java ClassLoader 是一個重要的Java運(yùn)行時系統(tǒng)組件。它負(fù)責(zé)在運(yùn)行時查找和裝入類文件的類。

            在Java中,ClassLoader是一個抽象類,它在包java.lang中,可以這樣說,只要了解了在ClassLoader中的一些重要的方法,再結(jié)合上面所介紹的JVM中類裝載的具體的過程,對動態(tài)裝載類這項(xiàng)技術(shù)就有了一個比較大概的掌握,這些重要的方法包括以下幾個:

            ①loadCass方法 loadClass(String name ,boolean resolve)其中name參數(shù)指定了JVM需要的類的名稱,該名稱以包表示法表示,如Java.lang.Object;resolve參數(shù)告訴方法是否需要解析類,在初始化類之前,應(yīng)考慮類解析,并不是所有的類都需要解析,如果JVM只需要知道該類是否存在或找出該類的超類,那么就不需要解析。這個方法是ClassLoader 的入口點(diǎn)。

            ②defineClass方法 這個方法接受類文件的字節(jié)數(shù)組并把它轉(zhuǎn)換成Class對象。字節(jié)數(shù)組可以是從本地文件系統(tǒng)或網(wǎng)絡(luò)裝入的數(shù)據(jù)。它把字節(jié)碼分析成運(yùn)行時數(shù)據(jù)結(jié)構(gòu)、校驗(yàn)有效性等等。

            ③findSystemClass方法 findSystemClass方法從本地文件系統(tǒng)裝入文件。它在本地文件系統(tǒng)中尋找類文件,如果存在,就使用defineClass將字節(jié)數(shù)組轉(zhuǎn)換成Class對象,以將該文件轉(zhuǎn)換成類。當(dāng)運(yùn)行Java應(yīng)用程序時,這是JVM 正常裝入類的缺省機(jī)制。

            ④resolveClass方法 resolveClass(Class c)方法解析裝入的類,如果該類已經(jīng)被解析過那么將不做處理。當(dāng)調(diào)用loadClass方法時,通過它的resolve 參數(shù)決定是否要進(jìn)行解析。

            ⑤findLoadedClass方法 當(dāng)調(diào)用loadClass方法裝入類時,調(diào)用findLoadedClass 方法來查看ClassLoader是否已裝入這個類,如果已裝入,那么返回Class對象,否則返回NULL。如果強(qiáng)行裝載已存在的類,將會拋出鏈接錯誤。

            2.3 裝載的應(yīng)用

            一般來說,我們使用虛擬機(jī)的類裝載時需要繼承抽象類java.lang.ClassLoader,其中必須實(shí)現(xiàn)的方法是loadClass(),對于這個方法需要實(shí)現(xiàn)如下操作:(1) 確認(rèn)類的名稱;(2) 檢查請求要裝載的類是否已經(jīng)被裝載;(3) 檢查請求加載的類是否是系統(tǒng)類;(4) 嘗試從類裝載器的存儲區(qū)獲取所請求的類;(5) 在虛擬機(jī)中定義所請求的類;(6) 解析所請求的類;(7) 返回所請求的類。

            所有的Java 虛擬機(jī)都包括一個內(nèi)置的類裝載器,這個內(nèi)置的類庫裝載器被稱為根裝載器(bootstrap ClassLoader)。根裝載器的特殊之處是它只能夠裝載在設(shè)計時刻已知的類,因此虛擬機(jī)假定由根裝載器所裝載的類都是安全的、可信任的,可以不經(jīng)過安全認(rèn)證而直接運(yùn)行。當(dāng)應(yīng)用程序需要加載并不是設(shè)計時就知道的類時,必須使用用戶自定義的裝載器(user-defined ClassLoader)。下面我們舉例說明它的應(yīng)用。

            public abstract class MultiClassLoader extends ClassLoader{
            ...
            public synchronized Class loadClass(String s, boolean flag)
            throws ClassNotFoundException
            {
            /* 檢查類s是否已經(jīng)在本地內(nèi)存*/
            Class class1 = (Class)classes.get(s);

            /* 類s已經(jīng)在本地內(nèi)存*/
            if(class1 != null) return class1;
            try/*用默認(rèn)的ClassLoader 裝入類*/ {
            class1 = super.findSystemClass(s);
            return class1;
            }
            catch(ClassNotFoundException _ex) {
            System.out.println(">> Not a system class.");
            }

            /* 取得類s的字節(jié)數(shù)組*/
            byte abyte0[] = loadClassBytes(s);
            if(abyte0 == null) throw new ClassNotFoundException();

            /* 將類字節(jié)數(shù)組轉(zhuǎn)換為類*/
            class1 = defineClass(null, abyte0, 0, abyte0.length);
            if(class1 == null) throw new ClassFormatError();
            if(flag) resolveClass(class1); /*解析類*/

            /* 將新加載的類放入本地內(nèi)存*/
            classes.put(s, class1);
            System.out.println(">> Returning newly loaded class.");

            /* 返回已裝載、解析的類*/
            return class1;
            }
            ...
            }
          三、Java虛擬機(jī)的類裝載原理

            前面我們已經(jīng)知道,一個Java應(yīng)用程序使用兩種類型的類裝載器:根裝載器(bootstrap)和用戶定義的裝載器(user-defined)。根裝載器是Java虛擬機(jī)實(shí)現(xiàn)的一部分,舉個例子來說,如果一個Java虛擬機(jī)是在現(xiàn)在已經(jīng)存在并且正在被使用的操作系統(tǒng)的頂部用C程序來實(shí)現(xiàn)的,那么根裝載器將是那些C程序的一部分。根裝載器以某種默認(rèn)的方式將類裝入,包括那些Java API的類。在運(yùn)行期間一個Java程序能安裝用戶自己定義的類裝載器。根裝載器是虛擬機(jī)固有的一部分,而用戶定義的類裝載器則不是,它是用Java語言寫的,被編譯成class文件之后然后再被裝入到虛擬機(jī),并像其它的任何對象一樣可以被實(shí)例化。 Java類裝載器的體系結(jié)構(gòu)如下所示:
            
            Java的類裝載模型是一種代理(delegation)模型。當(dāng)JVM 要求類裝載器CL(ClassLoader)裝載一個類時,CL首先將這個類裝載請求轉(zhuǎn)發(fā)給他的父裝載器。只有當(dāng)父裝載器沒有裝載并無法裝載這個類時,CL才獲得裝載這個類的機(jī)會。這樣, 所有類裝載器的代理關(guān)系構(gòu)成了一種樹狀的關(guān)系。樹的根是類的根裝載器(bootstrap ClassLoader) , 在JVM 中它以"null"表示。除根裝載器以外的類裝載器有且僅有一個父裝載器。在創(chuàng)建一個裝載器時, 如果沒有顯式地給出父裝載器, 那么JVM將默認(rèn)系統(tǒng)裝載器為其父裝載器。Java的基本類裝載器代理結(jié)構(gòu)如圖2所示:
          下面針對各種類裝載器分別進(jìn)行詳細(xì)的說明。
          根(Bootstrap) 裝載器:該裝載器沒有父裝載器,它是JVM實(shí)現(xiàn)的一部分,從sun.boot.class.path裝載運(yùn)行時庫的核心代碼。
          擴(kuò)展(Extension) 裝載器:繼承的父裝載器為根裝載器,不像根裝載器可能與運(yùn)行時的操作系統(tǒng)有關(guān),這個類裝載器是用純Java代碼實(shí)現(xiàn)的,它從java.ext.dirs (擴(kuò)展目錄)中裝載代碼。
            系統(tǒng)(System or Application) 裝載器:裝載器為擴(kuò)展裝載器,我們都知道在安裝JDK的時候要設(shè)置環(huán)境變量(CLASSPATH ),這個類裝載器就是從java.class.path(CLASSPATH 環(huán)境變量)中裝載代碼的,它也是用純Java代碼實(shí)現(xiàn)的,同時還是用戶自定義類裝載器的缺省父裝載器。

            小應(yīng)用程序(Applet) 裝載器: 裝載器為系統(tǒng)裝載器,它從用戶指定的網(wǎng)絡(luò)上的特定目錄裝載小應(yīng)用程序代碼。

            在設(shè)計一個類裝載器的時候,應(yīng)該滿足以下兩個條件:

            對于相同的類名,類裝載器所返回的對象應(yīng)該是同一個類對象

            如果類裝載器CL1將裝載類C的請求轉(zhuǎn)給類裝載器CL2,那么對于以下的類或接口,CL1和CL2應(yīng)該返回同一個類對象:a)S為C的直接超類;b)S為C的直接超接口;c)S為C的成員變量的類型;d)S為C的成員方法或構(gòu)建器的參數(shù)類型;e)S為C的成員方法的返回類型。
            每個已經(jīng)裝載到JVM中的類都隱式含有裝載它的類裝載器的信息。類方法getClassLoader 可以得到裝載這個類的類裝載器。一個類裝載器認(rèn)識的類包括它的父裝載器認(rèn)識的類和它自己裝載的類,可見類裝載器認(rèn)識的類是它自己裝載的類的超集。注意我們可以得到類裝載器的有關(guān)的信息,但是已經(jīng)裝載到JVM中的類是不能更改它的類裝載器的。

            Java中的類的裝載過程也就是代理裝載的過程。比如:Web瀏覽器中的JVM需要裝載一個小應(yīng)用程序TestApplet。JVM調(diào)用小應(yīng)用程序裝載器ACL(Applet ClassLoader)來完成裝載。ACL首先請求它的父裝載器, 即系統(tǒng)裝載器裝載TestApplet是否裝載了這個類, 由于TestApplet不在系統(tǒng)裝載器的裝載路徑中, 所以系統(tǒng)裝載器沒有找到這個類, 也就沒有裝載成功。接著ACL自己裝載TestApplet。ACL通過網(wǎng)絡(luò)成功地找到了TestApplet.class 文件并將它導(dǎo)入到了JVM中。在裝載過程中, JVM發(fā)現(xiàn)TestAppet是從超類java.applet.Applet繼承的。所以JVM再次調(diào)用ACL來裝載java.applet.Applet類。ACL又再次按上面的順序裝載Applet類, 結(jié)果ACL發(fā)現(xiàn)他的父裝載器已經(jīng)裝載了這個類, 所以ACL就直接將這個已經(jīng)裝載的類返回給了JVM , 完成了Applet類的裝載。接下來,Applet類的超類也一樣處理。最后, TestApplet及所有有關(guān)的類都裝載到了JVM中。

            四、結(jié)論

            類的動態(tài)裝載機(jī)制是JVM的一項(xiàng)核心技術(shù), 也是容易被忽視而引起很多誤解的地方。本文介紹了JVM中類裝載的原理、實(shí)現(xiàn)以及應(yīng)用,尤其分析了ClassLoader的結(jié)構(gòu)、用途以及如何利用自定義的ClassLoader裝載并執(zhí)行Java類,希望能使讀者對JVM中的類裝載有一個比較深入的理解

          posted @ 2008-08-21 20:45 竹子 閱讀(176) | 評論 (0)編輯 收藏
            2008年8月16日
          -------------------
          Tomcat 5中文問題
          author:kiss__sky@163.com
          -------------------

          問題描述:

          1 表單提交的數(shù)據(jù),用request.getParameter(“xxx”)返回的字符串為亂碼或者??
          2 直接通過url如http://localhost/a.jsp?name=中國,這樣的get請求在服務(wù)端用request. getParameter(“name”)時返回的是亂碼;按tomcat4的做法設(shè)置Filter也沒有用或者用request.setCharacterEncoding("GBK");也不管用

          原因:
          1 tomcat的j2ee實(shí)現(xiàn)對表單提交即post方式提示時處理參數(shù)采用缺省的iso-8859-1來處理
          2 tomcat對get方式提交的請求對query-string 處理時采用了和post方法不一樣的處理方式。(與tomcat4不一樣,所以設(shè)置setCharacterEncoding(“gbk”))不起作用。


          解決辦法:

          首先所有的jsp文件都加上:


          1 實(shí)現(xiàn)一個Filter.設(shè)置處理字符集為GBK。(在tomcat的webapps/servlet-examples目錄有一個完整的例子。請參考web.xml和SetCharacterEncodingFilter的配置。)

          1)只要把%TOMCAT安裝目錄%/ webapps\servlets-examples\WEB-INF\classes\filters\SetCharacterEncodingFilter.class文件拷到你的webapp目錄/filters下,如果沒有filters目錄,就創(chuàng)建一個。
          2)在你的web.xml里加入如下幾行:


          <filter>
          <filter-name>Set Character Encoding</filter-name>
          <filter-class>filters.SetCharacterEncodingFilter</filter-class>
          <init-param>
          <param-name>encoding</param-name>
          <param-value>GBK</param-value>
          </init-param>
          </filter>

          <filter-mapping>
          <filter-name>Set Character Encoding</filter-name>
          <url-pattern>/*</url-pattern>
          </filter-mapping>


          3)完成.

          2 get方式的解決辦法
          1) 打開tomcat的server.xml文件,找到區(qū)塊,加入如下一行:
          URIEncoding=”GBK”
          完整的應(yīng)如下:

          <Connector
          port="80" maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
          enableLookups="false" redirectPort="8443" acceptCount="100"
          debug="0" connectionTimeout="20000"
          disableUploadTimeout="true"
          URIEncoding="GBK"
          />



          2)重啟tomcat,一切OK。

          執(zhí)行如下jsp頁頁測試是否成功


          <%@ page contentType="text/html;charset=gb2312"%>
          <%@ page import="java.util.*"%>

          <%

          String q=request.getParameter("q");
          q = q == null? "沒有值" : q;

          %>


          <HTML>
          <HEAD><TITLE>新聞列表顯示</TITLE>
          <META http-equiv=Content-Type content="text/html; charset=gb2312">
          <META http-equiv=pragma content=no-cache>
          <body>
          你提交了:
          <%=q%>

          <br>
          <form action="tcnchar.jsp" method="post">
          輸入中文:<input type="text" name="q"><input type="submit" value="確定">
          <br>
          <a href="tcnchar.jsp?q=中國">通過get方式提交</a>

          </form>
          </BODY></HTML>


          測試結(jié)果如果你輸入文本框或者點(diǎn)超鏈都會顯示:你提交了”中國”,說明成功!!!!!



          特別感謝下面這篇帖子,幫我解決了中文問題.最后祝大家好運(yùn)!!!
          參考網(wǎng)址:

          http://www.javaworld.com.tw/jute/post/view?bid=9&id=44042&sty=1&tpg=1&age=0

          posted @ 2008-08-16 18:29 竹子 閱讀(155) | 評論 (0)編輯 收藏
          僅列出標(biāo)題  下一頁
          主站蜘蛛池模板: 桑日县| 河西区| 姚安县| 资溪县| 内乡县| 都昌县| 锦州市| 永泰县| 甘德县| 南木林县| 南昌市| 玉屏| 襄樊市| 灯塔市| 宜黄县| 鲁甸县| 曲沃县| 长宁区| 清远市| 绥德县| 新竹市| 阿鲁科尔沁旗| 绿春县| 报价| 沁水县| 五原县| 安图县| 泽普县| 基隆市| 武山县| 尖扎县| 靖西县| 休宁县| 平定县| 云林县| 钟山县| 沁水县| 新蔡县| 辽阳市| 莱芜市| 汉阴县|