版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
摩根面試準(zhǔn)備要點(diǎn)JVM架構(gòu)(Vincent)?
重要涉及兩個(gè)子系統(tǒng)和兩個(gè)組件:Classloader(類裝載器)子系統(tǒng),Executionengine(執(zhí)行引擎)子系統(tǒng);Runtimedat(yī)aarea(運(yùn)營時(shí)數(shù)據(jù)區(qū)域)組件,Nativeinterface(本地接口)組件。?
Classloader子系統(tǒng)的作用:根據(jù)給定的全限定名類名(如java.lang.Object)來裝載class文獻(xiàn)的內(nèi)容到Runtimedataarea中的methodarea(方法區(qū)域)。Javsa程序員可以extendsjava.lang.ClassLoader類來寫自己的Classloader。?
Executionengine子系統(tǒng)的作用:執(zhí)行classes中的指令。任何JVMspecification實(shí)現(xiàn)(JDK)的核心是Executionengine,換句話說:Sun的JDK和IBM的JDK好壞重要取決于他們各自實(shí)現(xiàn)的Execution
engine的好壞。每個(gè)運(yùn)營中的線程都有一個(gè)Executionengine的實(shí)例。
Nativeinterface組件:與nat(yī)ivelibraries交互,是其它編程語言交互的接口。
?
Runtimedat(yī)aarea組件:這個(gè)組件就是JVM中的內(nèi)存。下面對這個(gè)部分進(jìn)行具體介紹。Runtimedataarea的整體架構(gòu)圖Runtimedataarea重要涉及五個(gè)部分:Heap(堆),MethodArea(方法區(qū)域),JavaStack(java的棧),ProgramCounter(程序計(jì)數(shù)器),Nat(yī)ivemethodstack(本地方法棧)。Heap和MethodArea是被所有線程的共享使用的;而Javastack,Programcounter和Nativemethodstack是以線程為粒度的,每個(gè)線程獨(dú)自擁有。?
Heap
Java程序在運(yùn)營時(shí)創(chuàng)建的所有類實(shí)或數(shù)組都放在同一個(gè)堆中。而一個(gè)Java虛擬實(shí)例中只存在一個(gè)堆空間,因此所有線程都將共享這個(gè)堆。每一個(gè)java程序獨(dú)占一個(gè)JVM實(shí)例,因而每個(gè)java程序都有它自己的堆空間,它們不會彼此干擾。但是同一java程序的多個(gè)線程都共享著同一個(gè)堆空間,就得考慮多線程訪問對象(堆數(shù)據(jù))的同步問題。(這里也許出現(xiàn)的異常java.lang.OutOfMemoryError:Javaheapspace)??Methodarea?在Java虛擬機(jī)中,被裝載的class的信息存儲在Methodarea的內(nèi)存中。當(dāng)虛擬機(jī)裝載某個(gè)類型時(shí),它使用類裝載器定位相應(yīng)的class文獻(xiàn),然后讀入這個(gè)class文獻(xiàn)內(nèi)容并把它傳輸?shù)教摂M機(jī)中。緊接著虛擬機(jī)提取其中的類型信息,并將這些信息存儲到方法區(qū)。該類型中的類(靜態(tài))變量同樣也存儲在方法區(qū)中。與Heap同樣,methodarea是多線程共享的,因此要考慮多線程訪問的同步問題。比如,假設(shè)同時(shí)兩個(gè)線程都企圖訪問一個(gè)名為Lava的類,而這個(gè)類還沒有內(nèi)裝載入虛擬機(jī),那么,這時(shí)應(yīng)當(dāng)只有一個(gè)線程去裝載它,而另一個(gè)線程則只能等待。(這里也許出現(xiàn)的異常java.lang.OutOfMemoryError:PermGenfull)
Javastack?
Javastack以幀為單位保存線程的運(yùn)營狀態(tài)。虛擬機(jī)只會直接對Javastack執(zhí)行兩種操作:以幀為單位的壓?;虺鰲?。每當(dāng)線程調(diào)用一個(gè)方法的時(shí)候,就對當(dāng)前狀態(tài)作為一個(gè)幀保存到j(luò)avastack中(壓棧);當(dāng)一個(gè)方法調(diào)用返回時(shí),從javastack彈出一個(gè)幀(出棧)。棧的大小是有一定的限制,這個(gè)也許出現(xiàn)StackOverFlow問題。下面的程序可以說明這個(gè)問題。publicclassTestStackOverFlow{?publicstaticvoidmain(String[]args){? Recursiver=newRecursive();??r.doit(10000);? //Exceptioninthread"main"java.lang.StackOverflowError }}classRecursive{ publicintdoit(intt){? if(t<=1){ ??return1; }??returnt+doit(t-1); }}
?Programcounter?每個(gè)運(yùn)營中的Java程序,每一個(gè)線程都有它自己的PC寄存器,也是該線程啟動時(shí)創(chuàng)建的。PC寄存器的內(nèi)容總是指向下一條將被執(zhí)行指令的地址;,這里的地址可以是一個(gè)本地指針,也可以是在方法區(qū)中相相應(yīng)于該方法起始指令的偏移量。
Nativemethodstack?對于一個(gè)運(yùn)營中的Java程序而言,它還能會用到一些跟本地方法相關(guān)的數(shù)據(jù)區(qū)。當(dāng)某個(gè)線程調(diào)用一個(gè)本地方法時(shí),它就進(jìn)入了一個(gè)全新的并且不再受虛擬機(jī)限制的世界。本地方法可以通過本地方法接口來訪問虛擬機(jī)的運(yùn)營時(shí)數(shù)據(jù)區(qū),不止與此,它還可以做任何它想做的事情。比如,可以調(diào)用寄存器,或在操作系統(tǒng)中分派內(nèi)存等??傊?,本地方法具有和JVM相同的能力和權(quán)限。(這里出現(xiàn)JVM無法控制的內(nèi)存溢出問題nativeheapOutOfMemory)
CLassLoader(Vincent)Java的可執(zhí)行文獻(xiàn)不同于C/C++,Java編譯器只產(chǎn)生中間字節(jié)碼文獻(xiàn)(.class文獻(xiàn)),由Java虛擬機(jī)(java.exe)解釋執(zhí)行。Java發(fā)布的程序(JAR包)也多半是一堆class文獻(xiàn),運(yùn)營時(shí)由ClassLoader加載到Java虛擬機(jī)中執(zhí)行。ClassLoader是Java虛擬機(jī)的重要組成部分,由Java語言編寫,用戶可以實(shí)現(xiàn)自定義的ClassLoader來完畢特定的功能。下面我們用例子說明ClassLoader。JVM規(guī)范定義了兩種類型的ClassLoader:BootstrapClassLoader和User-definedClassLoader。JVM在運(yùn)營時(shí)會產(chǎn)生三個(gè)ClassLoader:BootstrapClassLoader、ExtensionClassLoader和AppClassLoader。Bootstrap是用C++編寫的,我們在Java中看不到它,是null,是JVM自帶的類裝載器,用來裝載核心類庫,如java.lang.*等。AppClassLoader的Parent是ExtClassLoader,而ExtClassLoader的Parent為BootstrapClassLoader。java中,什么叫不可更改的類(immutableclass)(KevinTam)從字面意思來理解就是不會發(fā)生變化的類,那么是什么不會發(fā)生變化呢,其實(shí)就是類的狀態(tài),也就是不變類的實(shí)例一旦被創(chuàng)建,其狀態(tài)就不會發(fā)生變化,舉個(gè)例子:假如人是一個(gè)class,那么我們中的每一個(gè)都是人這個(gè)類的具體的instance,假如人這個(gè)類只有一個(gè)狀態(tài)就是生身父母,那么它就是一個(gè)不變類,由于每一個(gè)人在出生的那一剎那,生身父母就已經(jīng)被設(shè)立了值,并且終生都不會發(fā)生變化。不變類有什么好處呢?1)不變類是線程安全的,由于不變類的狀態(tài)在創(chuàng)建以后不再發(fā)生變化,所以它可以在線程之間共享,而不需要同步。2)不變類的instance可以被reuse創(chuàng)建類的實(shí)例需要花費(fèi)CPU的時(shí)間,當(dāng)這個(gè)實(shí)例不再被引用時(shí),將會被垃圾回收掉,這時(shí)候,又需要花費(fèi)CPU的時(shí)間。對于不變類而言,一個(gè)好處就是可以將常用的實(shí)例進(jìn)行緩存,從而減少了對象的創(chuàng)建。舉個(gè)例子,對于布爾型,最常用的便是trueandfalse。JDK中的Boolean類就是一個(gè)不變類,并且對這兩個(gè)實(shí)例進(jìn)行了緩沖。publicfinalclassBooleanimplementsjava.io.Serializable{/***The<code>Boolean</code>objectcorrespondingtotheprimitive*value<code>true</code>.*/publicstaticfinalBooleanTRUE=newBoolean(true);/***The<code>Boolean</code>objectcorrespondingtotheprimitive*value<code>false</code>.*/publicstaticfinalBooleanFALSE=newBoolean(false);//這個(gè)方法不會創(chuàng)建新的對象,而是重用已經(jīng)創(chuàng)建好的instancepublicstaticBooleanvalueOf(booleanb){return(b?TRUE:FALSE);}}3)不變類的某些方法可以緩存計(jì)算的結(jié)果hashCode這個(gè)方法來自于Object這個(gè)類,這個(gè)方法用來返回對象的hashCode,重要用于將對象放置到hashtable中時(shí),來擬定這個(gè)對象的存儲位置。對于一個(gè)不變類的實(shí)例,它的hashCode也是不變的,所以就可以緩存這個(gè)計(jì)算的結(jié)果,來提高性能,避免不必要的運(yùn)算,JDK中的String類就是一個(gè)例子。publicfinalclassString{/**Cachethehashcodeforthestring*/privat(yī)einthash;//Defaultto0publicinthashCode(){inth=hash;if(h==0){//computethevaluehash=h;//cachethevalue}returnh;}}在JDK中,String,theprimitivewrapperclasses,andBigIntegerandBigDecimal都是不變類。假如一個(gè)類是不變類,這個(gè)類是不是就不能有改變狀態(tài)的方法呢?答案當(dāng)然是否認(rèn)的,String是一個(gè)不變類,仍然有replace,replaceAll這樣的方法,而String仍然是一個(gè)不變類,那是由于在這些改變狀態(tài)的方法中,每次都是新創(chuàng)建一個(gè)String對象。假如大家理解了不變類,那也就不難理解為什么在做String的concat(yī)enate時(shí),應(yīng)當(dāng)用StringBuffer而不是用+的操作符。如何對的使用String呢?1)不要用new去創(chuàng)建String對象。假如使用new去創(chuàng)建String,那么每次都會創(chuàng)建一個(gè)新對象。publicstaticvoidmain(String[]args){StringA1="A";StringA2="A";//Itwon'tcreateanewobjectcheckInstance(A1,A2);//Result:TheyaresameinstancesStringB1=newString("A");//createanewobjectStringB2=newString("A");//creatanewobjectcheckInstance(B1,B2);//Result:Theyaredifferentinstances}privatestaticvoidcheckInstance(Stringa1,Stringa2){if(a1==a2){System.out.println("Theyaresameinstances");}else{System.out.println("Theyaredifferentinstances");}}2)應(yīng)當(dāng)用StringBuffer來做連接操作由于String是一個(gè)不變類,那么在做連接操作時(shí),就會創(chuàng)建臨時(shí)對象來保存中間的運(yùn)算結(jié)果,而StringBuffer是一個(gè)mutableclass,這樣就不需要創(chuàng)建臨時(shí)的對象來保存結(jié)果,從而提高了性能。JAVAGarbageCollection(Vincent)垃圾分代回收算法(GenerationalCollecting)
基于對對象生命周期分析后得出的垃圾回收算法。把對象分為年青代、年老代、持久代,對不同生命周期的對象使用不同的算法(上述方式中的一個(gè))進(jìn)行回收?,F(xiàn)在的垃圾回收器(從J2SE1.2開始)都是使用此算法的。
??如上圖所示,為Java堆中的各代分布。?1.Young(年輕代)JVMspecification中的Heap的一部份
年輕代分三個(gè)區(qū)。一個(gè)Eden區(qū),兩個(gè)Survivor區(qū)。大部分對象在Eden區(qū)中生成。當(dāng)Eden區(qū)滿時(shí),還存活的對象將被復(fù)制到Survivor區(qū)(兩個(gè)中的一個(gè)),當(dāng)這個(gè)Survivor區(qū)滿時(shí),此區(qū)的存活對象將被復(fù)制到此外一個(gè)Survivor區(qū),當(dāng)這個(gè)Survivor去也滿了的時(shí)候,從第一個(gè)Survivor區(qū)復(fù)制過來的并且此時(shí)還存活的對象,將被復(fù)制年老區(qū)(Tenured);。需要注意,Survivor的兩個(gè)區(qū)是對稱的,沒先后關(guān)系,所以同一個(gè)區(qū)中也許同時(shí)存在從Eden復(fù)制過來對象,和從前一個(gè)Survivor復(fù)制過來的對象,而復(fù)制到年老區(qū)的只有從第一個(gè)Survivor去過來的對象。并且,Survivor區(qū)總有一個(gè)是空的。
2.Tenured(年老代)JVMspecificat(yī)ion中的Heap的一部份
年老代存放從年輕代存活的對象。一般來說年老代存放的都是生命期較長的對象。?3.Perm(持久代)JVMspecification中的Methodarea?用于存放靜態(tài)文獻(xiàn),如今Java類、方法等。持久代對垃圾回收沒有顯著影響,但是有些應(yīng)用也許動態(tài)生成或者調(diào)用一些class,例如Hibernate等,在這種時(shí)候需要設(shè)立一個(gè)比較大的持久代空間來存放這些運(yùn)營過程中新增的類。持久代大小通過-XX:MaxPermSize=進(jìn)行設(shè)立。?采用分區(qū)管理機(jī)制的JVM將JVM所管理的所有內(nèi)存資源分為2個(gè)大的部分。永久存儲區(qū)(PermanentSpace)和堆空間(TheHeapSpace)。其中堆空間又分為新生區(qū)(Young(New)generationspace)和養(yǎng)老區(qū)(Tenure(Old)generationspace),新生區(qū)又分為伊甸園(Edenspace),幸存者0區(qū)(Survivor0space)和幸存者1區(qū)(Survivor1space)。具體分區(qū)如下圖:
?那JVM他的這些分區(qū)各有什么用途,請看下面的解說。
永久存儲區(qū)(PermanentSpace):永久存儲區(qū)是JVM的駐留內(nèi)存,用于存放JDK自身所攜帶的Class,Interface的元數(shù)據(jù),應(yīng)用服務(wù)器允許必須的Class,Interface的元數(shù)據(jù)和Java程序運(yùn)營時(shí)需要的Class和Interface的元數(shù)據(jù)。被裝載進(jìn)此區(qū)域的數(shù)據(jù)是不會被垃圾回收器回收掉的,關(guān)閉JVM時(shí),釋放此區(qū)域所控制的內(nèi)存。?
堆空間(TheHeapSpace):是JAVA對象生死存亡的地區(qū),JAVA對象的出生,成長,死亡都在這個(gè)區(qū)域完畢。堆空間又分別按JAVA對象的創(chuàng)建和年齡特性分為養(yǎng)老區(qū)和新生區(qū)。??新生區(qū)(Young(New)generationspace):新生區(qū)的作用涉及JAVA對象的創(chuàng)建和從JAVA對象中篩選出能進(jìn)入養(yǎng)老區(qū)的JAVA對象。??伊甸園(Edenspace):JAVA對空間中的所有對象在此出生,該區(qū)的名字因此而得名。也即是說當(dāng)你的JAVA程序運(yùn)營時(shí),需要創(chuàng)建新的對象,JVM將在該區(qū)為你創(chuàng)建一個(gè)指定的對象供程序使用。創(chuàng)建對象的依據(jù)即是永久存儲區(qū)中的元數(shù)據(jù)。?
幸存者0區(qū)(Survivor0space)和幸存者1區(qū)(Survivor1space):當(dāng)伊甸園的空間用完時(shí),程序又需要創(chuàng)建對象;此時(shí)JVM的垃圾回收器將對伊甸園區(qū)進(jìn)行垃圾回收,將伊甸園區(qū)中的不再被其他對象所引用的對象進(jìn)行銷毀工作。同時(shí)將伊甸園中的尚有其他對象引用的對象移動到幸存者0區(qū)。幸存者0區(qū)就是用于存放伊甸園垃圾回收時(shí)所幸存下來的JAVA對象。當(dāng)將伊甸園中的尚有其他對象引用的對象移動到幸存者0區(qū)時(shí),假如幸存者0區(qū)也沒有空間來存放這些對象時(shí),JVM的垃圾回收器將對幸存者0區(qū)進(jìn)行垃圾回收解決,將幸存者0區(qū)中不在有其他對象引用的JAVA對象進(jìn)行銷毀,將幸存者0區(qū)中尚有其他對象引用的對象移動到幸存者1區(qū)。幸存者1區(qū)的作用就是用于存放幸存者0區(qū)垃圾回收解決所幸存下來的JAVA對象。
養(yǎng)老區(qū)(Tenure(Old)generationspace):用于保存從新生區(qū)篩選出來的JAVA對象。
上面我們看了JVM的內(nèi)存分區(qū)管理,現(xiàn)在我們來看JVM的垃圾回收工作是如何運(yùn)作的。一方面當(dāng)啟動J2EE應(yīng)用服務(wù)器時(shí),JVM隨之啟動,并將JDK的類和接口,應(yīng)用服務(wù)器運(yùn)營時(shí)需要的類和接口以及J2EE應(yīng)用的類和接口定義文獻(xiàn)也及編譯后的Class文獻(xiàn)或JAR包中的Class文獻(xiàn)裝載到JVM的永久存儲區(qū)。在伊甸園中創(chuàng)建JVM,應(yīng)用服務(wù)器運(yùn)營時(shí)必須的JAVA對象,創(chuàng)建J2EE應(yīng)用啟動時(shí)必須創(chuàng)建的JAVA對象;J2EE應(yīng)用啟動完畢,可對外提供服務(wù)。?JVM在伊甸園區(qū)根據(jù)用戶的每次請求創(chuàng)建相應(yīng)的JAVA對象,當(dāng)伊甸園的空間局限性以用來創(chuàng)建新JAVA對象的時(shí)候,JVM的垃圾回收器執(zhí)行對伊甸園區(qū)的垃圾回收工作,銷毀那些不再被其他對象引用的JAVA對象(假如該對象僅僅被一個(gè)沒有其他對象引用的對象引用的話,此對象也被歸為沒有存在的必要,依此類推),并將那些被其他對象所引用的JAVA對象移動到幸存者0區(qū)。
假如幸存者0區(qū)有足夠控件存放則直接放到幸存者0區(qū);假如幸存者0區(qū)沒有足夠空間存放,則JVM的垃圾回收器執(zhí)行對幸存者0區(qū)的垃圾回收工作,銷毀那些不再被其他對象引用的JAVA對象(假如該對象僅僅被一個(gè)沒有其他對象引用的對象引用的話,此對象也被歸為沒有存在的必要,依此類推),并將那些被其他對象所引用的JAVA對象移動到幸存者1區(qū)。?假如幸存者1區(qū)有足夠控件存放則直接放到幸存者1區(qū);假如幸存者0區(qū)沒有足夠空間存放,則JVM的垃圾回收器執(zhí)行對幸存者0區(qū)的垃圾回收工作,銷毀那些不再被其他對象引用的JAVA對象(假如該對象僅僅被一個(gè)沒有其他對象引用的對象引用的話,此對象也被歸為沒有存在的必要,依此類推),并將那些被其他對象所引用的JAVA對象移動到養(yǎng)老區(qū)。
假如養(yǎng)老區(qū)有足夠控件存放則直接放到養(yǎng)老區(qū);假如養(yǎng)老區(qū)沒有足夠空間存放,則JVM的垃圾回收器執(zhí)行對養(yǎng)老區(qū)區(qū)的垃圾回收工作,銷毀那些不再被其他對象引用的JAVA對象(假如該對象僅僅被一個(gè)沒有其他對象引用的對象引用的話,此對象也被歸為沒有存在的必要,依此類推),并保存那些被其他對象所引用的JAVA對象。假如到最后養(yǎng)老區(qū),幸存者1區(qū),幸存者0區(qū)和伊甸園區(qū)都沒有空間的話,則JVM會報(bào)告“JVM堆空間溢出(java.lang.OutOfMemoryError:Javaheapspace)”,也即是在堆空間沒有空間來創(chuàng)建對象。?這就是JVM的內(nèi)存分區(qū)管理,相比不分區(qū)來說;一般情況下,垃圾回收的速度要快很多;由于在沒有必要的時(shí)候不用掃描整片內(nèi)存而節(jié)省了大量時(shí)間。?通常大家還會碰到此外一種內(nèi)存溢犯錯誤“永久存儲區(qū)溢出(java.lang.OutOfMemoryError:JavaPermanentSpace)”。所有的垃圾收集算法都面臨同一個(gè)問題,那就是找出應(yīng)用程序不可到達(dá)的內(nèi)存塊,將其釋放,這里面得不可到達(dá)重要是指應(yīng)用程序已經(jīng)沒有內(nèi)存塊的引用了,而在JAVA中,某個(gè)對象相應(yīng)用程序是可到達(dá)的是指:這個(gè)對象被根(根重要是指類的靜態(tài)變量,或者活躍在所有線程棧的對象的引用)引用或者對象被另一個(gè)可到達(dá)的對象引用。Reference
Counting(引用計(jì)數(shù))
引用計(jì)數(shù)是最簡樸直接的一種方式,這種方式在每一個(gè)對象中增長一個(gè)引用的計(jì)數(shù),這個(gè)計(jì)數(shù)代表當(dāng)前程序有多少個(gè)引用引用了此對象,假如此對象的引用計(jì)數(shù)變?yōu)?,那么此對象就可以作為垃圾收集器的目的對象來收集。優(yōu)點(diǎn):簡樸,直接,不需要暫停整個(gè)應(yīng)用缺陷:1.需要編譯器的配合,編譯器要生成特殊的指令來進(jìn)行引用計(jì)數(shù)的操作,比如每次將對象賦值給新的引用,或者者對象的引用超過了作用域等。2.不能解決循環(huán)引用的問題跟蹤收集器跟蹤收集器一方面要暫停整個(gè)應(yīng)用程序,然后開始從根對象掃描整個(gè)堆,判斷掃描的對象是否有對象引用,這里面有三個(gè)問題需要搞清楚:1.假如每次掃描整個(gè)堆,那么勢必讓GC的時(shí)間變長,從而影響了應(yīng)用自身的執(zhí)行。因此在JVM里面采用了分代收集,在新生代收集的時(shí)候minor
gc只需要掃描新生代,而不需要掃描老生代。2.JVM采用了分代收集以后,minor
gc只掃描新生代,但是minor
gc怎么判斷是否有老生代的對象引用了新生代的對象,JVM采用了卡片標(biāo)記的策略,卡片標(biāo)記將老生代提成了一塊一塊的,劃分以后的每一個(gè)塊就叫做一個(gè)卡片,JVM采用卡表維護(hù)了每一個(gè)塊的狀態(tài),當(dāng)JAVA程序運(yùn)營的時(shí)候,假如發(fā)現(xiàn)老生代對象引用或者釋放了新生代對象的引用,那么就JVM就將卡表的狀態(tài)設(shè)立為臟狀態(tài),這樣每次minor
gc的時(shí)候就會只掃描被標(biāo)記為臟狀態(tài)的卡片,而不需要掃描整個(gè)堆。具體如下圖:3.GC在收集一個(gè)對象的時(shí)候會判斷是否有引用指向?qū)ο?,在JAVA中的引用重要有四種:Strong
reference,Soft
reference,Weak
reference,Phantom
reference.Strong
Reference
強(qiáng)引用是JAVA中默認(rèn)采用的一種方式,我們平時(shí)創(chuàng)建的引用都屬于強(qiáng)引用。假如一個(gè)對象沒有強(qiáng)引用,那么對象就會被回收。public
void
testStrongReference(){Object
referent
=
new
Object();Object
strongReference
=
referent;referent
=
null;System.gc();assertNotNull(strongReference);}
Soft
Reference軟引用的對象在GC的時(shí)候不會被回收,只有當(dāng)內(nèi)存不夠用的時(shí)候才會真正的回收,因此軟引用適合緩存的場合,這樣使得緩存中的對象可以盡量的再內(nèi)存中待長期一點(diǎn)。Public
void
testSoftReference(){String
str
=
"test";SoftReference<String>
softreference
=
new
SoftReference<String>(str);str=null;System.gc();assertNotNull(softreference.get());}
Weak
reference弱引用有助于對象更快的被回收,假如一個(gè)對象沒有強(qiáng)引用只有弱引用,那么在GC后,這個(gè)對象肯定會被回收。Public
void
testWeakReference(){String
str
=
"test";WeakReference<String>
weakReference
=
new
WeakReference<String>(str);str=null;System.gc();assertNull(weakReference.get());}
Phantom
reference
Mark-Sweep
Collector(標(biāo)記-清除收集器)標(biāo)記清除收集器最早由Lisp的發(fā)明人于1960年提出,標(biāo)記清除收集器停止所有的工作,從根掃描每個(gè)活躍的對象,然后標(biāo)記掃描過的對象,標(biāo)記完畢以后,清除那些沒有被標(biāo)記的對象。優(yōu)點(diǎn):1
解決循環(huán)引用的問題2
不需要編譯器的配合,從而就不執(zhí)行額外的指令缺陷:1.每個(gè)活躍的對象都要進(jìn)行掃描,收集暫停的時(shí)間比較長。Copying
Collector(復(fù)制收集器)復(fù)制收集器將內(nèi)存分為兩塊同樣大小空間,某一個(gè)時(shí)刻,只有一個(gè)空間處在活躍的狀態(tài),當(dāng)活躍的空間滿的時(shí)候,GC就會將活躍的對象復(fù)制到未使用的空間中去,本來不活躍的空間就變?yōu)榱嘶钴S的空間。復(fù)制收集器具體過程可以參考下圖:優(yōu)點(diǎn):1
只掃描可以到達(dá)的對象,不需要掃描所有的對象,從而減少了應(yīng)用暫停的時(shí)間缺陷:1.需要額外的空間消耗,某一個(gè)時(shí)刻,總是有一塊內(nèi)存處在未使用狀態(tài)2.復(fù)制對象需要一定的開銷Mark-Compact
Collector(標(biāo)記-整理收集器)標(biāo)記整理收集器汲取了標(biāo)記清除和復(fù)制收集器的優(yōu)點(diǎn),它分兩個(gè)階段執(zhí)行,在第一個(gè)階段,一方面掃描所有活躍的對象,并標(biāo)記所有活躍的對象,第二個(gè)階段一方面清除未標(biāo)記的對象,然后將活躍的的對象復(fù)制到堆得底部。標(biāo)記整理收集器的過程示意圖請參考下圖:
Mark-compact策略極大的減少了內(nèi)存碎片,并且不需要像Copy
Collector同樣需要兩倍的空間。JVM的垃圾收集策略
GC的執(zhí)行時(shí)要花費(fèi)一定的CPU資源和時(shí)間的,因此在JDK1.2以后,JVM引入了分代收集的策略,其中對新生代采用"Mark-Compact"策略,而對老生代采用了“Mark-Sweep"的策略。其中新生代的垃圾收集器命名為“minor
gc”,老生代的GC命名為"Full
Gc
或者M(jìn)ajor
GC".其中用System.gc()強(qiáng)制執(zhí)行的是Full
Gc.SpringIOCandAOP(Minjin)IoC和AOP都是Spring的核心思想??
當(dāng)然,最為一個(gè)框架級的輕量組件,大量的配置文獻(xiàn)是不可缺少的,但是核心是要把這些配置文獻(xiàn),配置節(jié)組裝起來,并將核心代碼編寫為完全業(yè)務(wù)無關(guān)的。我們看看Spring是怎么做的。
一方面,IoC,控制反轉(zhuǎn)。Spring開發(fā)的基本思想:面向接口的編程模式??蚣茏龅脑蕉?應(yīng)當(dāng)越能發(fā)現(xiàn)接口在其中起到的作用,而Spring將這種想法,開始貫徹到業(yè)務(wù)的開發(fā)中了。Bean的Set方法使用接口作為參數(shù),保證其擴(kuò)展性,實(shí)現(xiàn)依賴關(guān)系的松偶爾。所謂的控制反轉(zhuǎn),作為中文更好理解的一個(gè)翻譯應(yīng)當(dāng)是依賴注入,把依賴的類采用接口的方式,運(yùn)用Set函數(shù),傳入Bean的內(nèi)部,實(shí)現(xiàn)與外界的解耦合。這種注入也可作用于構(gòu)造函數(shù)。
另一方面,AOP,面向切面的編程方式,我覺得更通俗的說法應(yīng)當(dāng)是對容器內(nèi)的Bean進(jìn)行方法干涉。被容器中創(chuàng)建的類,看起來執(zhí)行一個(gè)普通的函數(shù)調(diào)用,由于被容器預(yù)解決,而會在方法執(zhí)行前/后進(jìn)行一些其他的、可配置的操作。當(dāng)然,這種方法也同樣是面向接口的,或者直接使用反射的。運(yùn)用java.lang.reflect.Invocat(yī)ionHandler接口可以達(dá)成這種干涉的效果。下面是轉(zhuǎn)載的一個(gè)簡樸示例。Java代碼import
java.lang.reflect.Invocat(yī)ionHandler;
import
java.lang.reflect.Method;
import
java.lang.reflect.Proxy;
public
class
DynaProxyHello
implements
InvocationHandler
{
private
Object
proxy;
private
Object
delegate;
public
Object
bind(Object
delegate,Object
proxy)
{
this.proxy
=
proxy;
this.delegate
=
delegate;
return
Proxy.newProxyInstance(
this.delegate.getClass().getClassLoader(),
this.delegate
.getClass().getInterfaces(),
this);
public
Object
invoke(Object
proxy,
Method
method,
Object[]
args)
throws
Throwable
{
Object
result
=
null;
try
{
//反射得到操作者的實(shí)例
Class
clazz
=
this.proxy.getClass();
//反射得到操作者的Start方法
Method
start
=
clazz.getDeclaredMethod("start",
new
Class[]
{
Method.class
});
//反射執(zhí)行start方法
start.invoke(this.proxy,
new
Object[]
{
method
});
//執(zhí)行要解決對象的原本方法
result
=
method.invoke(this.delegate,
args);
//
反射得到操作者的end方法
Method
end
=
clazz.getDeclaredMethod("end",
new
Class[]
{
Method.class
});
//
反射執(zhí)行end方法
end.invoke(thixy,
new
Object[]
{
method
});
}
cat(yī)ch
(Exception
e)
{
e.printStackTrace();
}
return
result;
}
}
public
class
Test
{
public
static
void
main(String[]
args)
{
IHello
hello
=
(IHello)new
DynaProxyHello().bind(new
Hello(),new
LoggerOperation());
hello.sayGoogBye("Double
J");
hello.sayHello("Double
J");
}
}
??
當(dāng)然,不是只有這一個(gè)實(shí)現(xiàn)方式,java的代理功能只能代理接口,假如要代理類的化,可以使用cglib。?
Spring框架當(dāng)然不會是上述的那么簡樸(事實(shí)上它非常復(fù)雜),但是我關(guān)注的是核心的實(shí)現(xiàn)方式和設(shè)計(jì)思想。在有些時(shí)候,我們不需要使用Spring,甚至不能使用Spring(比如不用Java開發(fā)),但是這種思想和方式是可以復(fù)用的。使用這種設(shè)計(jì)思想,按照當(dāng)前的語言和環(huán)境規(guī)定,實(shí)現(xiàn)自己的IoC和AOP框架。Spring框架(Minjin)Spring框架是一個(gè)分層架構(gòu),由7個(gè)定義良好的模塊組成。Spring模塊構(gòu)建在核心容器之上,核心容器定義了創(chuàng)建、配置和管理bean的方式,如圖1所示。
圖1.Spring框架的7個(gè)模塊
組成Spring框架的每個(gè)模塊(或組件)都可以單獨(dú)存在,或者與其他一個(gè)或多個(gè)模塊聯(lián)合實(shí)現(xiàn)。每個(gè)模塊的功能如下:核心容器:核心容器提供Spring框架的基本功能。核心容器的重要組件是BeanFactory,它是工廠模式的實(shí)現(xiàn)。BeanFactory使用控制反轉(zhuǎn)(IOC)模式將應(yīng)用程序的配置和依賴性規(guī)范與實(shí)際的應(yīng)用程序代碼分開。Spring上下文:Spring上下文是一個(gè)配置文獻(xiàn),向Spring框架提供上下文信息。Spring上下文涉及公司服務(wù),例如JNDI、EJB、電子郵件、國際化、校驗(yàn)和調(diào)度功能。SpringAOP:通過配置管理特性,SpringAOP模塊直接將面向方面的編程功能集成到了Spring框架中。所以,可以很容易地使Spring框架管理的任何對象支持AOP。SpringAOP模塊為基于Spring的應(yīng)用程序中的對象提供了事務(wù)管理服務(wù)。通過使用SpringAOP,不用依賴EJB組件,就可以將聲明性事務(wù)管理集成到應(yīng)用程序中。SpringDAO:JDBCDAO抽象層提供了故意義的異常層次結(jié)構(gòu),可用該結(jié)構(gòu)來管理異常解決和不同數(shù)據(jù)庫供應(yīng)商拋出的錯誤消息。異常層次結(jié)構(gòu)簡化了錯誤解決,并且極大地減少了需要編寫的異常代碼數(shù)量(例如打開和關(guān)閉連接)。SpringDAO的面向JDBC的異常遵從通用的DAO異常層次結(jié)構(gòu)。SpringORM:Spring框架插入了若干個(gè)ORM框架,從而提供了ORM的對象關(guān)系工具,其中涉及JDO、Hibernat(yī)e和iBatisSQLMap。所有這些都遵從Spring的通用事務(wù)和DAO異常層次結(jié)構(gòu)。SpringWeb模塊:Web上下文模塊建立在應(yīng)用程序上下文模塊之上,為基于Web的應(yīng)用程序提供了上下文。所以,Spring框架支持與JakartaStruts的集成。Web模塊還簡化了解決多部分請求以及將請求參數(shù)綁定到域?qū)ο蟮墓ぷ?。SpringMVC框架:MVC框架是一個(gè)全功能的構(gòu)建Web應(yīng)用程序的MVC實(shí)現(xiàn)。通過策略接口,MVC框架變成為高度可配置的,MVC容納了大量視圖技術(shù),其中涉及JSP、Velocity、Tiles、iText和POI。Spring框架的功能可以用在任何J2EE服務(wù)器中,大多數(shù)功能也合用于不受管理的環(huán)境。Spring的核心要點(diǎn)是:支持不綁定到特定J2EE服務(wù)的可重用業(yè)務(wù)和數(shù)據(jù)訪問對象。毫無疑問,這樣的對象可以在不同J2EE環(huán)境(Web或EJB)、獨(dú)立應(yīng)用程序、測試環(huán)境之間重用。SpringAOP兩種實(shí)現(xiàn)機(jī)制(BaomingChai)SPRING是通過動態(tài)代理來實(shí)現(xiàn)AOP的,SPRING內(nèi)部提供了2種實(shí)現(xiàn)機(jī)制?1.假如是有接口聲明的類進(jìn)行AOP,spring調(diào)用的是java.lang.reflection.Proxy類來做解決org.springframework.a(chǎn)op.framework.JdkDynamicAopProxy publicObjectgetProxy(ClassLoaderclassLoader){ ?if(logger.isDebugEnabled()){?? ClasstargetClass=this.advised.getTargetSource().getTargetClass(); logger.debug("CreatingJDKdynamicproxy"+ ??(targetClass!=null?"for["+targetClass.getName()+"]":"")); ?}? Class[]proxiedInterfaces=AopProxyUtpleteProxiedInterfaces(this.advised);??returnProxy.newProxyInstance(classLoader,proxiedInterfaces,this);?}org.springframework.aop.framework.ReflectiveMethodInvocationpublicObjectproceed()throwsThrowable{? // Westartwithanindexof-1andincrementearly.? if(this.currentInterceptorIndex==this.interceptorsAndDynamicMethodMat(yī)chers.size()-1){? returninvokeJoinpoint();? } ObjectinterceptorOrInterceptionAdvice= erceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);??if(interceptorOrInterceptionA(yù)dviceinstanceofInterceptorAndDynamicMethodMatcher){ ??//Evaluatedynamicmethodmatcherhere:stat(yī)icpartwillalreadyhave //beenevaluat(yī)edandfoundtomatch.???InterceptorAndDynamicMethodMat(yī)cherdm= ?(InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;???if(dm.methodMat(yī)cher.matches(this.method,this.targetClass,this.arguments)){ ? returndm.interceptor.invoke(this); ?} ??else{? ?//Dynamicmatchingfailed.? ??//Skipthisinterceptorandinvokethenextinthechain.????returnproceed(); ??} }??else{ ? //It'saninterceptor,sowejustinvokeit:Thepointcutwillhave?? //beenevaluatedstat(yī)icallybeforethisobjectwasconstructed. ?return((MethodInterceptor)interceptorOrInterceptionA(yù)dvice).invoke(this); ?} }2.假如是沒有接口聲明的類呢?SPRING通過CGLIB包和內(nèi)部類來實(shí)現(xiàn)privat(yī)estaticclassStaticUnadvisedInterceptorimplementsMethodInterceptor,Serializable{??privatefinalObjecttarget; ?publicStaticUnadvisedInterceptor(Objecttarget){???this.target=target; ?} ?publicObjectintercept(Objectproxy,Methodmethod,Object[]args, MethodProxymethodProxy)throwsThrowable{? ObjectretVal=methodProxy.invoke(target,args);???returnmassageReturnTypeIfNecessary(proxy,target,retVal); } }?/** *Methodinterceptorusedforstatictargetswithnoadvicechain,whenthe *proxyistobeexposed. */ privatestaticclassStaticUnadvisedExposedInterceptorimplementsMethodInterceptor,Serializable{ ?privatefinalObjecttarget;??publicStaticUnadvisedExposedInterceptor(Objecttarget){ ? this.target=target;??} publicObjectintercept(Objectproxy,Methodmethod,Object[]args,MethodProxymethodProxy)throwsThrowable{ ? ObjectoldProxy=null; try{ ?oldProxy=AopContext.setCurrentProxy(proxy);? ?ObjectretVal=methodProxy.invoke(target,args); returnmassageReturnTypeIfNecessary(proxy,target,retVal);? ?} ? finally{?? ?AopContext.setCurrentProxy(oldProxy); ?} }?}?/** *Interceptorusedtoinvokeadynamictargetwithoutcreatingamethod?*invocationorevaluat(yī)inganadvicechain.(Weknowtherewasnoadvice?*forthismethod.) */ privateclassDynamicUnadvisedInterceptorimplementsMethodInterceptor,Serializable{??publicObjectintercept(Objectproxy,Methodmethod,Object[]args,MethodProxymethodProxy)throwsThrowable{ Objecttarget=advised.getTargetSource().getTarget();? ?try{ ? ObjectretVal=methodProxy.invoke(target,args); ?? returnmassageReturnTypeIfNecessary(proxy,target,retVal); ?} finally{ ??advised.getTargetSource().releaseTarget(target); ? } }?} /**?*Interceptorforunadviseddynamictargetswhentheproxyneedsexposing.?*/?privateclassDynamicUnadvisedExposedInterceptorimplementsMethodInterceptor,Serializable{ ?publicObjectintercept(Objectproxy,Methodmethod,Object[]args,MethodProxymethodProxy)throwsThrowable{?? ObjectoldProxy=null; Objecttarget=advised.getTargetSource().getTarget(); ?try{? ? oldProxy=AopContext.setCurrentProxy(proxy); ? ?ObjectretVal=methodProxy.invoke(target,args);? ??returnmassageReturnTypeIfNecessary(proxy,target,retVal); ?} ??finally{ AopContext.setCurrentProxy(oldProxy); ? advised.getTargetSource().releaseTarget(target); } } }我們自己也可以來試試?1.jdkproxy方式?
先來一個(gè)接口
IHelloWorld.javapackagekris.aop.test;publicinterfaceIHelloWorld{?publicvoidprint(Stringname); publicvoidwrite(Stringsth);}再來一個(gè)實(shí)現(xiàn)
?HelloWorld.javapackagekris.aop.test;publicclassHelloWorldimplementsIHelloWorld{?publicvoidprint(Stringname){ ?System.out.println("HelloWorld"+name);?} publicvoidwrite(Stringsth){??System.out.println("write"+sth); ? }}代理類
DefaultInvocat(yī)ionHandler.javapackagekris.a(chǎn)op.test;importjava.lang.reflect.Invocat(yī)ionHandler;importjava.lang.reflect.Method;publicclassDefaultInvocationHandlerimplementsInvocationHandler{ /** *替換外部class調(diào)用的方法 *obj 外部已經(jīng)已經(jīng)包裝好InvocationHandler的實(shí)例?*method 外部方法 *args??方法參數(shù)?*/ publicObjectinvoke(Objectobj,Methodmethod,Object[]args)? throwsThrowable{ ?Strings1[]={"kris"};? Strings2[]={"anyone"}; IHelloWo(hù)rldihw=newHelloWorld();? System.out.println("start!"); method.invoke(ihw,args);??method.invoke(ihw,s1);??Objecto=method.invoke(ihw,s2);??System.out.println("stop!");??returno; }}測試類?Test.javapackagekris.aop.test;importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Proxy;publicclassTest{?publicstaticvoidmain(Stringargs[]){??Classclazz=newHelloWorld().getClass();? ClassLoadercl=clazz.getClassLoader();? Classclasses[]=clazz.getInterfaces(); InvocationHandlerih=newDefaultInvocationHandler(); //用InvocationHandler給HelloWorld進(jìn)行AOP包裝 ?IHelloWo(hù)rldihw=(IHelloWo(hù)rld)Proxy.newProxyInstance(cl,classes,ih);? ihw.print("test");??ihw.write("test"); }}2.用CGLIB包實(shí)現(xiàn),一方面不要忘了引入那個(gè)包packagekris.aop.cglib.test;publicclassHelloWorld{?publicvoidprint(Stringname){? System.out.println("HelloWo(hù)rld"+name); }?publicvoidwrite(Stringsth){??System.out.println("write"+sth); }?publicvoidprint(){??System.out.println("HelloWo(hù)rld"); }}代理類(沒用內(nèi)部類,看起來清楚點(diǎn))packagekris.aop.cglib.test;importjava.lang.reflect.Method;importnet.sf.cglixy.MethodInterceptor;importnet.sf.cglib.proxy.MethodProxy;publicclassMethodInterceptorImplimplementsMethodInterceptor{?publicObjectintercept(Objectobj,Methodmethod,Object[]args, ? MethodProxyproxy)throwsThrowable{? System.out.println(method);??proxy.invokeSuper(obj,args); returnnull; }}測試類packagekris.aop.cglib.test;importnet.sf.cglib.proxy.Enhancer;publicclassTest{ publicstaticvoidmain(String[]args){??Enhancerenhancer=newEnhancer(); enhancer.setSuperclass(HelloWorld.class); //設(shè)立回調(diào)方法實(shí)現(xiàn)類 enhancer.setCallback(newMethodInterceptorImpl());??//實(shí)例化已經(jīng)添加回調(diào)實(shí)現(xiàn)的HELLOWORLD實(shí)例 HelloWorldmy=(HelloWorld)enhancer.create(); my.print();?}}SpringAOP的底層實(shí)現(xiàn)技術(shù)(BaomingChai,StephenYu)AOP概述軟件的編程語言最終的目的就是用更自然更靈活的方式模擬世界,從原始機(jī)器語言到過程語言再到面向?qū)ο蟮恼Z言,我們看到編程語言在一步步用更自然、更強(qiáng)大的方式描述軟件。AOP是軟件開發(fā)思想的一個(gè)奔騰,AOP的引入將有效填補(bǔ)OOP的局限性,OOP和AOP分別從縱向和橫向?qū)浖M(jìn)行抽象,有效地消除反復(fù)性的代碼,使代碼以更優(yōu)雅的更有效的方式進(jìn)行邏輯表達(dá)。AOP有三種植入切面的方法:其一是編譯期織入,這規(guī)定使用特殊的Java編譯器,AspectJ是其中的代表者;其二是類裝載期織入,而這規(guī)定使用特殊的類裝載器,AspectJ和AspectWerkz是其中的代表者;其三為動態(tài)代理織入,在運(yùn)營期為目的類添加增強(qiáng)生成子類的方式,SpringAOP采用動態(tài)代理織入切面。SpringAOP使用了兩種代理機(jī)制,一種是基于JDK的動態(tài)代理,另一種是基于CGLib的動態(tài)代理,之所以需要兩種代理機(jī)制,很大限度上是由于JDK自身只提供基于接口的代理,不支持類的代理?;贘DK的代理和基于CGLib的代理是SpringAOP的核心實(shí)現(xiàn)技術(shù),結(jié)識這兩代理技術(shù),有助于探究SpringAOP的實(shí)現(xiàn)機(jī)理。只要你樂意,你甚至可以拋開Spring,提供自己的AOP實(shí)現(xiàn)。帶有橫切邏輯的實(shí)例
?一方面,我們來看一個(gè)無法通過OOP進(jìn)行抽象的反復(fù)代碼邏輯,它們就是AOP改造的重要對象。下面,我們通過一個(gè)業(yè)務(wù)方法性能監(jiān)視的實(shí)例了解橫切邏輯。業(yè)務(wù)方法性能監(jiān)視,在每一個(gè)業(yè)務(wù)方法調(diào)用之前開始監(jiān)視,業(yè)務(wù)方法結(jié)束后結(jié)束監(jiān)視并給出性能報(bào)告:代碼清單2ForumService:包含性能監(jiān)視橫切代碼packagecom.baobaotao.proxy;?publicclassForumServiceImplimplementsForumService...{
publicvoidremoveTopic(inttopicId)...{
//開始性能監(jiān)視?
PerformanceMonitor.begin("com.baobaotao.proxy.ForumServiceImpl.removeTopic");?
System.out.println("模擬刪除Topic記錄:"+topicId);?
try...{
Thread.currentThread().sleep(20);
}catch(Exceptione)...{?
thrownewRuntimeException(e);?
}?
//結(jié)束監(jiān)視、并給出性能報(bào)告信息?
PerformanceMonitor.end();
}
publicvoidremoveForum(intforumId)...{
//開始性能監(jiān)視?PerformanceMonitor.begin("com.baobaotao.proxy.ForumServiceImpl.removeForum");?
System.out.println("模擬刪除Forum記錄:"+forumId);?
try...{?
Thread.currentThread().sleep(40);?
}catch(Exceptione)...{?
thrownewRuntimeException(e);?
}?
//結(jié)束監(jiān)視、并給出性能報(bào)告信息
PerformanceMonitor.end();?
}?}代碼清單2中粗體表達(dá)的代碼就是具有橫切特性的代碼,需要進(jìn)行性能監(jiān)視的每個(gè)業(yè)務(wù)方法的前后都需要添加類似的性能監(jiān)視語句。?
我們保證實(shí)例的完整性,我們提供了一個(gè)非常簡樸的性能監(jiān)視實(shí)現(xiàn)類,如所示代碼清單3所示:代碼清單3PerformanceMonitorpackagecom.baobaotao.proxy;publicclassPerformanceMonitor{
//通過一個(gè)ThreadLocal保存線程相關(guān)的性能監(jiān)視信息
privat(yī)estaticThreadLocal<MethodPerformace>performaceRecord=?newThreadLocal<MethodPerformace>();
publicstaticvoidbegin(Stringmethod){
System.out.println("beginmonitor...");
MethodPerformacemp=newMethodPerformace(method);
performaceRecord.set(mp);?
}
publicstat(yī)icvoidend(){
System.out.println("endmonitor...");
MethodPerformacemp=performaceRecord.get();?
mp.printPerformace();//打印出業(yè)務(wù)方法性能監(jiān)視的信息
}
}PerformanceMonitor提供了兩個(gè)方法,begin(Stringmethod)方法開始對某個(gè)業(yè)務(wù)類方法的監(jiān)視,method為業(yè)務(wù)方法的署名,而end()方法結(jié)束對業(yè)務(wù)方法的監(jiān)視,并給出性能監(jiān)視的信息。由于每一個(gè)業(yè)務(wù)方法都必須單獨(dú)記錄性能監(jiān)視數(shù)據(jù),所以我們使用了ThreadLocal,ThreadLocal是削除非線程安全狀態(tài)的不二法寶。ThreadLocal中的元素為方法性能記錄對象MethodPerformace,它的代碼如下所示:代碼清單4MethodPerformacepackagecom.baobaotao.proxy;?publicclassMethodPerformace{?
privatelongbegin;
privat(yī)elongend;?
privat(yī)eStringserviceMethod;?
publicMethodPerformace(StringserviceMethod){
this.serviceMethod=serviceMethod;
this.begin=System.currentTimeMillis();//記錄方法調(diào)用開始時(shí)的系統(tǒng)時(shí)間?
}?
publicvoidprintPerformace(){?
//以下兩行程序得到方法調(diào)用后的系統(tǒng)時(shí)間,并計(jì)算出方法執(zhí)行花費(fèi)時(shí)間
end=System.currentTimeMillis();?
longelapse=end-begin;
//報(bào)告業(yè)務(wù)方法執(zhí)行時(shí)間?
System.out.println(serviceMethod+"花費(fèi)"+elapse+"毫秒。");
}?}通過下面代碼測試這個(gè)擁有方法性能監(jiān)視能力的業(yè)務(wù)方法:packagecom.baobaotao.proxy;
publicclassTestForumService{?
publicstaticvoidmain(String[]args){?
ForumServiceforumService=newForumServiceImpl();?
forumService.removeForum(10);
forumService.removeTopic(1012);
}
}我們得到以下的輸出信息:beginmonitor...?模擬刪除Forum記錄:10
endmonitor...
com.baobaxy.ForumServiceImpl.removeForum花費(fèi)47毫秒。beginmonitor...?模擬刪除Topic記錄:1012
endmonitor...?com.baobaotao.proxy.ForumServiceImpl.removeTopic花費(fèi)16毫秒。如實(shí)例所示,要對業(yè)務(wù)類進(jìn)行性能監(jiān)視,就必須在每個(gè)業(yè)務(wù)類方法的前后兩處添加上反復(fù)性的啟動性能監(jiān)視和結(jié)束性能監(jiān)視的代碼。這些非業(yè)務(wù)邏輯的性能監(jiān)視代碼破壞了作為業(yè)務(wù)類ForumServiceImpl的純粹性。下面,我們分別JDK動態(tài)代理和CGLib動態(tài)代理技術(shù),將業(yè)務(wù)方法中啟動和結(jié)束性能監(jiān)視的這些橫切代碼從業(yè)務(wù)類中完畢移除。JDK動態(tài)代理
?在JDK1.3以后提供了動態(tài)代理的技術(shù),允許開發(fā)者在運(yùn)營期創(chuàng)建接口的代理實(shí)例。在Sun剛推出動態(tài)代理時(shí),還很難想象它有多大的實(shí)際用途,現(xiàn)在我們終于發(fā)現(xiàn)動態(tài)代理是實(shí)現(xiàn)AOP的絕好底層技術(shù)。
JDK的動態(tài)代理重要涉及到j(luò)ava.lang.reflect包中的兩個(gè)類:Proxy和InvocationHandler。其中InvocationHandler是一個(gè)接口,可以通過實(shí)現(xiàn)該接口定義橫切邏輯,在并通過反射機(jī)制調(diào)用目的類的代碼,動態(tài)將橫切邏輯和業(yè)務(wù)邏輯編織在一起。
?而Proxy為InvocationHandler實(shí)現(xiàn)類動態(tài)創(chuàng)建一個(gè)符合某一接口的代理實(shí)例。這樣講一定很抽象,我們立即著手動用Proxy和InvocationHandler這兩個(gè)魔法戒對上一節(jié)中的性能監(jiān)視代碼進(jìn)行AOP式的改造。
?一方面,我們從業(yè)務(wù)類ForumServiceImpl中刪除性能監(jiān)視的橫切代碼,使ForumServiceImpl只負(fù)責(zé)具體的業(yè)務(wù)邏輯,如所示:代碼清單5ForumServiceImpl:移除性能監(jiān)視橫切代碼packagecom.baobaotao.proxy;
publicclassForumServiceImplimplementsForumService{
publicvoidremoveTopic(inttopicId){
①
System.out.println("模擬刪除Topic記錄:"+topicId);?
try{
Thread.currentThread().sleep(20);?
}catch(Exceptione){?
thrownewRuntimeException(e);
}
②
}?
publicvoidremoveForum(intforumId){?
①
System.out.println("模擬刪除Forum記錄:"+forumId);?
try{?
Thread.currentThread().sleep(40);
}catch(Exceptione){
thrownewRuntimeException(e);?
}
②?
}?}在代碼清單5中的①和②處,本來的性能監(jiān)視代碼被移除了,我們只保存了真正的業(yè)務(wù)邏輯。
?從業(yè)務(wù)類中移除的橫切代碼當(dāng)然還得找到一個(gè)寄居之所,InvocationHandler就是橫切代碼的家園樂土,我們將性能監(jiān)視的代碼安頓在PerformaceHandler中,如代碼清單6所示:代碼清單6PerformaceHandlerpackagecom.baobaotao.proxy;
importjava.lang.reflect.Invocat(yī)ionHandler;?importjava.lang.reflect.Method;publicclassPerformaceHandlerimplementsInvocat(yī)ionHandler{
privateObjecttarget;?
publicPerformaceHandler(Objecttarget){//①target為目的的業(yè)務(wù)類?
this.target=target;
}?
publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)
throwsThrowable{?
PerformanceMonitor.begin(target.getClass().getName()+"."+method.getName());?
Objectobj=method.invoke(target,args);//②通過反射方法調(diào)用目的業(yè)務(wù)類的業(yè)務(wù)方法?
PerformanceMonitor.end();?
returnobj;?
}?}粗體部分的代碼為性能監(jiān)視的橫切代碼,我們發(fā)現(xiàn),橫切代碼只出現(xiàn)一次,而不是本來那樣星灑各處。大家注意②處的method.invoke(),該語句通過反射的機(jī)制調(diào)用目的對象的方法,這樣Invocat(yī)ionHandler的invoke(Objectproxy,Methodmethod,Object[]args)方法就將橫切代碼和目的業(yè)務(wù)類代碼編織到一起了,所以我們可以將InvocationHandler當(dāng)作是業(yè)務(wù)邏輯和橫切邏輯的編織器。下面,我們對這段代碼做進(jìn)一步的說明。一方面,我們實(shí)現(xiàn)InvocationHandler接口,該接口定義了一個(gè)invoke(Objectproxy,Methodmethod,Object
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 廣告用品采購合同范例
- 天花鋁扣板購銷合同范例
- 房產(chǎn)委托開發(fā)合同范例
- 異地搬家托運(yùn)服務(wù)合同范例
- 學(xué)校購買校服合同范例
- 基建變安裝合同范例
- 夫妻遺產(chǎn)分配合同范例
- 《流域開發(fā)與治理》課件
- 修理勞務(wù)合同范例
- 事故處理合同范例
- 南京交通職業(yè)技術(shù)學(xué)院單招職測參考試題庫(含答案)
- 班前晨會內(nèi)容及安全注意事項(xiàng)
- 焊接專業(yè)大學(xué)生職業(yè)生涯規(guī)劃
- T-NAHIEM 101-2023 急診科建設(shè)與設(shè)備配置標(biāo)準(zhǔn)
- 教育部《中小學(xué)德育工作指南》-德育工作指南
- 建筑保溫材料生產(chǎn)加工項(xiàng)目建設(shè)方案
- 2016-2023年太原幼兒師范高等??茖W(xué)校高職單招(英語/數(shù)學(xué)/語文)筆試歷年參考題庫含答案解析
- IT運(yùn)維述職報(bào)告
- 外科學(xué)總論-腫瘤課件
- 鐵路違章分析報(bào)告
- 液化石油氣供應(yīng)工程設(shè)計(jì)規(guī)范(局部修訂征求意見稿)
評論
0/150
提交評論