JVM,JAVA虛擬機(jī),內(nèi)存機(jī)制,線程_第1頁(yè)
JVM,JAVA虛擬機(jī),內(nèi)存機(jī)制,線程_第2頁(yè)
JVM,JAVA虛擬機(jī),內(nèi)存機(jī)制,線程_第3頁(yè)
JVM,JAVA虛擬機(jī),內(nèi)存機(jī)制,線程_第4頁(yè)
JVM,JAVA虛擬機(jī),內(nèi)存機(jī)制,線程_第5頁(yè)
已閱讀5頁(yè),還剩16頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

JVM的機(jī)制學(xué)習(xí)綱行理管存內(nèi)程線大執(zhí)的碼代代碼編譯為classSunjdk中的javac裝載class執(zhí)彳亍class內(nèi)存空間內(nèi)存回收ClassLoaderClientcompilerServercompiler方法區(qū),堆,JVM方法

棧,本地方法棧,PC寄存

器堆上分配棧上分配■-—TLAB分配.-算法Copying■,—內(nèi)存分配算*Mark-Sweep....Mark-Compact新生代可用GC分代回收J(rèn)DK實(shí)現(xiàn)—GC參數(shù)內(nèi)存狀態(tài)分析G1Jconsole,jstat,jmap,MAT同步交互狀態(tài)并行回收GC并行GCMinorGC觸發(fā)機(jī)制/一及日志串行Mark-Sweep-Compact舊生代GCFullGC并行。。mpact-■-'并發(fā)Mark-SweepJVM標(biāo)準(zhǔn)結(jié)構(gòu)類(lèi)加載子系統(tǒng)內(nèi)存空間Java代碼執(zhí)行機(jī)制源碼編譯機(jī)制JVM規(guī)范中定義了class文件的格式,JDK在編譯java源碼時(shí),使用了javac,javac編譯的步驟:分析和輸入到符號(hào)表(ParseAndEnter)Parse做的是詞法和語(yǔ)法的分析。詞法分析:將代碼字符串轉(zhuǎn)變?yōu)閠oken序列語(yǔ)法分析:將根據(jù)語(yǔ)法由token序列生成抽象語(yǔ)法樹(shù)Enter將符號(hào)輸入到符號(hào)表,通常包括確定類(lèi)的超類(lèi)和接口,添加默認(rèn)構(gòu)造器等。注解處理語(yǔ)義分析和生成class文件通常生成class文件不知包括字節(jié)碼,一般包括結(jié)構(gòu)信息,元數(shù)據(jù),方法信息。下面是一個(gè)例子:Compiledfrom"Foo.java"〃類(lèi)/繼承的超類(lèi)/實(shí)現(xiàn)的接口的聲明信息publicclassFooextendsjava.lang.ObjectSourceFile:"Foo.java"http://class文件格式版本號(hào),majorversion50表示jdk6,49為jdk5只有高版本能執(zhí)行低版本的class文件minorversion:0

majorversion:50〃常量池,存放了所有的Field名稱(chēng),方法名,方法簽名,類(lèi)型名,代碼及class文件中的常量值Constantpool:const#1=Method#7.#27;//java/lang/Object."<init>":()Vconst#2=Fieldconst#3=class#6.#28;//Foo.count:I#29;//java/lang/Exceptionconst#4=Stringconst#5=Methoding;)Vconst#6=class#30;//countoverflow#3.#31;//java/lang/Exception."<init>":(Ljava/lang/Str#32;//Fooconst#7=class#33;//java/lang/Objectconst#8=Ascizconst#9=AscizMAX_COUNT;I;const#10=Ascizconst#11=int1000;const#12=Ascizconst#13=AscizConstantValue;count;<init>;const#14=Ascizconst#15=Asciz()V;Code;const#16=Ascizconst#17=AscizLineNumberTable;LocalVariableTable;const#18=Ascizconst#19=Ascizthis;LFoo;;const#20=Ascizconst#21=Ascizbar;()I;const#22=Ascizconst#23=AscizStackMapTable;Exceptions;const#24=Ascizconst#25=Asciz<clinit>;SourceFile;const#26=AscizFoo.java;const#27=NameAndType#13:#14;//"<init>":()Vconst#28=NameAndType#12:#9;//count:Iconst#29=Ascizjava/lang/Exception;const#30=Ascizcountoverflow;const#31=NameAndType#13:#34;//"<init>":(Ljava/lang/String;)Vconst#32=Ascizconst#33=Ascizconst#34=AscizFoo;java/lang/Object;(Ljava/lang/String;)V;{//將符號(hào)輸入到符號(hào)表時(shí)生成的默認(rèn)構(gòu)造器方法publicFoo();Signature:()VLineNumberTable:line1:0LocalVariableTable:StartLengthSlotNameSignature050thisLFoo;Code:Stack=1,Locals=1,Args_size=10:aload_01:invokespecial#1;//Methodjava/lang/Object."<init>":()V4:returnLineNumberTable:line1:0LocalVariableTable:StartLengthSlotNameSignature050thisLFoo;//bar方法的元數(shù)據(jù)信息publicintbar()throwsjava.lang.Exception;Signature:()I//對(duì)應(yīng)字節(jié)碼的源碼行號(hào)信息,可在編譯的時(shí)候通過(guò)-g:none去掉行號(hào)信息,行號(hào)信息對(duì)于查找問(wèn)題而言至關(guān)重要,因此最好保留LineNumberTable:line5:0line6:15line7:19line9:29//局部變量信息,如生成的class文件中無(wú)局部變量信息,則無(wú)法知道局部變量的名稱(chēng),并且局部變量信息是和方法綁定的,接口是沒(méi)有方法體的,所以ASM之類(lèi)的在獲取接口方法時(shí),是拿不到方法中參數(shù)的信息的LocalVariableTable:StartLengthSlotNameSignature0330thisLFoo;Code:Stack=3,Locals=1,Args_size=1〃對(duì)應(yīng)方法的字節(jié)碼0:getstatic#2;//Fieldcount:I3:iconst_14:iadd5:dup6:putstatic#2;//Fieldcount:I9:sipush100012:if_icmplt2915:iconst_016:putstatic#2;//Fieldcount:I19:new#3;//classjava/lang/Exception22:dup23:ldc#4;//Stringcountoverflow25:invokespecial#5;//Methodjava/lang/Exception."<init>":(Ljava/lang/String;)V28:athrow29:getstatic#2;//Fieldcount:I32:ireturnLineNumberTable:line5:0line6:15line7:19line9:29LocalVariableTable:StartLengthSlotNameSignature0330thisLFoo;〃記錄有分支的情況(對(duì)應(yīng)代碼中的if???for,while等)StackMapTable:number_of_entries=1frame_type=29/*same*/〃異常處理表Exceptions:throwsjava.lang.Exceptionstatic{};Signature:()VLineNumberTable:line3:0Code:Stack=1,Locals=0,Args_size=00:iconst_01:putstatic#2;//Fieldcount:I4:returnLineNumberTable:line3:0}類(lèi)加載生成的class文件,可被JVM裝載,并形成Class對(duì)象的機(jī)制,稱(chēng)為類(lèi)加載機(jī)制之后就可對(duì)Class對(duì)像進(jìn)行實(shí)例化并調(diào)用,類(lèi)加載機(jī)制可在運(yùn)行時(shí)動(dòng)態(tài)加載外部的類(lèi)。JVM的類(lèi)加載分為三步:裝載,鏈接,初始化裝載和鏈接之后,即將二進(jìn)制字節(jié)碼轉(zhuǎn)化成Class對(duì)象初始化則不是加載時(shí),必須的步驟,最遲可在首次調(diào)用對(duì)象前執(zhí)行,行為包括給靜態(tài)變量賦值,調(diào)用<高血>()等裝載:裝載通過(guò)類(lèi)的全限定名+ClassLoader實(shí)例ID對(duì)其進(jìn)行標(biāo)識(shí)鏈接:對(duì)二進(jìn)制字節(jié)碼的格式進(jìn)行校驗(yàn),初始化裝載類(lèi)中的靜態(tài)變量及解析類(lèi)中調(diào)用的接口,類(lèi)初始化:執(zhí)行類(lèi)中的靜態(tài)初始化代碼,構(gòu)造器代碼及靜態(tài)屬性的初始化,以下情況初始化會(huì)被觸發(fā):調(diào)用了new反射調(diào)用了類(lèi)中的方法子類(lèi)調(diào)用了初始化JVM啟動(dòng)過(guò)程中指定的初始化類(lèi)類(lèi)執(zhí)行字節(jié)碼解釋執(zhí)行在源碼編譯階段將源碼編譯成JVM字節(jié)碼后,要由JVM在運(yùn)行期對(duì)其進(jìn)行解釋并執(zhí)行,稱(chēng)為字節(jié)碼解釋執(zhí)行方式對(duì)于字節(jié)碼JVM有自己的執(zhí)行指令,invokestatic,invokevirtual,invokeinterface,ipvokespecial,分別對(duì)應(yīng)static方法,對(duì)象實(shí)例方法,接口方法,private方法/編譯源碼后生成的<init>方法的調(diào)用。eg:publicclassDemo(publicvoidexecute()(A.execute();Aa=newA();bar();IFoob=newB();bar();}}classA(publicstaticintexecute()(return1+2;}publicintbar()(return1+2;}}classBimplementsIFoo(publicintbar()(return1+2;}}interfaceIFoo(publicintbar();}對(duì)上面的代碼編譯,然后javapcDemo查看其execute方法字節(jié)碼Compiledfrom"Demo.java"publicclassDemoextendsjava.lang.Object(publicDemo();Code:0:aload_01:invokespecial#1;//Methodjava/lang/Object."<init>":()V4:returnpublicvoidexecute();Code:0:invokestatic#2;//MethodA.execute:()I3:pop4:new#3;//classA7:dup8:invokespecial#4;//MethodA."<init>”:()V11:astore_112:aload_113:invokevirtual#5;//MethodA.bar:()I16:pop17:new#6;//classB20:dup21:invokespecial#7;//MethodB."<init>”:()V24:astore_225:aload_226:invokeinterface#8,1;//InterfaceMethodIFoo.bar:()I31:pop32:return}上面可以看到4個(gè)指令的使用情況JDK棧體系線程創(chuàng)建后,產(chǎn)生PC(程序計(jì)數(shù)器)和棧,PC:存放了下一條要執(zhí)行的指令在方法內(nèi)的便宜量;棧:棧中存放了棧幀(StackFrame),每個(gè)方法的每次調(diào)用都會(huì)產(chǎn)生棧幀棧幀主要分為局部變量去和操作數(shù)棧兩部分,局部變量區(qū)用于存放方法中局部變量和參數(shù)操作數(shù)棧用于存放方法執(zhí)行過(guò)程中產(chǎn)生的中間結(jié)果,棧幀中還有一些雜用控件,指向方法已解析的常量池的引用,其他一些VM內(nèi)部實(shí)現(xiàn)需要的數(shù)據(jù)eg:publicclassDemo1(publicstaticvoidfoo()(inta=1;intb=2;intc=(a+b)*5;}}編譯Demo1.java查看字節(jié)碼:Compiledfrom"Demo1.java"publicclassDemo1extendsjava.lang.Object(publicDemo1();Code:0:aload_01:invokespecial#1;//Methodjava/lang/Object."<init>":()V4:returnpublicstaticvoidfoo();Code:0:iconst_1//將類(lèi)型為int,值為1的常量放入操作數(shù)棧1:istore_0//將操作數(shù)棧中棧頂?shù)闹祻棾龇湃刖植孔兞繀^(qū)2:iconst_2//將類(lèi)型為int,值為2的常量放入操作數(shù)棧3:istore_1//將操作數(shù)棧中棧頂?shù)闹祻棾龇湃刖植孔兞繀^(qū)4:iload_0//裝載局部變量區(qū)中的第一個(gè)值到操作數(shù)棧5:iload_1//裝載局部變量區(qū)中的第一個(gè)值到操作數(shù)棧6:iadd〃執(zhí)行int類(lèi)型的add指令,并將結(jié)果放入操作數(shù)棧7:iconst_5//將類(lèi)型為int,值為5的常量放入操作數(shù)棧8:imul〃執(zhí)行int類(lèi)型的mul指令,并將結(jié)果放入操作數(shù)棧9:istore_2//將操作數(shù)棧中棧頂?shù)闹祻棾龇湃刖植孔兞繀^(qū)10:return//返回}對(duì)于方法指令的解釋執(zhí)行,執(zhí)行方式為經(jīng)典馮。諾依曼體系中的FDX循環(huán)方式,即獲取下一條指令,解碼并分派,然后執(zhí)行。在實(shí)現(xiàn)FDX循環(huán)時(shí)有switch-threading,token-threading,direct-threading,subroutine-threading,inline-threading等多種方式switch-threading,最簡(jiǎn)單:while(true)(intcode=fetchNextCode();switch(code)(caseIADD://doaddcase…://dosth}}〃每次執(zhí)行完后回到起點(diǎn),重新獲取下一條,并繼續(xù)switch,大部分時(shí)間都花在跳轉(zhuǎn)和獲取下一條上token-threading:在switch-threading上稍作改進(jìn)IADD:{//doadd;fetchNextCode();dispatch();}ICOUNT_0:{push(0);fetchNextCode();dispatch();}雖然冗余了fetchNextCode和dispatch,消耗內(nèi)存大一些,但由于去除了switch,性能會(huì)好一些SUNJDK的重點(diǎn)為編譯成機(jī)器碼,因此采用了token-threading,讓解釋執(zhí)行更高效,還做了其他的優(yōu)化:棧頂緩存(top-of-stackcaching)和部分棧幀共享?xiàng)m斁彺妫阂驗(yàn)樵诤芏嗖僮髦幸獙⒅捣湃氩僮鲾?shù)棧,導(dǎo)致了寄存器和內(nèi)存的不斷交換數(shù)據(jù),SunJDK采用了一個(gè)棧頂緩存,將本來(lái)放在操作數(shù)棧頂?shù)闹稻彺嬖诩拇嫫魃?,這樣對(duì)于大多只要一個(gè)值的操作而言,不需要將數(shù)據(jù)放入操作數(shù)棧,可在寄存器中計(jì)算,完了再放回操作數(shù)棧部分棧幀共享:當(dāng)一個(gè)方法調(diào)用另一個(gè)方法時(shí),傳入另一個(gè)方法的參數(shù)已經(jīng)放在了操作數(shù)棧中,SunJDK做了優(yōu)化,當(dāng)調(diào)用方法時(shí),后一方法可將前一方法的操作數(shù)棧作為當(dāng)前方法的局部變量,從而節(jié)省數(shù)據(jù)copy帶來(lái)的消耗編譯執(zhí)行因?yàn)榻忉寛?zhí)行效率低,所以為了提升性能,SunJDK將字節(jié)碼編譯成機(jī)器碼,編譯在運(yùn)行時(shí)進(jìn)行,稱(chēng)為JIT編譯器。SunJDK在執(zhí)行過(guò)程中對(duì)執(zhí)行頻率高的代碼進(jìn)行編譯,對(duì)執(zhí)行不頻繁的代碼進(jìn)行解釋?zhuān)诰幾g上SunJDK提供了兩種方式:clientcompiler(-client),servercompiler(-server)clientcompiler:又稱(chēng)為C1,只做少量性能開(kāi)銷(xiāo)比高的優(yōu)化,占用內(nèi)存少,適合于桌面交換式應(yīng)用,其他方面的優(yōu)化有方法內(nèi)聯(lián),去虛擬化,冗余消除方法內(nèi)聯(lián):是在調(diào)用方法時(shí),可直接將調(diào)用的方法的指令直接植入到當(dāng)前方法中,通過(guò)-XX:MaxInlineSize=35可以配置當(dāng)前允許內(nèi)聯(lián)的方法的大小,<=35字節(jié)的方法都可內(nèi)聯(lián)。去虛擬化:在裝載class文件后,進(jìn)行類(lèi)層次分析,如發(fā)現(xiàn)類(lèi)中的方法只提供一個(gè)實(shí)現(xiàn)類(lèi)時(shí),那么對(duì)于調(diào)用方法的代碼,也可進(jìn)行內(nèi)聯(lián)冗余消除:是指在編譯時(shí),根據(jù)運(yùn)行時(shí)狀況進(jìn)行代碼的折疊或消除,eg:if(false){...}這樣的代碼就消除了servercompiler:又稱(chēng)C2,是較重量級(jí)的,占用內(nèi)存較多,適合于服務(wù)器端,不同于C1的局部(方法塊)的優(yōu)化,C2更多在全局的優(yōu)化,收集的主要信息有:分支的跳軸不跳轉(zhuǎn)的頻率,某條指令出現(xiàn)過(guò)的類(lèi)型,是否出現(xiàn)過(guò)空值,是否出現(xiàn)過(guò)異常逃逸分析是C2進(jìn)行很多優(yōu)化的基礎(chǔ),是指根據(jù)運(yùn)行狀況判斷方法中的變量是否會(huì)被外部讀取,不會(huì)則認(rèn)為是不逃逸的,基于逃逸分析C2編譯時(shí)會(huì)做標(biāo)量替換,棧上分配和同步消除等標(biāo)量替換:簡(jiǎn)單說(shuō)就是用標(biāo)量替換聚合量eg:Pointpoint=newPoint();System.out.println("point.x=''+point.x+'';point.y="+point.y);在這里如果point沒(méi)有被后面的代碼調(diào)用到,則經(jīng)過(guò)編譯后,代碼變成:intx=1;inty=2;System.out.println("point.x=''+x+'';point.y="+y);之后,基于此可再進(jìn)行冗余消除這種方式的好處是,如果創(chuàng)建的對(duì)象未用到全部的變量,則節(jié)省了內(nèi)存,代碼執(zhí)行時(shí),不需要找對(duì)象的引用,快一點(diǎn)棧上分配:在上面的例子中,如果p沒(méi)有逃逸,則C2會(huì)選擇在棧上創(chuàng)建對(duì)象實(shí)例,而不是JVM堆中,好處是一方面更快,另一方面回收時(shí)隨著方法的結(jié)束,對(duì)象也被回收。同步削除:在變量沒(méi)有逃逸時(shí),同步是不需要的,C2會(huì)直接去掉同步其他優(yōu)化:逆優(yōu)化:當(dāng)運(yùn)行后C1,C2編譯出來(lái)的機(jī)器碼不再符合優(yōu)化條件,則會(huì)進(jìn)行逆優(yōu)化(deoptimization),也就是回到解釋執(zhí)行的方式,例如基于類(lèi)層次分析編譯的代碼,在加入新的接口實(shí)現(xiàn)類(lèi)時(shí),就執(zhí)行逆優(yōu)化其他編譯方式:OSR:OSR和C1,C2的不同在于,C1,C2基于方法入口進(jìn)行優(yōu)化,替換方法調(diào)用的入口,而OSR是替換循環(huán)代碼的入口,會(huì)出現(xiàn)編譯后方法的整段代碼被編譯了,只有循環(huán)部分是機(jī)器碼,其他仍是解釋執(zhí)行可以通過(guò)-client,-server進(jìn)行自主選擇編譯方式,默認(rèn)JDK會(huì)根據(jù)機(jī)器的狀況選擇合適的方式為什么不在啟動(dòng)時(shí)將代碼編譯成機(jī)器碼?靜態(tài)編譯不能根據(jù)運(yùn)行狀況來(lái)優(yōu)化執(zhí)行的代碼,C2是根據(jù)運(yùn)行狀況來(lái)進(jìn)行動(dòng)態(tài)編譯的,C2收集數(shù)據(jù)的時(shí)間越長(zhǎng),編譯出來(lái)的代碼會(huì)越優(yōu)解釋執(zhí)行比編譯更節(jié)省內(nèi)存啟動(dòng)時(shí)解釋執(zhí)行啟動(dòng)速度比編譯再啟動(dòng)更快但是未編譯時(shí),解釋執(zhí)行慢,因此需要一個(gè)閥值,用來(lái)給JVM判斷JDK中主要有兩個(gè)計(jì)數(shù)器,一個(gè)為調(diào)用計(jì)數(shù)器,計(jì)算方法調(diào)用次數(shù);另一個(gè)是回邊計(jì)數(shù)器,計(jì)算循環(huán)次數(shù)CompileThreshold:可通過(guò)-XX:CompileThreshold=10000來(lái)設(shè)置,在超過(guò)閥值時(shí)就編譯成機(jī)器碼,在client模式下為1500,server模式下為10000OnStackReplacePercentage:該值用于判斷是否啟動(dòng)OSR編譯,閥值client默認(rèn)為933,server為140,可通過(guò)-XX:OnStackReplacePercentage=140來(lái)設(shè)置,閥值:在client模式下為公式CompileThreshold*(OnStackReplacePercentage/100),在server模式下(CompileThreshold*(OnStackReplacePercentage-InterpreterProfilePercentage))/100,InterpreterProfilePercentage默認(rèn)值為33,當(dāng)觸發(fā)了OSR后,調(diào)用計(jì)數(shù)器設(shè)置為CompileThreshold的值,回邊計(jì)數(shù)器設(shè)置為CompileThreshold/2的值

