指令级别解释对象创建过程和DCL为什么要volatile
先上圖
先明白幾點
1.jvm stack指的是線程棧,一個線程有一個jvm stack。
2.線程里的一個方法對應(yīng)一個棧幀,一個棧幀有Local Variables(本地變量表,記錄了方法的參數(shù)、局部變量)、Operand Stacks(方法運算的變量引用,常量值需要壓棧和彈棧)、Dynamic Linking(方法運算需要引用方法區(qū)的引用)、Return Address(記錄了方法需要返回的調(diào)用者的地址)。
3.所有方法棧里的運算都要壓棧和彈棧,就是Operand Stacks要頻繁的發(fā)生 astore_n 和 aload_n。
對象的創(chuàng)建過程
1.main方法被壓入線程棧底部;
2.Hello_02 h = new Hello_02()這一句的字節(jié)碼:
0 new --> 在heap里開辟一個內(nèi)存,放Hello_02對象,這個對象賦默認值(int類型是0,引用類型是null)把這個對象的地址壓棧到main方法的Operand Stacks里;
3 dup --> 復制main方法棧里的Operand Stacks的Hello_02對象的地址,所以此時Operand Stacks里有相同的兩個指向Hello_02對象的地址。
4 invokespecial --> 把復制Hello_02的地址從Operand Stacks彈出來頂上的那個,運行init構(gòu)造方法,給局部變量賦初始值。對象創(chuàng)建完成,Operand Stacks里剩下Hello_02的地址就指向了創(chuàng)建完成的Hello_02。
7 astore_1 --> 把Operand Stacks 里的指向Heap里的地址彈棧,賦值給Local Variables 里的h
,到這一步Hello_02 h = new Hello_02()這一步就完成了。
為什么要DCL要加volatile呢?
對象創(chuàng)建的過程中,cpu會亂序執(zhí)行,如果一個線程運行到 3 dup的時候進來另一個線程要用h,而此時0 new已經(jīng)執(zhí)行完了,所以實例已經(jīng)不是null了,但是還沒有4 invokespecial的init,所以heap里的對象局部變量都是默認值,還不是初始值,所以就不是程序要想要的初始化好的對象,所以要加volitale鎖定內(nèi)存,保證指令不亂序執(zhí)行(邏輯上),讓程序得到正確的初始化好的對象。
進入h.m1()
8 aload_1 -->對應(yīng)了h.m1()方法,把main棧里的Local Variables的h壓到Operand Stacks里。
9 invokevirtual --> 運行m1方法,這時候main棧停止,切換到m1方法棧。
0 sipush -->先解釋sipush,s表示是short類型,i表示int類型(jvm達不到int類型的的數(shù)值運算會映射成int類型) s標識short,數(shù)值>127(byte類型的范圍)就映射成short類型。push就是壓棧。所以這條指令就是把 200 這個值壓入 Operand Stacks。
3 istore_1 --> Operand Stacks里的200 彈棧,給到 Local Variables 里的i。
4 return --> 返回到main方法里的 invokevirtual。
** 返回main **
12 return --> 返回給線程的調(diào)用者。
** 同理 **
m1是有返回值的,那么m1棧執(zhí)行完之后,會把結(jié)果壓棧進入main棧的棧頂,此時沒有變量接收,就多了一步 12 pop,把m1的返回值彈出去,扔掉。
** 再者 **
m1方法的返回值有i變量接收,就直接把m1方法的返回值給了main方法的Local Variables里的2號位置,即變量i,就是 istore_2這一步。、
** 遞歸 **
線程把m這個方法,根據(jù)傳參依次壓棧,執(zhí)行完一個彈棧一個。所以如果遞歸有死循環(huán)或者遞歸跳出條件設(shè)置不合理,會棧堆滿了Overfolw異常。
總結(jié)
以上是生活随笔為你收集整理的指令级别解释对象创建过程和DCL为什么要volatile的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 指令级别理解i++和++i
- 下一篇: TCP的拥塞