同理可得:
#2 代表一个PrintStream对象,即System.out
#3 代表一个字符串,即"Hello World"
#4 代表PrintStream类定义的带一个String参数的方法println
#5 代表HelloWorld类
#6 代表Object类
3.代码信息
{
public com.qfc.test.thread.HelloWorld();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object.“”😦)V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/qfc/test/thread/HelloWorld;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello World
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 5: 0
line 6: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;
}
SourceFile: “HelloWorld.java”
代码中主要包含字段,构造器(代码块),方法等信息
我们以main方法为例
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V 描述方法是带一个String数组参数的方法,[代表数组
flags: ACC_PUBLIC, ACC_STATIC 方法是public,static的
Code:
stack=2, locals=1, args_size=1 操作数栈深度2,局部变量表槽个数1,形参个数1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello World
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 5: 0
line 6: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;
关于操作数栈,和局部变量表
=============
当Java启动一个线程后,JVM就会给该线程创建:
1、虚拟机栈:保存非native方法的调用信息
2、程序计数器:用来保存下次JVM“执行引擎”需要的指令信息
3、本地方法栈:保存native方法的调用信息
当线程运行中,每调用一次非native方法就会在“虚拟机栈”中压入一个“栈帧”,而栈帧结构包括:
1、局部变量表:
有槽位,用来存储方法中的局部变量的数据,槽位从0开始
2、操作数栈:
会根据JVM“执行引擎”的指令来操作被加入其中的数据。操作数栈的数据来源是JVM堆中“运行时常量池,串池”,JVM虚拟机栈的栈帧中的局部变量表。
3、方法返回地址:
当方法运行结束时,返回到哪
4、动态链接
模拟main方法字节码执行过程
===============
1.执行main方法时,JVM会启动一个main线程,则会创建main线程的虚拟机栈,程序计数器,本地方法栈(本次不涉及),虚拟机栈中会创建好局部变量表,操作数栈,且表和栈的大小已经在class文件中确定了。程序计数器记录执行引擎将要执行的指令。
iconst_0指令表示 创建一个常量0,并压入操作数栈中
将操作数栈顶数据存入(istore) 局部变量表槽位1处(_1)
getstatic获取静态变量,并压入操作数栈
ldc是指加载常量池或串池中的常量到 操作数栈中
ldc #3 就是加载“Hello World”到操作数栈中
invokevirtual指令用于调用对象的实例方法。
如invokevirtual #4 调用的是println方法,该方法的对象是0x001,即System.out
该方法的参数即“Hello World”。
所以需要main栈帧中操作数栈中的数据就被清空了。当println执行结束后,其栈帧被释放。
后面执行引擎中的GC模块会选择时机工作,清除掉堆中的0x001对应的对象
通过字节码来破解x=x++问题
===============
public class HelloWorld {
public static void main(String[] args) {
int x = 0;
int i = 0;
while (i<4){
x= x++;
i++;
}
System.out.println(x);
}
}
这道题目我曾经很困惑,为啥x不是4,而是0。
现在通过反编译其字节码就可以知道了
最后:学习总结——MyBtis知识脑图(纯手绘xmind文档)
学完之后,若是想验收效果如何,其实最好的方法就是可自己去总结一下。比如我就会在学习完一个东西之后自己去手绘一份xmind文件的知识梳理大纲脑图,这样也可方便后续的复习,且都是自己的理解,相信随便瞟几眼就能迅速过完整个知识,脑补回来。下方即为我手绘的MyBtis知识脑图,由于是xmind文件,不好上传,所以小编将其以图片形式导出来传在此处,细节方面不是特别清晰。但可给感兴趣的朋友提供完整的MyBtis知识脑图原件(包括上方的面试解析xmind文档)
除此之外,前文所提及的Alibaba珍藏版mybatis手写文档以及一本小小的MyBatis源码分析文档——《MyBatis源码分析》等等相关的学习笔记文档,也皆可分享给认可的朋友!
若是想验收效果如何,其实最好的方法就是可自己去总结一下。比如我就会在学习完一个东西之后自己去手绘一份xmind文件的知识梳理大纲脑图,这样也可方便后续的复习,且都是自己的理解,相信随便瞟几眼就能迅速过完整个知识,脑补回来。下方即为我手绘的MyBtis知识脑图,由于是xmind文件,不好上传,所以小编将其以图片形式导出来传在此处,细节方面不是特别清晰。但可给感兴趣的朋友提供完整的MyBtis知识脑图原件(包括上方的面试解析xmind文档)
[外链图片转存中…(img-nZpQxKTr-1714801113965)]
除此之外,前文所提及的Alibaba珍藏版mybatis手写文档以及一本小小的MyBatis源码分析文档——《MyBatis源码分析》等等相关的学习笔记文档,也皆可分享给认可的朋友!