JVM內(nèi)存管理內(nèi)存管理PC寄存器局部變量區(qū)操作數(shù)棧-棧幀JVM方法棧JVM內(nèi)存結(jié)構(gòu)JVM內(nèi)存管理內(nèi)存管理PC寄存器1.方法區(qū)存放了類(lèi)的信息(名稱(chēng),修飾符等),包括類(lèi)的靜態(tài)屬性,final類(lèi)型的常量,F(xiàn)ield信息,方法信息,這些是全局共享的,是會(huì)被GC的在JDK中這塊區(qū)域?qū)?yīng)PermanetGeneration,又成為持久代,默認(rèn)大小16MB,最大值64MB,可通過(guò)-XX:PermSize和-XX:MaxPermSize來(lái)指定。當(dāng)所需的內(nèi)存大小超出其允許范圍時(shí),拋出OutOfMemoryError2.堆用于存放對(duì)象實(shí)例和數(shù)組值??梢哉J(rèn)為所有通過(guò)new創(chuàng)建的對(duì)象的內(nèi)存都在此分配,Heep對(duì)象所占內(nèi)存由GC回收??赏ㄟ^(guò)-Xms和-Xmx來(lái)設(shè)置大小,當(dāng)Heep的空內(nèi)存不到40%時(shí),會(huì)增大內(nèi)存到-Xmx設(shè)置值,可通過(guò)-XX:MinHeapFreeRatio來(lái)改變比例,當(dāng)空內(nèi)存超過(guò)70%時(shí),減小到到-Xms設(shè)置值,可通過(guò)-XX:MaxHeapFreeRatio來(lái)改變比例,對(duì)于運(yùn)行的系統(tǒng),為了避免頻繁的更改Heep的大小,通常將-Xms和-Xmx設(shè)成一樣為了讓內(nèi)存的回收更有效,SunJDK采用了分代管理的方式......NewGenerationEdenS0S1OldGeneration-HeepJVM堆的分代NewGeneration:在Java中大部分new的對(duì)象都在NewGeneration中分配,由EdenSpace和兩塊大小相同的SurvivorSpace組成OldGeneration:當(dāng)在NewGeneration經(jīng)過(guò)多次GC還存活的對(duì)象將移到Old中,如緩存對(duì)象,新對(duì)象也可能在Old上分配,主要有兩種狀況:1.大對(duì)象,超過(guò)了-XX:PretenureSizeThreshold=1024設(shè)置值,2.大數(shù)組對(duì)象,沒(méi)有引用外部對(duì)象本地方法棧用于支持native方法的執(zhí)行,存放native方法的信息,在SunJDK中本地方法棧和JVM方法棧是同一個(gè)PC寄存器和JVM方法棧每個(gè)線程都會(huì)創(chuàng)建PC寄存器和JVM方法棧,PC寄存器占用CPU寄存器和操作系統(tǒng)內(nèi)存,JVM方法棧占用內(nèi)存,JVM方法棧為線程私有,當(dāng)方法執(zhí)行完畢后,其棧幀占用的內(nèi)存也會(huì)釋放。當(dāng)JVM方法??臻g不足時(shí),拋出StackOverflowError,在SunJDK中通過(guò)-Xss來(lái)設(shè)置內(nèi)存分配Java對(duì)象占用的內(nèi)存主要在堆上進(jìn)行分配,堆為所有線程共享的,因此分配內(nèi)存時(shí)需要加鎖,因此創(chuàng)建對(duì)象開(kāi)銷(xiāo)較大。SunJDK為了提高內(nèi)存分配效率,為每個(gè)新線程在EdenSpace中分配一塊獨(dú)立的空間,叫做TLAB,其大小由JVM運(yùn)行時(shí)計(jì)算而得,通過(guò)配置-XX:TLABWasteTargetPercent來(lái)設(shè)置占用EdenSpace的比例,默認(rèn)1%,JVM將根據(jù)這個(gè)比率,線程數(shù)量及線程是否頻繁分配對(duì)象來(lái)給每個(gè)線程分配合適的大小。因?yàn)樵赥LAB中分配內(nèi)存不需要加鎖,因此JVM中給線程中的對(duì)象分配內(nèi)存都盡量在TLAB中,如果對(duì)象過(guò)大或TLAB不夠,則仍在堆上分配。當(dāng)然,還有Java對(duì)象在棧上分配的情況,前面已經(jīng)提過(guò)了。內(nèi)存回收收集器GC的一般實(shí)現(xiàn),是通過(guò)收集器,收集器主要有引用計(jì)數(shù)收集器和跟蹤收集器。引用計(jì)數(shù)收集器:采用分散式的管理方式,記錄對(duì)象是否被引用,當(dāng)計(jì)數(shù)器為0時(shí),可GC。缺點(diǎn):每次對(duì)象賦值時(shí),要進(jìn)行計(jì)數(shù)器加減,有消耗,而且對(duì)于循環(huán)賦值的情況,不能使用。跟蹤計(jì)數(shù)器:采用了集中式的管理方式,全局記錄數(shù)據(jù)的引用狀態(tài)。當(dāng)一定條件時(shí)啟動(dòng)GC(如空間不足,定時(shí)等),執(zhí)行時(shí)從根集合來(lái)掃描對(duì)象的引用關(guān)系。采用的算法有:復(fù)制,標(biāo)記-清除,標(biāo)記-壓縮復(fù)制:掃描,找到所有存活的對(duì)象,提供一塊空間,復(fù)制所有的對(duì)象進(jìn)去。缺點(diǎn):需要額外的空間,需要移動(dòng)對(duì)象。適用情況:當(dāng)存活對(duì)象較少時(shí)。標(biāo)記-清除:掃描,對(duì)所有存活對(duì)象進(jìn)行標(biāo)記,然后清除所有未標(biāo)記對(duì)象,不需要額外的移動(dòng)對(duì)象。缺點(diǎn):會(huì)產(chǎn)生空間碎片適用:當(dāng)存活對(duì)象較多時(shí)。標(biāo)記-壓縮:標(biāo)記過(guò)程與標(biāo)記-清除一樣,只不過(guò)清除后,將像左移動(dòng)所有存活對(duì)象,缺點(diǎn):額外的移動(dòng)優(yōu)點(diǎn):不會(huì)產(chǎn)生空間碎片SunJDK中可用的GCGC方式新生代可用GC:SunJDK認(rèn)為大部分的新生代中對(duì)象存活時(shí)間較短,因此使用Copying算法來(lái)對(duì)新生代進(jìn)行GC,新生代分為三部分:Eden用于存放新建的對(duì)象,S0和S1其中一塊在MinorGC觸發(fā)時(shí)作為Copying的目標(biāo)空間,另一塊清空。因此通常將S0和S1稱(chēng)為,F(xiàn)romSpace和ToSpace,SunJDK提供了串行GC,并行回收GC,并行GC三種方式來(lái)GC,對(duì)新生代對(duì)象所占內(nèi)存進(jìn)行的GC又通常稱(chēng)為MinorGC。1.串行GC:(NG代表NewGeneration,OG代表OldGeneration)Ng的內(nèi)存分配采用了空閑指針的方式,指針保持最后一個(gè)分配對(duì)象在NG中的位置,當(dāng)有新的對(duì)象要分配時(shí),如果有剩余空間則分配,更新指針,如果沒(méi)有,則MinorGCMinorGC用了Copying算法,要查找所有根集合中的存活對(duì)象,SunJDK認(rèn)為根集合為:當(dāng)前運(yùn)行的線程棧上引用的對(duì)象,常量和靜態(tài)變量,傳到本地方法中,但是沒(méi)有被釋放的對(duì)象。MinorGC僅從根集合上得到對(duì)象是不夠的,當(dāng)OG上的對(duì)象引用了NG的對(duì)象,則出現(xiàn)問(wèn)題,OG較大,不可能每次MinorGC都去掃描OG,因此SunJDK使用了remembersetrememberset:當(dāng)對(duì)象進(jìn)行賦值時(shí),如果賦值的是一個(gè)對(duì)象引用,則檢查要賦值的對(duì)象是否在OG,而賦值對(duì)象是否在NG,如果是則,在rememberset中記錄。所以對(duì)MinorGC而言,完整的根集合是SunJDK認(rèn)為的根集合加上rememberset中的標(biāo)記對(duì)象。同時(shí)為了避免在GC時(shí),對(duì)象關(guān)系的變化,SunJDK在編譯時(shí)設(shè)置了SafePoint,即在循環(huán)和方法執(zhí)行結(jié)束處,如果要執(zhí)行MinorGC,要等所有線程進(jìn)入SafePoint。前面提到,當(dāng)NG中的多次GC,還存活的對(duì)像放入OG中,這個(gè)次數(shù)在MinorGC下是有參數(shù)-XX:PretenureSizeThreshold來(lái)決定,同時(shí)可以在運(yùn)行時(shí),加上-XX+UseSerialGC來(lái)強(qiáng)制指定GC方式。對(duì)象的引用關(guān)系:默認(rèn)是強(qiáng)引用:Aa=newA();只有在主動(dòng)釋放了引用后,才會(huì)GC。其他引用方式:軟引用:SoftReference弱引用:WeakReference虛引用:PhantomReferenceMinorGC:對(duì)于單CPU系統(tǒng),比較適用。2并行回收GCEden,S0,S1的大?。耗J(rèn)采用的是InitialSurvivorRatio來(lái)進(jìn)行配置:此值的大小是NG/survivorspace,在SunJDK1.6之后,加入了SurvivorRatio參數(shù),此值大小為edenspace/survivorspace在并行回收時(shí),默認(rèn)采用InitialSurvivorRatio,如果只設(shè)了SurvivorRatio則并行回收GC時(shí),將SurvivorRatio值+2賦給InitialSurvivorRatio,同時(shí)配置的話,已InitialSurvivorRatio為準(zhǔn),因此采用并行回收時(shí),如果-Xmn為16MB,則默認(rèn)edenspace為12MB,survivorspace各2MB,如果SurvivorRatio配置為8,則denspace為12。8MB,survivorspace各1.6MB。對(duì)于并行回收GC,在啟動(dòng)時(shí)Eden,S0,S1按照上面的方式進(jìn)行分配,但在運(yùn)行一段時(shí)間后,并行回收GC會(huì)根據(jù)MinorGC的頻率、消耗時(shí)間等來(lái)動(dòng)態(tài)調(diào)整Eden,S0,S1的大小,可通過(guò)-XX:-UseAdaptiveSizePolicy來(lái)固定Eden,S0,S1的大小。PSGC不是根據(jù)-XX:PretenureSizeThreshold來(lái)決定是否在OG上存放新對(duì)象,而是在分配時(shí),如果Eden不夠大,此對(duì)象的大小〉=Edenspace大小的一半,就在OG上分配.eg:pufalicclassPSGCDirect01dDeino{pufalicstaticvoidmain(String[]args)throwsException{byte[]bytes=newbyte[1024*1024*2];byte[]bytes2=newbyte[1024*1024*2];byte[]bytes3=newbyte[1024*1024*2];System,out.printIn(,rreadytodirectallocatetoold");Thread,sleep(30000);//jstat^WSystem,out.println(,rdoit1);byte[]]oytes4=newbyte[1024*1024*4];PSGC(并行回收GC)測(cè)試,以參數(shù)java-Xms20M-Xmn10M-Xmx20M-XX:SurvivorRatio=8-XX:+UseParallelGCPSGCDirectOldDemo運(yùn)行例子,并以jstat來(lái)查看兩次,看到bytes4數(shù)組分配在OG(OldGeneration)上jstat-gcpid;查看結(jié)果:

YGCYGCTFGCFGCTGCT00.00000.0000.000YGCTFGCYGCYGCTFGCFGCTGCT00.00000.0000.000YGCTFGCFGCTGCT0.00000.0000.00011024.0102^.00.00.08192.06471.910240.00.012288.02036.^第二次:|SueSICSUUSIUECEUOCOUPCPUYGC1024.01024.00.00.08192.06471.910240.04096.012288.02036.50分析:第一次時(shí)OU為0,OG上的占用空間為0,EC為Edenspace的空間大小,要分配bytes4時(shí),bytes4的大小為4096=EC/2,所以分配在了OG上,因此第二次查看時(shí),OU=4096.0.3.并行GC(ParNew)并行GC在EdenS0,S1的大小分配上和串行GC是一樣的。和PSGC的區(qū)別在于并行GC配合OG使用CMSGC,CMSGC在進(jìn)行OGGC時(shí),有些過(guò)程是并發(fā)的,如此時(shí)發(fā)生MinorGC,需要做一些處理,而PSGC沒(méi)有這些處理。在Eden上分配內(nèi)存不足時(shí),JVM即觸發(fā)MinorGC,也可在程序中手動(dòng)System.gc調(diào)用示例MinorGC:publicclassMinorGCDenio{publicstaticvoidmain(String[]args)throwsException(Thread,sleep(10000);F/1給啟動(dòng)jmt■日仁肘|可MenioryObjectobject=newMeinoryObject(1024*1024);for(inti=0;i:2;i+4){happenMinorGC(11);Thread.sleep(10000);privatestaticvoidhappenMinorGC(inthappenMinorGCIndex)throwsException(for(inti=0;i<happenMinorGCIndex;i-l-+){if(i==happenMinorGCIndex-1){Thread.sleep(10000);Systein.out.printIn(,ritiinorgcshouldhappenFF);newMenioryObject(1024*1024);classMemoryObject(privatebyte[]bytes;pub1icMenioryObject(intobjectSize)(this.bytes=newbytejectSize];}}javacMinorGCDemo.javajava-Xms40M-Xmx40M-Xmn16M-verbose:gc-XX:+PrintGCDetails這里分配整個(gè)JVM方法棧大小為40M,其中NewGeneration為16M,Eden為12M,兩個(gè)survivorspace各2M,OG為24M通過(guò)jstat-gcutilpid100010(間隔1s,查看10次)查看Eden,S0,S1,old在minorGC時(shí)的變化結(jié)果:Console:minorgcshouldhappen[GC[DefNew:1255lK->1159K(14784K),□.0025361secs]1255lK->1159K(39360K),0.0028512secs][Times:user=0.□□sys=0.00^real=0.00secs]minorgcshouldhappenHeapdefnewgeneration000)edenspacefromspacetospacetotal14784K,used12821K[Ox241eOOOO,Ox251eOOOO,Ox251eO13184K,Console:minorgcshouldhappen[GC[DefNew:1255lK->1159K(14784K),□.0025361secs]1255lK->1159K(39360K),0.0028512secs][Times:user=0.□□sys=0.00^real=0.00secs]minorgcshouldhappenHeapdefnewgeneration000)edenspacefromspacetospacetotal14784K,used12821K[Ox241eOOOO,Ox251eOOOO,Ox251eO13184K,1600K,1600K,tenuredgeneration88%used[0\24160000,0x24d43a50/Ox24ecOOOO)72%used[0X25050000,0x25171c48,Ox251eOOOO)0%used[0x24ec0000^Ox24ecOOOO^0x25050000)total24576K,usedOK[0x251e0000,OxaeSeOOOO,0x269e0000)Ithespace24576K^compactingpermgen□)thespacerospacerwspace12288K,8192K,12288K,jstat顯示:soS1E0PYGCYGCTFGCFGCTGCT0.00□.0087.442.96□0.000□0.000□.□□00.000.0095.212.97□0.000□0.000□.□□00.00□.0095.212.97□0.000□0.000□.□□00.0072.4478.692.9710.003□0.000□.□□30.0072.4478.692.9710.003□0.000□.□□30.0072.4486.462.9710.003□0.000□.□□3OG和PG可用的GCJDK提供了串行,并行,并發(fā)三種GC來(lái)對(duì)OG和PG占的內(nèi)存進(jìn)行GC。1.串行:串行基于標(biāo)記-清除-壓縮(Mark-Sweep-Compact)方式,在使用串行時(shí),OG的內(nèi)存分配和NG是一樣的。串行分三步:a)從根集合開(kāi)始掃描對(duì)象b)對(duì)未標(biāo)記的對(duì)象進(jìn)行清除c)執(zhí)行滑動(dòng)壓縮,將存活的對(duì)象向OG空間開(kāi)始處移動(dòng),最終在OG中留出一塊連續(xù)的空間到結(jié)尾處的空間。整個(gè)過(guò)程是單線程的,需要暫停應(yīng)用并行采用了Mark-Compact的方式,內(nèi)存分配上和串行相同并行GC分三步:a)根據(jù)CPU的數(shù)量,將OG分為多個(gè)region,并發(fā)掃描這些region,進(jìn)行標(biāo)記b)一般經(jīng)過(guò)多次GC,OG最左邊存放多是活躍的對(duì)象,從左往右掃描,找到第一個(gè)值得Compact的region,這個(gè)region左邊的區(qū)域認(rèn)為是denseprefix不進(jìn)行操作,然后繼續(xù)往右掃描,標(biāo)記region的源和目標(biāo),切換這些對(duì)象的指針,并在region上做標(biāo)志,同時(shí)清除regions中其他不存活對(duì)象的空間,此過(guò)程是單線程的c)根據(jù)上面的分析,找到目標(biāo)region,和完全沒(méi)有存活對(duì)象的region,并行進(jìn)行對(duì)象移動(dòng)和region回收并發(fā)(CMSConcurrentMark-SweepGC)因?yàn)镸ark-Sweep掃描整個(gè)OG需要較長(zhǎng)時(shí)間,SunJDK提供了CMSGC的方式,使大部分動(dòng)作與應(yīng)用并發(fā)的進(jìn)行。CMS用了Mark-Sweep的方式,在清除后,會(huì)產(chǎn)生很多個(gè)空白空間,它將這些空間在一個(gè)freelist中標(biāo)記,當(dāng)NG的新對(duì)象請(qǐng)求OG空間,在freelist中可以找到夠用的空間使用。同時(shí),因?yàn)镃MS與應(yīng)用并發(fā),會(huì)導(dǎo)致空間的分配和回收的同時(shí)進(jìn)行,導(dǎo)致freelist競(jìng)爭(zhēng)激烈,CMS為了避免這個(gè)現(xiàn)象,引入了Mutualexclusionlocks,以JVM分配內(nèi)存為先。整個(gè)CMS過(guò)程中要進(jìn)行三次的掃描,標(biāo)記。FullGC當(dāng)OG,PG觸發(fā)GC時(shí),出現(xiàn)對(duì)NG,OGPG都進(jìn)行GC的,稱(chēng)為FullGC。FullGC被觸發(fā)時(shí),NG先按配置進(jìn)行NG的GC,然后OG,PG進(jìn)行GC。但其中有一種情況,當(dāng)MinorGC前,NG移到OG的對(duì)象大于OG的剩余空間,則不進(jìn)行MinorGC,直接采用OG的GC方式對(duì)NG,OG,PG進(jìn)行GC。除了直接調(diào)用System.gc外,觸發(fā)FullGC的四種情況1.OG空間不足上面說(shuō)到的,轉(zhuǎn)入OG的對(duì)象太大,OG不足,則觸發(fā)FullGC,如果FullGC之后還不足,則拋出java.lang.OutOfMemoryError:Javaheapspace這種情況調(diào)優(yōu):不要?jiǎng)?chuàng)建過(guò)大對(duì)象和數(shù)組PG空間滿PG存放了class信息,當(dāng)系統(tǒng)要加載的類(lèi),反射的類(lèi)和調(diào)用的方法過(guò)多時(shí),PG滿,而且沒(méi)使用CMS的情況下,F(xiàn)ullGC使用。如果還不行,則拋出java.lang.OutOfMemoryError:PermGenspaceCMSGC出現(xiàn)promotionfailed和concurrentmodefailurepromotionfailed:MinorGC時(shí),survivorspace放不下,轉(zhuǎn)入OG,但是OG不足concurrentmodefailure:在CMSGC時(shí),同時(shí)有對(duì)象要放入OG,OG不足統(tǒng)計(jì)得到的MinorGC晉升到OG的平均大小大于OG的剩余空間小結(jié):client,server模式默認(rèn)GCNGOG&PGClient串行GC串行GCServer并行回收GC并行GCSunJDKGC組合方式NGOGPG-XX:+UseSerialGC串行GC串行GC-XX:+UseParallelGC并行回收GC并行GC-XX:+UseConcMarkSweepGC并行GC并發(fā)GC當(dāng)出現(xiàn)concurrentmodefailure時(shí)采用串行GC-XX:+UseParallelOldGC-XX:+UseConcMarkSweepGC-XX:+UseParNewGC并行回收GC串行GC并行GC并發(fā)GC當(dāng)出現(xiàn)concurrentmodefailure或promotionfailed時(shí)采用串行GC不支持的組合1.-XX:+UseParNewGC-XX:+UseParallelOldGC2.-XX:+UseParNewGC-XX:+UseSerialGC不支持的組合JVM內(nèi)存狀況查看方法和分析工具輸出GC日志將JVM支持的日志輸出到控制臺(tái)或文件,到Console:JVM啟動(dòng)參數(shù)加入:-XX:+PrintGC-XX:+PrintGCDetails-XX:+PrintGCTimeStamps-XX:+PrintGCApplicationStoppedTime,按照參數(shù)的順序分別輸出GC的簡(jiǎn)要信息,GC的詳細(xì)信息,GC的時(shí)間信息及GC造成的應(yīng)用暫停的時(shí)間到文件:在上面的啟動(dòng)參數(shù)中加入:-Xloggc:gc.log可指定gc信息輸出到gc.log可用于GC分析的參數(shù)還有:-verbose:gc,-XX:+PrintTenuringDistribution等。GCPortalGCPortal提供了對(duì)GC日志的圖標(biāo)分析,需要運(yùn)行在tomcat上,還有數(shù)據(jù)庫(kù)的支持,配置有點(diǎn)麻煩。GCPortal對(duì)于JDK6的日志,只能分析6M以下的。GCPortal提供了吞吐量分析,耗費(fèi)CPU時(shí)間,造成的應(yīng)用暫停時(shí)間,每秒從NG到OG的數(shù)量,minorGC的狀況及FullGC的狀況等Jconsole是JDK5以上版本自帶工具,圖形化,直接運(yùn)行Jconsole.exe或Jconsole.sh,查到JAVA進(jìn)程的pid,就可查看相應(yīng)進(jìn)程的狀況。Jconsole顯示JVM的很多信息:內(nèi)存,線程,類(lèi)和Mbean等。JVisualVM是JDK6update7之后推出的,類(lèi)似Jprofiler的工具JMapJDK自帶的用于分析JVM內(nèi)存狀況的工具查

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論