前言
内存结构
CPU不可以直接操作堆区数据,需要通过中间高速缓存区进行相关数据操作;
dex指令理解
我们知道Android虚拟机执行的是dex指令,我们可以先使用javac
指令编译class,再通过dx指令
编译查看对应java方法的dex指令;
- 局部变量方法调用dex指令查看;
public class Person {
public void run() {
Person person = new Person();
person.run();
}
}
编译成Person.class文件后,使用dx --dex --verbose --dump-to=a.txt --dump-method=Person.run --verbose-dump Person.class
dx指令编译Person.run方法,删除无用信息后,得到文件内容以及解释如下:
Person.run:()V:
regs: 0002; ins: 0001; outs: 0001
0000: new-instance v0, Person // 1.在高速缓存中声明一个v0;2.将Person.class加载到方法区;3.在堆区实例化一个对象person;4.v0指向堆区的person对象;
0002: invoke-direct {v0}, Person.<init>:()V // 调用构造方法初始化person对象,并将返回值赋给v0
0005: invoke-virtual {v0}, Person.run:()V // 调用v0执行person.run方法;
0008: return-void //返回void
- 全部全量方法调用dex指令查看;
public class Person {
Person person;
public void run() {
person = new Person();
person.run();
}
}
编译成Person.class文件后,使用dx --dex --verbose --dump-to=a.txt --dump-method=Person.run --verbose-dump Person.class
dx指令编译Person.run方法,删除无用信息后,得到文件内容以及解释如下:
Person.run:()V:
regs: 0002; ins: 0001; outs: 0001
0000: new-instance v0, Person // 1.在高速缓存中声明一个v0;2.将Person.class加载到方法区;3.在堆区实例化一个对象person;4.v0指向堆区的person对象;
0002: invoke-direct {v0}, Person.<init>:()V // 调用构造方法初始化person对象,并将返回值赋给v0
0005: iput-object v0, v1, Person.person:LPerson; //将v0赋值v1,则全局变量person(v1)就指向了堆区中的实例对象;
0007: iget-object v0, v1, Person.person:LPerson; //由于cpu不能直接使用堆区数据(v1),cpu将v1的值又读了一遍到v0中;
0009: invoke-virtual {v0}, Person.run:()V // 调用v0执行person.run方法;
000c: return-void
Q:为什么DCL单例需要使用 volatile关键字修饰?
A:当指令进行了重排,可能会出现Person.<init>:()V
指令在
0005: iput-object v0, v1, Person.person:LPerson 0007: iget-object v0, v1, Person.person:LPerson;
指令之后的情况,就会导致没有调用构造方法就使用了person对象的情况,而从引起问题;
结语
如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )