- Java虛擬機(jī)的指令由一個(gè)字節(jié)長(zhǎng)度的努酸、代表著某種特定操作含義的數(shù)字(稱為操作碼,Opcode)以及跟隨其后的零至多個(gè)代表此操作所需參數(shù)(稱為操作數(shù)杜恰,Operands)而構(gòu)成获诈。由于Java虛擬機(jī)采用面向操作數(shù)棧而不是寄存器的架構(gòu),所以大多數(shù)的指令都不包含操作數(shù)心褐,只有一個(gè)操作碼烙荷。
- 由于限制了Java虛擬機(jī)操作碼的長(zhǎng)度為一個(gè)字節(jié)(即0~255),這意味著指令集的操作碼總數(shù)不可能超過(guò)256條檬寂;又由于Class文件格式放棄了編譯后代碼的操作數(shù)長(zhǎng)度對(duì)齊终抽,這就意味著虛擬機(jī)處理那些超過(guò)一個(gè)字節(jié)數(shù)據(jù)的時(shí)候,不得不在運(yùn)行時(shí)從字節(jié)中重建出具體數(shù)據(jù)的結(jié)構(gòu)桶至,如果要將一個(gè)16位長(zhǎng)度的無(wú)符號(hào)整數(shù)使用兩個(gè)無(wú)符號(hào)字節(jié)存儲(chǔ)起來(lái)(將它們命名為byte1和byte2)昼伴,那它們的值應(yīng)該是這樣的:
(byte1<<8)|byte2
放棄了操作數(shù)長(zhǎng)度對(duì)齊,可以省略很多填充和間隔符號(hào)镣屹;用一個(gè)字節(jié)來(lái)代表操作碼圃郊,也是為了盡可能獲得短小精干的編譯代碼。
do{ 自動(dòng)計(jì)算PC寄存器的值加1;
根據(jù)PC寄存器的指示位置女蜈,從字節(jié)碼流中取出操作碼;
if(字節(jié)碼存在操作數(shù))從字節(jié)碼流中取出操作數(shù);
執(zhí)行操作碼所定義的操作;
}while(字節(jié)碼流長(zhǎng)度>0);
一持舆、字節(jié)碼與數(shù)據(jù)類型
在Java虛擬機(jī)的指令集中,大多數(shù)的指令都包含了其操作所對(duì)應(yīng)的數(shù)據(jù)類型信息.
對(duì)于大部分與數(shù)據(jù)類型相關(guān)的字節(jié)碼指令伪窖,它們的操作碼助記符中都有特殊的字符來(lái)表明專門(mén)為哪種數(shù)據(jù)類型服務(wù):i代表對(duì)int類型的數(shù)據(jù)操作逸寓,l代表long,s代表short,b代表byte,c代表char,f代表float,d代表double,a代表reference。也有一些指令的助記符中沒(méi)有明確地指明操作類型的字母覆山,如arraylength指令竹伸,它沒(méi)有代表數(shù)據(jù)類型的特殊字符,但操作數(shù)永遠(yuǎn)只能是一個(gè)數(shù)組類型的對(duì)象簇宽。還有另外一些指令勋篓,如無(wú)條件跳轉(zhuǎn)指令goto則是與數(shù)據(jù)類型無(wú)關(guān)的。
由于Java虛擬機(jī)的操作碼長(zhǎng)度只有一個(gè)字節(jié)魏割,Java虛擬機(jī)的指令集對(duì)于特定的操作只提供了有限的類型相關(guān)指令去支持它譬嚣,指令集將會(huì)故意被設(shè)計(jì)成非完全獨(dú)立的(Java虛擬機(jī)規(guī)范中把這種特性稱為“Not Orthogonal”,即并非每種數(shù)據(jù)類型和每一種操作都有對(duì)應(yīng)的指令)钞它。有一些單獨(dú)的指令可以在必要的時(shí)候用來(lái)將一些不支持的類型轉(zhuǎn)換為可被支持的類型拜银。
表6-31列舉了Java虛擬機(jī)所支持的與數(shù)據(jù)類型相關(guān)的字節(jié)碼指令殊鞭,通過(guò)使用數(shù)據(jù)類型列所代表的特殊字符替換opcode列的指令模板中的T,就可以得到一個(gè)具體的字節(jié)碼指令盐股。如果在表中指令模板與數(shù)據(jù)類型兩列共同確定的格為空,則說(shuō)明虛擬機(jī)不支持對(duì)這種數(shù)據(jù)類型執(zhí)行這項(xiàng)操作耻卡。例如疯汁,load指令有操作int類型的iload,但是沒(méi)有操作byte類型的同類指令卵酪。
大部分的指令都沒(méi)有支持整數(shù)類型byte幌蚊、char和short,甚至沒(méi)有任何指令支持boolean類型溃卡。編譯器會(huì)在編譯期或運(yùn)行期將byte和short類型的數(shù)據(jù)帶符號(hào)擴(kuò)展(Sign-Extend)為相應(yīng)的int類型數(shù)據(jù)溢豆,將boolean和char類型數(shù)據(jù)零位擴(kuò)展(Zero-Extend)為相應(yīng)的int類型數(shù)據(jù)。與之類似瘸羡,在處理boolean漩仙、byte、short和char類型的數(shù)組時(shí)犹赖,也會(huì)轉(zhuǎn)換為使用對(duì)應(yīng)的int類型的字節(jié)碼指令來(lái)處理队他。因此,大多數(shù)對(duì)于boolean峻村、byte麸折、short和char類型數(shù)據(jù)的操作,實(shí)際上都是使用相應(yīng)的int類型作為運(yùn)算類型(Computational Type)粘昨。
二垢啼、指令簡(jiǎn)單分類
1.加載和存儲(chǔ)指令
用于將數(shù)據(jù)在棧幀中的局部變量表和操作數(shù)棧之間來(lái)回傳輸,這類指令包括如下內(nèi)容张肾。
將一個(gè)局部變量加載到操作棧:
iload芭析、iload_<n>、lload吞瞪、lload_<n>放刨、fload、fload_<n>尸饺、dload进统、dload_<n>、aload浪听、aload_<n>
將一個(gè)數(shù)值從操作數(shù)棧存儲(chǔ)到局部變量表:
istore螟碎、istore_<n>、lstore迹栓、lstore_<n>掉分、fstore、fstore_<n>、dstore酥郭、dstore_<n>华坦、astore、astore_<n>
將一個(gè)常量加載到操作數(shù)棧:
bipush不从、sipush惜姐、ldc、ldc_w椿息、ldc2_w歹袁、aconst_null、iconst_m1寝优、iconst_<i>条舔、lconst_<l>、fconst_<f>乏矾、dconst_<d>
擴(kuò)充局部變量表的訪問(wèn)索引的指令:
wide
存儲(chǔ)數(shù)據(jù)的操作數(shù)棧和局部變量表主要就是由加載和存儲(chǔ)指令進(jìn)行操作孟抗,除此之外,還有少量指令钻心,如訪問(wèn)對(duì)象的字段或數(shù)組元素的指令也會(huì)向操作數(shù)棧傳輸數(shù)據(jù)夸浅。
上面所列舉的指令助記符中,有一部分是以尖括號(hào)結(jié)尾的(例如iload_<n>)扔役,這些指令助記符實(shí)際上是代表了一組指令(例如iload_<n>帆喇,它代表了iload_0、iload_1亿胸、iload_2和iload_3這幾條指令)坯钦。這幾組指令都是某個(gè)帶有一個(gè)操作數(shù)的通用指令(例如iload)的特殊形式,對(duì)于這若干組特殊指令來(lái)說(shuō)侈玄,它們省略掉了顯式的操作數(shù)婉刀,不需要進(jìn)行取操作數(shù)的動(dòng)作,實(shí)際上操作數(shù)就隱含在指令中序仙。除了這點(diǎn)之外突颊,它們的語(yǔ)義與原生的通用指令完全一致(例如iload_0的語(yǔ)義與操作數(shù)為0時(shí)的iload指令語(yǔ)義完全一致)。
2.運(yùn)算指令
用于對(duì)兩個(gè)操作數(shù)棧上的值進(jìn)行某種特定運(yùn)算潘悼,并把結(jié)果重新存入到操作棧頂律秃。
可以分為兩種:整型+浮點(diǎn)型 數(shù)據(jù)進(jìn)行運(yùn)算的指令,無(wú)論是哪種算術(shù)指令治唤,都使用Java虛擬機(jī)的數(shù)據(jù)類型棒动,由于沒(méi)有直接支持byte、short宾添、char和boolean類型的算術(shù)指令船惨,對(duì)于這類數(shù)據(jù)的運(yùn)算柜裸,應(yīng)使用操作int類型的指令代替。整數(shù)與浮點(diǎn)數(shù)的算術(shù)指令在溢出和被零除的時(shí)候也有各自不同的行為表現(xiàn)粱锐,所有的算術(shù)指令如下疙挺。
加法指令:iadd、ladd怜浅、fadd铐然、dadd
減法指令:isub、lsub海雪、fsub锦爵、dsub
乘法指令:imul舱殿、lmul奥裸、fmul、dmul
除法指令:idiv沪袭、ldiv湾宙、fdiv、ddiv
求余指令:irem冈绊、lrem侠鳄、frem、drem
取反指令:ineg死宣、lneg伟恶、fneg、dneg
位移指令:ishl毅该、ishr博秫、iushr、lshl眶掌、lshr挡育、lushr
按位或指令:ior、lor
按位與指令:iand朴爬、land
按位異或指令:ixor即寒、lxor
局部變量自增指令:iinc
比較指令:dcmpg、dcmpl召噩、fcmpg母赵、fcmpl、lcmp
3.類型轉(zhuǎn)換指令
可以將兩種不同的數(shù)值類型進(jìn)行相互轉(zhuǎn)換
Java虛擬機(jī)直接支持(即轉(zhuǎn)換時(shí)無(wú)需顯式的轉(zhuǎn)換指令)以下數(shù)值類型的寬化類型轉(zhuǎn)換(Widening Numeric Conversions具滴,即小范圍類型向大范圍類型的安全轉(zhuǎn)換):
int類型到long市咽、float或者double類型
long類型到float、double類型
float類型到double類型
處理窄化類型轉(zhuǎn)換(Narrowing Numeric Conversions)時(shí)抵蚊,必須顯式地使用轉(zhuǎn)換指令來(lái)完成施绎,這些轉(zhuǎn)換指令包括:i2b溯革、i2c、i2s谷醉、l2i致稀、f2i、f2l俱尼、d2i抖单、d2l和d2f。窄化類型轉(zhuǎn)換可能會(huì)導(dǎo)致轉(zhuǎn)換結(jié)果產(chǎn)生不同的正負(fù)號(hào)遇八、不同的數(shù)量級(jí)的情況矛绘,轉(zhuǎn)換過(guò)程很可能會(huì)導(dǎo)致數(shù)值的精度丟失。
在將int或long類型窄化轉(zhuǎn)換為整數(shù)類型T的時(shí)候刃永,轉(zhuǎn)換過(guò)程僅僅是簡(jiǎn)單地丟棄除最低位N個(gè)字節(jié)以外的內(nèi)容货矮,N是類型T的數(shù)據(jù)類型長(zhǎng)度,這將可能導(dǎo)致轉(zhuǎn)換結(jié)果與輸入值有不同的正負(fù)號(hào)斯够。符號(hào)位處于數(shù)值的最高位囚玫,高位被丟棄之后,轉(zhuǎn)換結(jié)果的符號(hào)就取決于低N個(gè)字節(jié)的首位了读规。
在將一個(gè)浮點(diǎn)值窄化轉(zhuǎn)換為整數(shù)類型T(T限于int或long類型之一)的時(shí)候抓督,將遵循以下轉(zhuǎn)換規(guī)則:
如果浮點(diǎn)值是NaN,那轉(zhuǎn)換結(jié)果就是int或long類型的0束亏。
如果浮點(diǎn)值不是無(wú)窮大的話铃在,浮點(diǎn)值使用IEEE 754的向零舍入模式取整,獲得整數(shù)值v碍遍,如果v在目標(biāo)類型T(int或long)的表示范圍之內(nèi)定铜,那轉(zhuǎn)換結(jié)果就是v。否則雀久,將根據(jù)v的符號(hào)宿稀,轉(zhuǎn)換為T(mén)所能表示的最大或者最小正數(shù)。
從double類型到float類型的窄化轉(zhuǎn)換過(guò)程與IEEE 754中定義的一致赖捌,通過(guò)IEEE 754向最接近數(shù)舍入模式舍入得到一個(gè)可以使用float類型表示的數(shù)字祝沸。如果轉(zhuǎn)換結(jié)果的絕對(duì)值太小而無(wú)法使用float來(lái)表示的話,將返回float類型的正負(fù)零越庇。如果轉(zhuǎn)換結(jié)果的絕對(duì)值太大而無(wú)法使用float來(lái)表示的話罩锐,將返回float類型的正負(fù)無(wú)窮大,對(duì)于double類型的NaN值將按規(guī)定轉(zhuǎn)換為float類型的NaN值卤唉。
4.對(duì)象創(chuàng)建與訪問(wèn)指令
雖然類實(shí)例和數(shù)組都是對(duì)象涩惑,但Java虛擬機(jī)對(duì)類實(shí)例和數(shù)組的創(chuàng)建與操作使用了不同的字節(jié)碼指令。對(duì)象創(chuàng)建后桑驱,就可以通過(guò)對(duì)象訪問(wèn)指令獲取對(duì)象實(shí)例或者數(shù)組實(shí)例中的字段或者數(shù)組元素竭恬,這些指令如下跛蛋。
創(chuàng)建類實(shí)例的指令:new
創(chuàng)建數(shù)組的指令:newarray、anewarray痊硕、multianewarray
訪問(wèn)類字段和實(shí)例字段的指令:getfield赊级、putfield、getstatic岔绸、putstatic
把一個(gè)數(shù)組元素加載到操作數(shù)棧的指令:baload理逊、caload、saload盒揉、iaload晋被、laload、faload刚盈、daload羡洛、aaload
將一個(gè)操作數(shù)棧的值存儲(chǔ)到數(shù)組元素中的指令:bastore、castore扁掸、sastore翘县、iastore最域、fastore谴分、dastore、aastore
取數(shù)組長(zhǎng)度的指令:arraylength
檢查類實(shí)例類型的指令:instanceof镀脂、checkcast
5.操作數(shù)棧管理指令
將操作數(shù)棧的棧頂一個(gè)或兩個(gè)元素出棧:pop牺蹄、pop2
復(fù)制棧頂一個(gè)或兩個(gè)數(shù)值并將復(fù)制值或雙份的復(fù)制值重新壓入棧頂:dup、dup2薄翅、dup_x1沙兰、dup2_x1、dup_x2翘魄、dup2_x2
將棧最頂端的兩個(gè)數(shù)值互換:swap
6.控制轉(zhuǎn)移指令
可以讓Java虛擬機(jī)有條件或無(wú)條件地從指定的位置指令 下一條指令繼續(xù)執(zhí)行程序鼎天,在有條件或無(wú)條件地修改PC寄存器的值。
條件分支:ifeq暑竟、iflt斋射、ifle、ifne但荤、ifgt罗岖、ifge、ifnull腹躁、ifnonnull桑包、if_icmpeq、if_icmpne纺非、if_icmplt哑了、if_icmpgt赘方、if_icmple、if_icmpge弱左、if_acmpeq和if_acmpne
復(fù)合條件分支:tableswitch蒜焊、lookupswitch
無(wú)條件分支:goto、goto_w科贬、jsr泳梆、jsr_w、ret
在Java虛擬機(jī)中有專門(mén)的指令集用來(lái)處理int和reference類型的條件分支比較操作榜掌,為了可以無(wú)須明顯標(biāo)識(shí)一個(gè)實(shí)體值是否null优妙,也有專門(mén)的指令用來(lái)檢測(cè)null值。
對(duì)于boolean憎账、byte套硼、char和short的條件分支比較操作,都是使用int類型的比較指令來(lái)完成胞皱,而對(duì)于long邪意、float和double的條件分支比較操作,則會(huì)先執(zhí)行相應(yīng)類型的比較運(yùn)算指令(dcmpg反砌、dcmpl雾鬼、fcmpg、fcmpl宴树、lcmp策菜,見(jiàn)6.4.3節(jié)),運(yùn)算指令會(huì)返回一個(gè)整型值到操作數(shù)棧中酒贬,隨后再執(zhí)行int類型的條件分支比較操作來(lái)完成整個(gè)分支跳轉(zhuǎn)又憨。由于各種類型的比較最終都會(huì)轉(zhuǎn)化為int類型的比較操作,int類型比較是否方便完善就顯得尤為重要锭吨,所以Java虛擬機(jī)提供的int類型的條件分支指令是最為豐富和強(qiáng)大的蠢莺。
7.方法調(diào)用和返回指令
invokevirtual 調(diào)用對(duì)象的實(shí)例方法,根據(jù)對(duì)象的實(shí)際類型進(jìn)行分派(虛方法分派)
invokeinterface 調(diào)用接口方法零如,它會(huì)在運(yùn)行時(shí)搜索一個(gè)實(shí)現(xiàn)了這個(gè)接口方法的對(duì)象躏将,找出適合的方法進(jìn)行調(diào)用invokespecial 調(diào)用一些需要特殊處理的實(shí)例方法,包括實(shí)例初始化方法埠况、私有方法和父類方法
invokestatic調(diào)用類方法
invokedynamic在運(yùn)行時(shí)動(dòng)態(tài)解析出調(diào)用點(diǎn)限定符所引用的方法耸携,并執(zhí)行該方法,前面4條調(diào)用指令的分派邏輯都固化在Java虛擬機(jī)內(nèi)部辕翰,而invokedynamic指令的分派邏輯是由用戶所設(shè)定的引導(dǎo)方法決定的夺衍。
方法調(diào)用指令與數(shù)據(jù)類型無(wú)關(guān),而方法返回指令是根據(jù)返回值的類型區(qū)分的喜命,包括ireturn(當(dāng)返回值是boolean沟沙、byte河劝、char、short和int類型時(shí)使用)矛紫、lreturn赎瞎、freturn、dreturn和areturn颊咬,另外還有一條return指令供聲明為void的方法务甥、實(shí)例初始化方法以及類和接口的類初始化方法使用。
8.異常處理指令
在Java程序中顯式拋出異常的操作(throw語(yǔ)句)都由athrow指令來(lái)實(shí)現(xiàn),在其他Java虛擬機(jī)指令檢測(cè)到異常狀況時(shí)也自動(dòng)拋出喳篇。
而在Java虛擬機(jī)中敞临,處理異常(catch語(yǔ)句)不是由字節(jié)碼指令來(lái)實(shí)現(xiàn)的,而是采用異常表來(lái)完成的麸澜。
9.同步指令
Java虛擬機(jī)可以支持方法級(jí)的同步和方法內(nèi)部一段指令序列的同步挺尿,這兩種同步結(jié)構(gòu)都是使用管程(Monitor)來(lái)支持的。
方法級(jí)的同步是隱式的炊邦,即無(wú)須通過(guò)字節(jié)碼指令來(lái)控制编矾,它實(shí)現(xiàn)在方法調(diào)用和返回操作之中。虛擬機(jī)可以從方法常量池的方法表結(jié)構(gòu)中的ACC_SYNCHRONIZED訪問(wèn)標(biāo)志得知一個(gè)方法是否聲明為同步方法馁害。當(dāng)方法調(diào)用時(shí)窄俏,調(diào)用指令將會(huì)檢查方法的ACC_SYNCHRONIZED訪問(wèn)標(biāo)志是否被設(shè)置,如果設(shè)置了蜗细,執(zhí)行線程就要求先成功持有管程裆操,然后才能執(zhí)行方法怒详,最后當(dāng)方法完成(無(wú)論是正常完成還是非正常完成)時(shí)釋放管程炉媒。在方法執(zhí)行期間,執(zhí)行線程持有了管程昆烁,其他任何線程都無(wú)法再獲取到同一個(gè)管程吊骤。如果一個(gè)同步方法執(zhí)行期間拋出了異常,并且在方法內(nèi)部無(wú)法處理此異常静尼,那么這個(gè)同步方法所持有的管程將在異常拋到同步方法之外時(shí)自動(dòng)釋放白粉。
同步一段指令集序列通常是由Java語(yǔ)言中的synchronized語(yǔ)句塊來(lái)表示的,Java虛擬機(jī)的指令集中有monitorenter和monitorexit兩條指令來(lái)支持synchronized關(guān)鍵字的語(yǔ)義鼠渺,正確實(shí)現(xiàn)synchronized關(guān)鍵字需要Javac編譯器與Java虛擬機(jī)兩者共同協(xié)作支持鸭巴,
代碼:
void onlyMe(Foo f){
synchronized(f){
System.out.println();
}
}
javap反編譯:
void onlyMe(org.clazz.Foo);
flags:
Code:
stack=2, locals=4, args_size=2
0: aload_1 //將對(duì)象f入棧
1: dup //復(fù)制棧頂元素(即f的引用)
2: astore_2 //將棧頂元素存儲(chǔ)到局部變量表slot 2中
3: monitorenter //以棧頂元素(即f)作為鎖,開(kāi)始同步
4: getstatic #3 // Field java/lang/System.out:Ljav
a/io/PrintStream;
7: invokevirtual #4 // Method java/io/PrintStream.prin
tln:()V
10: aload_2 //將局部變量Slow 2的元素(即f)入棧
11: monitorexit //退出同步
12: goto 20 // 方法正常結(jié)束拦盹,跳轉(zhuǎn)到18返回
15: astore_3 //從這步開(kāi)始是異常路徑鹃祖,見(jiàn)下面異常表的target 15
16: aload_2 //將局部變量Slow 2的元素(即f)入棧
17: monitorexit //退出同步
18: aload_3 //將局部變量Slow 3的元素(即異常對(duì)象)入棧
19: athrow //把異常對(duì)象重新拋出給onlyMe()方法的調(diào)用者
20: return //方法正常返回
Exception table:
from to target type
4 12 15 any
15 18 15 any
LineNumberTable:
line 18: 0
line 19: 4
line 20: 10
line 21: 20
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 15
locals = [ class org/clazz/TestClass, class org/clazz/Foo, class java/
lang/Object ]
stack = [ class java/lang/Throwable ]
frame_type = 250 /* chop */
offset_delta = 4
}
編譯器必須確保無(wú)論方法通過(guò)何種方式完成,方法中調(diào)用過(guò)的每條monitorenter指令都必須執(zhí)行其對(duì)應(yīng)的monitorexit指令普舆,而無(wú)論這個(gè)方法是正常結(jié)束還是異常結(jié)束恬口。
從上述字節(jié)碼序列中可以看到校读,為了保證在方法異常完成時(shí)monitorenter和monitorexit指令依然可以正確配對(duì)執(zhí)行,編譯器會(huì)自動(dòng)產(chǎn)生一個(gè)異常處理器祖能,這個(gè)異常處理器聲明可處理所有的異常歉秫,它的目的就是用來(lái)執(zhí)行monitorexit指令。
三养铸、公有設(shè)計(jì)和私有實(shí)現(xiàn)
Java虛擬機(jī)規(guī)范描繪了Java虛擬機(jī)應(yīng)有的共同程序存儲(chǔ)格式:Class文件格式以及字節(jié)碼指令集雁芙。
Java虛擬機(jī)實(shí)現(xiàn)必須能夠讀取Class文件并精確實(shí)現(xiàn)包含在其中的Java虛擬機(jī)代碼的語(yǔ)義。拿著Java虛擬機(jī)規(guī)范一成不變地逐字實(shí)現(xiàn)其中要求的內(nèi)容當(dāng)然是一種可行的途徑钞螟,但一個(gè)優(yōu)秀的虛擬機(jī)實(shí)現(xiàn)却特,在滿足虛擬機(jī)規(guī)范的約束下對(duì)具體實(shí)現(xiàn)做出修改和優(yōu)化也是完全可行的,并且虛擬機(jī)規(guī)范中明確鼓勵(lì)實(shí)現(xiàn)者這樣做筛圆。只要優(yōu)化后Class文件依然可以被正確讀取裂明,并且包含在其中的語(yǔ)義能得到完整的保持,那實(shí)現(xiàn)者就可以選擇任何方式去實(shí)現(xiàn)這些語(yǔ)義太援,虛擬機(jī)后臺(tái)如何處理Class文件完全是實(shí)現(xiàn)者自己的事情闽晦,只要它在外部接口上看起來(lái)與規(guī)范描述的一致即可。
將輸入的Java虛擬機(jī)代碼在加載或執(zhí)行時(shí)翻譯成另外一種虛擬機(jī)的指令集提岔。
將輸入的Java虛擬機(jī)代碼在加載或執(zhí)行時(shí)翻譯成宿主機(jī)CPU的本地指令集(即JIT代碼生成技術(shù))仙蛉。