最近学习一下多线程,所以了解一下线程不安全原因以及底层实现:
jvm组成
class Content :是由类加载器把class文件加载到内存中的一块内存,与class文件的区别只是存放的地址不同,内容完全一样,class文件放在硬盘上,classContent在内存中。
class对象:是由类加载器对classContent解析出的对象,存放在方法区中。
方法区(一种规范,以下两种实现不能共存):
永久代:在堆上,jdk1.8之前用
元空间:在直接内存里,最小20M,最大无穷,元空间最小最大一般设置成一样的,防止内存抖动,设置成物理内存的1/32
虚拟机栈:默认1M,每创建一个线程,就会创建一个虚拟机栈。
栈帧:在一个线程中每调用一个方法就会创建一个栈帧。
栈帧的组成:
局部变量表的大小在编译期就确定了
java跨平台:jvm是基于内存的,java文件由javac编译成.class文件到硬盘上。
分析下面一段代码:
结构:
运行的时候这就是一个线程,因此创建了一个虚拟机栈。
变量j的内容,也就是new出来的东西在堆内存中,而指向这个变量的指针,也就是j 存放在方法区中,方法区中存放的是一个内存地址,与堆内存中这个变量的内存地址一致。
4个方法:init,clinit,main,add。
动态链接存的是类方法的直接地址,上边这个类编译解析后的方法区中有4个方法(init,main,add,clinit)
在main方法中,j2是局部变量,j2指向的是堆内存中的对象。
在add方法中,局部变量有两个但却占用3个slot,原因是在0的位置是this指针,所有非静态方法的局部变量0的位置都是 this指针。
a怎样实现赋值的?
首先,局部变量表在index=1的位置创建a,操作数栈把10压入栈顶,然后操作数栈pop栈顶元素复制给a,b同理,
怎么实现return a+b?
把index=1的值压入操作数栈顶,把index=2的值压入操作数栈顶,然后pop 20,pop 10进行相加,相加后的值再压入栈。
最后实现return(返回地址):
4部分:把局部变量表的指针恢复到main方法的局部变量表指针,把操作数栈的指针恢复到main方法的操作数指针,把程序计数器指向到main方法内的位置,
清空栈帧的内存。
main方法的执行流程:
操作数栈中创建压入一个空对象,然后复制这个空对象并压入,然后pop出这个对象复制给init方法局部变量index为0的this,然后继续pop出对象复制给main方法局部变量index为1的j2,此时操作数栈为空。。。
下图的前4步就是new一个对象的过程。
下图main方法还执行了一个打印操作,跟上图代码稍有区别。
这里查看字节码用的是java Bytecode Editor,下载后运行jbe.sh,打开class文件。
eclipse找class文件位置方法:
window-show view -navigator,然后在bin目录下就是class文件,右键properties打开class文件的位置。
推荐博客:https://blog.csdn.net/sureSand/article/details/78701477
https://blog.csdn.net/wqq3670/article/details/105401190