plsql 无法解析指定的连接标识符_Java方法加载、解析、存储、调用
?? ?方法調用在項目中是數不勝數,除了一些常量類其他的類都會定義方法并調用,那你有想過他是怎么從一個java語言寫的方法到計算機執行的嗎,下面我們就來學習Class字節碼文件中保存java中的方法、方法加載解析、方法信息存儲、方法的調用、方法執行過程……
一、java方法在class字節碼文件中的存儲
1、從.java文件到.class 文件是javac編譯器的輸入和輸出,也就是將高級java語言編譯為JVM字節碼文件。
2、編譯后.class文件中是怎么存儲方法的?
????我們先看看.class 文件的結構,.class 文件結構主要有兩種類型的變量即無符號數和表,無符號數有u1、u2、u4、u8 分別代表1字節變量 2字節變量 4字節變量和8字節變量而表是一種數據結構,用來描述具體的屬性,表有點類似于c語言結構體或java類,表可以由另一個表和無符號數組成……
3、.class字節碼文件其實就是一個大表表結構如下:
| 類型 | 名稱 | 數量 |
| u4 | magic | 1 |
| u2 | minor_version | 1 |
| u2 | major_version | 1 |
| u2 | constant_pool_count | 1 |
| cp_info | constant_pool | constant_pool_count - 1 |
| u2 | access_flags | 1 |
| u2 | this_class | 1 |
| u2 | super_class | 1 |
| u2 | interfaces_count | 1 |
| u2 | interfaces | interfaces_count |
| u2 | fields_count | 1 |
| field_info | fields | fields_count |
| u2 | methods_count | 1 |
| method_info | methods | methods_count |
| u2 | attribute_count | 1 |
| attribute_info | attributes | attributes_count |
????我們編譯的.java文件被編譯成.class 文件后按照這個格式保存,變量大小和順序都是嚴格按照這個順序的……或者說所有的.java文件被編譯后生成的.class文件都是這樣的,唯一不同的可能就是文件中常量池表個數、字段表、方法表、屬性表的個數不同而已,其他的如順序、類型都是一樣的……
4、換個角度說來就是一個.java 文件編譯之后生成的.class 文件我們可以用一個C語言結構體、java類來描述而這個結構體或類的屬性是固定的。
5、.class文件的結構體是Java虛擬機的規范。
6、回到正題我們來看看java方法在.class 字節碼文件中的存儲
(1)、方法的描述有兩個變量,u2無符號數來描述該類中有多少個方法,而methods變量中的method_info 表就是用來描述一個具體方法的結構體(c)、類(java)。
(2)、method_info 表的構成? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? method_info這個表結構也是java虛擬機規范,所有的java方法被編譯后都要按照這個表結構存儲。
? ? method_info 表結構中包含4個u2類型的變量分別表示訪問標識符、方法名在常量池中的索引位置,方法描述符在常量池中的索引位置,attributes 數組中attribute_info 表結構個數。
????通過method_info 結構體我們能找到一個方法的名稱、方法描述符(方法參數信息、方法返回值信息)、方法訪問標識符(private、public、static、synchronized 等……供應access_flag 一個標記),這些相當于是描述一個方法類似于java類中的Class對象一樣,我們找到了方法,但還沒看到方法中的執行邏輯即代碼部分,接著接續找……
(3)、attribute_info 表結構
????
? ? attribute_info 表中多個屬性是用屬性名稱來區分的,比如Code屬性表、Exceptions屬性表、AnnotationDefault屬性表、MethodParameters屬性表……
??? attribute_info 表結構其中包含了很多屬性這里就不展開來說了,感興趣的可以看看《java虛擬機規范》這本書,這里只說code屬性表。
????說一句attribute_info 屬性表的定義沒有之前的幾種表嚴格,如果attribute_info屬性表中的屬性java虛擬機不認識則忽略。
? ? attribute_info 屬性表結構中找不到我們想要的答案,繼續。
(4)、Code屬性表
????
(5)、Code屬性是變長的屬性,位于method_info 結構的屬性表中。Code 屬性中包含某個方法、實例初始化方法、類或接口初始化方法的java虛擬機指令以及相關輔助信息。
(6)、如果方法申明為native或者abstract方法,那么method_info 結構表的屬性絕不能有Code屬性。在其他情況下method_info 必須有且只能有一個Code屬性。
(7)、Code 屬性表中的u4類型的code_length 代表字節碼長度,u1類型的code是用于存儲字節碼指令的一些列字節流。既然叫做字節碼指令那么每個指令就是一個u1類型的單字節,當虛擬機讀取到code中的一個字節碼時,就可以對應找出這個字節碼代表的是什么指令并且可以知道這條指令后面是否需要跟隨參數,以及參數應當如何理解。這也就是我們要找的java方法的具體邏輯執行部分。
(8)、通過一路梳理發現之前的屬性都是在描述方法,如方法名稱、方法描述符、方法修飾符,而只有code屬性中記錄了方法具體執行時需要的數據比如字節碼指令、異常表個數、異常表、異常表的生命周期、棧幀中的局部變量表大小、棧幀中的操作數棧大小等運行期信息。
(9)、如果為java程序中的信息分為代碼(Code,方法體里面的java代碼)和元數據(MetaData,包括類、字段、方法定義以及其他信息)兩部分那么在整個Class文件中,Code屬性用于描述代碼,所有的其他數據項目都用于描述元數據。
??? .java文件中的方法在.class文件中的存儲如下:
.java文件中的方法—>.class 文件中的method_info表—>attribute_info屬性表—>Code屬性表—>code字節碼指令。
??? OK通過上面一個個表結構我們看到了一個.java文件到.class 文件中的表示方式和存儲格式,同樣也看到了.java文件中的方法在.class 文件中的標識方式和存儲。
??? .java 文件是高級語言java的文件是我們能看懂,但是計算機看不懂、JVM也看不懂,為了能讓JVM看懂,我們通過javac編譯器將.java文件按照JVM規范編譯為JVM能認識的字節碼文件,那下一步就是JVM加載.class 字節碼文件并按照JVM規范來解析.class 字節碼文件。
二、方法信息加載解析
????通過javac編譯器編譯后生成的.class 文件是JVM運行時數據的來源,下面來看看.class 文件被加載解析并獲得java方法信息的流程。
????一個類在JVM中的生命周期是:類加載、連接(校驗、準備、解析)、初始化、運行、停止、卸載。
1、我們知道一個java文件被編譯后會生成一個.class 文件,這里先不考慮內部類等。
2、在什么時候會JVM會加載.class 文件?
????之前的《new一個對象》那篇文章中有提到過,這里我們在來看看,JVM執行到getstatic、putstatic、new、invokestatic、java.lang.reflect包反射調用這些指令時需要初始化類,而初始化是在類已加載的基礎上,所以只要JVM執行到這些指令則如果所屬類還沒加載則一定會進行加載操作。
3、類的.class文件加載是通過類加載器加載的,類加載器有
(1)、bootstrap class loader類加載器:它用來加載 Java 的核心類,是用原生代碼來實現的,并不繼承自 java.lang.ClassLoader(負責加載$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++實現,不是ClassLoader子類)。
(2)、extensions class loader類加載器:它負責加載JRE的擴展目錄,lib/ext或者由java.ext.dirs系統屬性指定的目錄中的JAR包的類。由Java語言實現,父類加載器為null。
(3)、system class loader類加載器:?被稱為系統(也稱為應用)類加載器,它負責在JVM啟動時加載來自Java命令的-classpath選項、java.class.path系統屬性,或者CLASSPATH換為變量所指定的JAR包和類路徑。
(4)、用戶自定義類加載器(自定義類加載器是想使用非雙親委派模式來加載一個之前已加載過的類)
4、類加載器的核心方法
?(1)、findClass(String name) 查找指定的全類名類并加載該類的.class字節碼文件,加載到JVM內存中是byte[]。通過這一步將類加載到了JVM內存中。
?( 2)、defineClass0、defineClass1、defineClass2三個native方法,將加載的.class字節碼流進行驗證、解析操作,即生成該字節碼文件對應的instanceKlass對象、java.lang.Class 對象,其實這個過程中是在classFileParse::parseClassFile()方法中進行的。
5、classFileParse::parseClassFile()解析加載的.class文件
(1)、.class 字節碼文件是二進制流的文件,被加載后會嚴格按照.class字節碼文件格式解析即按照.class字節碼表來順序的解析。
(2)、.class 字節碼文件是二進制的那怎么來獲取表中的屬性,因為每個屬性都有固定大小或能計算出大小所以按照規定的大小來做相應的偏移量就OK了。
(3)、方法中調用parse_method()方法對.class字節碼文件中的methods變量按照表格式進行解析,我們知道methods變量中存儲的都是method_info 表,其整個組成關系如下:??
(4)、ClassFileStream * cfs = Stream(); 獲取的字節碼文件。
(5)、u2 ?flags = cfs->get_u2_fast();獲取方法method_info 表中的u2類型的access_flag屬性。
(6)、u2 ?name_index = cfs->get_u2_fast();獲取方法名在常量池中的索引。
(7)、方法屬性解析、code屬性解析、code屬性中的行號、方法局部變量表、異常屬性等。以此類推按照method_info 表結構解析完方法。? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
(8)、methodOop ?m_oop=oopFactory::new_method() 方法來生成JVM內部方法對象。
(9)、字節碼復制、將從.class 字節碼文件中解析出來的java方法代碼復制到method_oop 對象中。
? 具體的方法解析完成 哦也。
三、方法信息存儲
????上面方法信息解析完之后創建了一個methodOop對象,那methodOop對象是干什么用的?
?? ?methodOop包含java方法的一切信息,列如方法名、方法返回值類型、入參、字節碼指令、棧深度、局部變量表、字節碼對應的java行號等信息,總之HotSpot 通過methodOop將java class字節碼文件中的方法信息存儲到了內存中,并且這篇內存區域是結構化的,使得可以在JVM運行期方便的訪問java方法的各種屬性信息。
1、這么看來方法信息的存儲有了地方了即.class 字節碼文件中的Methods變量被JVM加載解析后都會生成methodOop對象保存在方法區(元數據區)。
2、說方法信息保存到methodOop對象不是錯誤的但也不準確,因為我們再看.class 字節碼文件中的method_info表的時候發現,其實方法由兩部分組成即代碼和元數據,同樣在JVM內部也是分開存放的,methodOop對象中存儲了方法元數據而代碼其實是存儲在constMethodOop 對象中。
3、methodOop 主要存儲java方法的名稱、簽名、訪問標識、解釋入口等信息,而constMethodOop則用于存儲方法的字節碼指令、行號表、異常表等信息。
4、那constMethodOop 對象是在哪里創建的,其實在創建methodOop 對象后里立馬就創建了constMethodOop對象;methodOop = oopFactory::new_method(); constMethodOop cm = new_constMethod() ;
5、methodOop對象內部布局:
這里我們看到了一個屬性屬性字段constMethod,他引用就是constMethodOop對象的地址。
6、constMethodOop 對象內存布局:
????同樣這里我們也看到了很多熟悉的屬性名稱比如bytecodes、linenumbertable、localvariabletable、 這些都是.class 字節碼文件中的code_attribute 屬性表中的屬性和包含的屬性表。
7、那方法代碼即編譯后的字節碼具體存儲在哪里?
????其實一個方法的字節碼指令就保存在constMethodOop 對象的末尾,在上面的圖中我們也看到了bytecode屬性,其中就保存了引用constMethodOop對象所屬方法的字節碼指令。
??? OK 到這里我們知道了.class字節碼文件中的methods變量中的每個method_info表被JVM加載后解析生成methodOop對象、constMethodOop 對象保存在方法區(元數據區即metaData)。
8、那問題來了類的方法信息即methodOop對象和類是怎么關聯的?
(1)、在調用系統加載器System Class Loader 對應用程序的java類進行加載的過程中,完成方法符號連接、驗證,最重要的是完成vtable與itable的構建,從而支持在JVM運行的方法動態綁定。
(2)、java類在JVM內部對應的對象是instanceKlassOop(JDK8中是instanceKlass)。在JVM加載java類的過程中,JVM會動態解析java類的方法及其父類方法的重寫,進而構建出一個vtable并將vtable分配到instanceKlassOop內存區域的末尾,從而支持運行期的方法動態綁定。
(3)、方法表一般在類加載的連接階段進行初始化,準備了類的變量初始值后,虛擬機會把該類的方法表也初始化完畢。
(4)、其實在方法解析后還有一步那就是將methodOop對象地址保存到vtable 中。
(5)、方法信息保存在methodOop對象中而methodOop對象地址保存在vtable 中,而vtable保存在instanceKlassOop對象的內存區域末尾,這樣類、方法表、方法對象關聯起來了。
(6)、類、方法關聯關系
instanceKlassOop-->vtable-->methodOop-->constMethodOop-->bytecode?
??? OK 在類方法解析存儲中我們看到有一個關聯點就是vtable 即虛方法表,在學習方法調用前我們先來看看vtable 具體是什么?
四、vtable (virtual table) 虛方法表
1、vtable的構建
? java類在JVM內部對應的對象是instanceKlassOop(JDK8中是instanceKlass)。在JVM加載java類的過程中,JVM會動態解析java類的方法及其父類方法的重寫,進而構建出一個vtable并將vtable分配到instanceKlassOop內存區域的末尾,從而支持運行期的方法動態綁定。
2、vtable 大小計算
? JVM在第一次加載java類時會調用classFileParse.cpp::parseClassFile()函數對java class字節碼進行解析,在parseClassFile()函數中會調用parse_method()函數解析java類中的方法,parse_method()函數執行完之后,會繼續調用klassVtable::compute_vtable_size_and_num_mirandas()函數,計算當前java類的vtable大小。
3、vtable 大小計算步驟
(1)、獲取父類vtable的大小,并將當前類的vtable的大小設置為父類的vtable的大小。
(2)、循環遍歷當前java類的每一個方法,調用needs_new_vtable_entry()函數進行判斷,如果判斷結果是true則vtable 大小加1。
4、什么情況下needs_new_vtable_entry()函數返回true
? ? 當這個方法滿足重寫方法條件下才會返回true,那什么樣的條件滿足重寫?
(1)、java方法是非static、非private、非final、類是非final的。
(2)、包含和父類方法名、方法描述符一樣的方法。
????如果滿足上面2個條件則needs_new_vtable_entry()函數返回true,也就是虛擬方法表大小加1。
5、JVM內部vtable的實現機制
?(1)、每一個java類在JVM內部都有一個對應的instanceKlassOop,vtable就被分配在這個oop內存區域的后面。
?(2)、vtable表中的每一個位置存放一個指針,指向java方法在內存中所對應的methodOop的內存首地址。
?(3)、如果一個java類繼承了父類,則該java類就會直接繼承父類的vtable。
?(4)、若該java類中聲明了一個非private、非final、非static的方法,若該方法是對父類方法的重寫,則JVM會更新父類vtable表中指向父類被重寫的方法的指針,使其指向子類中該方法的內存地址。若該方法并不是對父類方法的重寫,則JVM會向該java類的vtable中插入一個新的指針元素,使其指向該方法的內存位置。
6、vtable 特點總結
(1)、vtable 分配在instanceKlassOop對象實例的內存末尾。
(2)、所謂vtable,可以看作是一個數組,數組中的每一項成員元素都是一個指針,指針指向java方法在JVM內部所對應的method實例對象的內存首地址。
(3)、vtable 是java實現面向對象的多態性的機制,如果一個java方法可以被繼承和重寫,則最終通過invokevirtual 字節碼指令完成java方法動態綁定和分發。事實上很多面向對象的語言都是基于vtable 機制去實現多態,例如c++。
(4)、java子類會繼承父類的vtable 。
(5)、java中所有類都繼承自java.lang.Object,java.lang.Object中有5個虛方法:void finalize()、boolean equals(Object)、String toString()、int hashCode()、Object clone() 因此,如果一個java類中不聲明任何方法則其vtable 的長度默認為5。
(6)、java類中不是每一個java方法的內存地址都會保存到vtable 表中。只有當java子類中聲明的java方法是public、protected的、且沒有final、static 修飾,并且java子類中的方法并非對父類方法的重寫時,JVM才會在vtable 表中為該方法增加一個引用。
(7)、如果java子類某個方法重寫了父類方法,則子類的vtable 中原本對父類方法的指針引用會被替換為對子類的方法引用。
OK 我們知道了vtable 是什么、什么時候創建、里面保存什么 那下面開始方法調用的學習……
五、方法調用
?? ?方法調用主要的任務就是確定調用哪個方法,定位到唯一的方法,如果沒有繼承、沒有多態那定位一個方法可能比較容易,但是java是面向對象語言,有繼承、多態的特性所以很多時候在編譯階段是無法確定調用的方法是子類的方法還是父類的方法,只有真正到了運行的時候才能確定……
? ?按照方法特性、方法唯一性的確定時間,方法可以分為虛方法(包括接口方法)、和非虛方法,當然就有了兩種調用方式即解析調用、分派調用。
? ? 什么是解析調用、什么樣的方法是被解析調用的、什么是虛方法、什么是非虛方法、什么是分派調用 …… 帶著這些疑問開始。
1、解析
?? ?方法在程序正真運行前就有一個可確定的調用版本,并且這個方法的調用版本在運行期是不可改變的。換句話說調用目標在程序代碼寫好,編譯器進行編譯時就確定了。這類方法的調用稱為解析。
2、什么樣的方法在編譯器就能確定他的唯一性
? ? 靜態方法、私有方法、final 方法、父類方法,靜態方法是類型直接關聯的不會有重載、重寫多個版本,私有方法是外部不可訪問,final修飾的方法是不能繼承所以他們在整個程序運行期都會只有一個版本。
3、符號引用到直接引用
?? ? .class字節碼文件中的引用都是符號引用,比如調用某個方法則使用符號引用引用到該方法就完事了,但是當類加載器加載到JVM內存之后進行解析操作時如果這個方法是非虛方法即方法在運行期只有一個版本則會將方法的符號引用解析為直接引用即這個方法在內存中的入口地址的引用。
4、虛方法、非虛方法
? ? 之前我們在學習vtable時其實已經知道只有滿足public、protected、非private、非static、非final 、非final類、并且沒有重寫父類方法的話則都會在instanceKlassOop對象的內存區域默認的vtable 大小加1 ,也就是說滿足這些條件的方法是虛方法、不滿足這些條件的就是非虛方法。
5、虛方法表和分派調用(動態綁定)
? ? 虛方法表是一種技術方案,這種技術方案解決的就是動態分派、運行時動態綁定的問題。
6、解析調用、分派調用
?? ?解析調用一定是靜態的過程,在編譯期間就能確定,在類裝載的解析階段就會把涉及的符號引用全部轉化為可確定的直接引用,不會延遲到運行期再去完成。而分派調用則可能是靜態的,也可能是動態的。
7、靜態分派、動態分派
(1)、靜態分派:所有依賴靜態類型來定位方法執行版本的分派動作稱為靜態分派。靜態分派的典型應用是方法重載。靜態分派發生在編譯階段,因此確定靜態分派的動作實際上不是由虛擬機來執行的。
(2)、所謂的靜態分派就是在編譯期確定方法的符號引用。這個可以和方法重載的場景聯系在一起想想。
(3)、動態分派:動態分派是在JVM執行方法時按照運行類型來動態查找確定具體的調用方法。
(4)、動態分派時其實已經知道了要調用的方法名和方法描述(方法參數、方法返回值類型)但是不確定調用屬于哪個類的方法,比如子類繼承父類、重寫父類方法和不重寫父類方法兩種情況下子類調用的方法是不同的。
(5)、我們來看看父類、子類、重寫、不重寫場景下的符號引用、具體調用方法
?重寫父類方法:
? ? 父類
/***?@author?BitterCaffe*?@date?2020/8/30*?@description:?TODO*/public?class?Father?{????public?void?say()?{????????System.out.println("father?say");????}}子類重寫父類方法
/***?@author?BitterCaffe*?@date?2020/8/30*?@description:?TODO*/public?class?Son?extends?Father?{????@Override????public?void?say()?{????????System.out.println("son?say");????}}測試代碼
/***?@author?BitterCaffe*?@date?2020/8/30*?@description:?TODO*/public?class?TestDynamicDispatcher?{????public?static?void?main(String[]?args)?{????????//多態,????????Father?father?=?new?Son();????????//符號引用的確定是按照靜態類型來的,所以這里符號引用是父類的類名和方法????????//因為方法滿足public、非static、非private、非final、非final類所以是虛方法,動態分派????????//具體調用的方法是按照運行時類型確定????????//因為這里繼承了父類并重寫了父類方法所以vtable?中存儲的是子類的methodOop對象,所以執行子類的方法????????father.say();????}}字節碼反編譯驗證
不重寫父類方法:
子類只繼承不重寫父類方法
/***?@author?BitterCaffe*?@date?2020/8/30*?@description:?TODO*/public?class?Son?extends?Father?{}測試代碼
/***?@author?BitterCaffe*?@date?2020/8/30*?@description:?TODO*/public?class?TestDynamicDispatcher?{????public?static?void?main(String[]?args)?{????????//多態????????Father?father?=?new?Son();????????//符號引用的確定是按照靜態類型來的,所以這里符號引用是父類的類名和方法????????//因為方法滿足public、非static、非private、非final、非final類所以是虛方法,動態分派????????//具體調用的方法是按照運行時類型確定,具體運行的對象是子類????????//繼承了父類但沒有重寫父類方法所以vtable?中存儲的是父類的methodOop對象,所以具體運行的是子類但執行的是父類的方法????????father.say();????}}字節碼反編譯驗證
反編譯后的字節碼都是一致的,因為他們的靜態類型都是Father類所以應該是一致的……
8、其實到這里方法調用方式也完了,但是可能還是有疑問對于上面的繼承父類重寫父類方法、繼承父類不重寫方法他的符號引用都是父類的方法為何在重寫父類方法后調用的是子類方法、不重寫調用的是父類的方法,這個還要從invokevirtual 指令的多態查找過程開始說起,invokevirtual 指令的運行是解析過程大致分為如下步驟:
(1)、找到操作數棧頂的第一個元素所指向的對象的實際類型,記作C;
(2)、如果在類型C中找到與常量池中的描述符和簡單名稱都相符的方法,則進行訪問權限校驗,如果通過則返回這個方法的直接引用,查找過程結束;如果不通過,則返回java.lang.IllegalAccessError 異常。
(3)、否則,按照繼承關系從下往上一次對C的各個父類進行第2步的搜索和校驗過程。
(4)、如果始終沒找到合適的方法,則拋出java.lang.AbstractMethodError 異常。
9、按照上面4步,在重寫父類方法后,第1步、第2步就確定了方法是子類的方法所以調用的就是子類方法,如果沒有重寫父類方法則到第3步發現方法是父類的則引用的方法就是父類方法。
10、由于invokevirtual 指令執行的第一步就是在運行期確定接收者的實際類型,所以兩次調用中的invokevirtual 指令把常量池中的類方法符號引用解析到了不同的直接引用上,這個過程就是java語言中方法重寫的本質。
11、上面5中說明了虛擬方法表和動態分派的關系,其實理解了虛擬方法表,個人感覺對方法方法重寫有更清晰、簡單的認識
(1)、invokevirtual 指令執行的第一步就是在運行期確定接收者的實際類型,實際類型確定了就確定了instanceOop。
(2)、instanceOop 確定了按照對象頭的meta_data 就能確定instanceKlassOop對象。
(3)、確定了instanceKlassOop 對象就能找到vtable 。
(4)、按照classFilePare::parseClassFile()方法中方法解析完之后會給該類的instanceKlassOop 對象的vtable 中記錄虛擬方法對象的地址即methodOop對象的地址。
(5)、按照vtable 中methodOop對象指針添加規則如果子類重寫父類方法則會把對父類methodOop對象的指針引用改為子類自己的methodOop對象指針。
(6)、這樣我們就能從vtable 中找到具體methodOop對象,這樣我們就找到了具體執行那個類的方法。
(7)、具體代碼即字節碼指令存儲在methodOop對象的constMethodOop引用的類中即ConstMethodOop對象的bytecode 字段中。
? ? 這樣一路下來對象、類、方法、字節指令都找確定了……
六、總結
????類加載-->解析-->方法解析--> 生成methodOop對象--> methodOop對象地址保存到vtable(虛擬方法表)--> 如果有父類則把父類的methodOop對象地址也保存到本類的vtable中--> 這樣解析完之后一個類的instanckKlassOop對象內存模型最底部就是該類的vtable即這個類的虛擬方法表,其中保存了該類以及該類的父類中的method0op對象指針即methodOop * methodoop 也就是每個方法的入口-->如果本類重寫了父類方法則本類instanceKlassOop對象的vtable中會把父類的methodOop對象指針地址替換為子類自己的方法指針--> 這樣類加載后的解析就結束了-->運行程序-->調用方法-->當著對象調用方法時會去這個對象的元數據指針所指instanceKlassOop下找方法,找到了則調用,這時有兩種情況,一種是子類重寫了父類方法則調用的就是子類自己的方法,一種是子類沒有重寫父類方法則這里存儲的是父類的方法地址則調用的就是父類的方法。
????本來想著方法調用和方法執行一起來寫,但是方法執行涉及到棧內存、棧幀、局部變量表、形參、方法返回地址、操作數棧、局部變量表操作數棧重疊、字節碼執行器、寄存器這些知識點,但到月底了還是沒明白字節碼執行器和寄存器,cpu、cpu寄存器他們之間的關系所以沒法寫方法的執行了……
8月唯一一篇文章終于寫完了…… 2020.08.30 22:00
參考書籍:
《深入理解java虛擬機》第二版 周志明 著
《揭秘java虛擬機JVM設計原理與實現》 封亞飛 著
總結
以上是生活随笔為你收集整理的plsql 无法解析指定的连接标识符_Java方法加载、解析、存储、调用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 融合牙是什么意思
- 下一篇: 访问 asp网页 白屏_(02)ASP如