JMM到底是个啥子
欲学多线程,先学JMM。
JMM(Java Memory Model),即Java内存模型,是一个逻辑概念,并不是说物理内存里就是这样的结构,其实内存中的物理地址本身对于操作系统也是一个逻辑地址。
- 线程独占:每个线程都有它独立的空间,随线程的生命周期而创建和销毁
- 线程共享:所有线程都能访问此内存数据,随虚拟机或者GC创建和销毁
方法区
JVM用来存储加载的类信息、常量、静态变量、编译后的代码等数据
堆内存
Java启动时创建此内存空间,用于存放类的实例,即对象,满了就会OOM(OutMemoryError),垃圾回收主要管理的就是堆内存。
虚拟机栈
每个线程都有自己的一个虚拟机栈,线程栈由多个栈帧组成,线程中执行的每个方法对应着各自的一个栈帧。 栈帧内容包括局部变量表、操作数栈、动态链接、方法返回地址、附加信息等,栈内存默认最大1M,超出则StackOverFlow
本地方法栈
用于执行一些native方法,调用操作系统的一些功能等,比如文件读写的底层涉及到文件系统,肯定要经过操作系统的,还比如jvm初始化时候执行的一些C++代码。虚拟机规范并没有规定具体实现,所以不同的虚拟机实现方式不同。
程序计数器(PC)
Program Counter Register,记录当前线程执行的字节码的位置,存储的时字节码的指令地址,执行native方法时PC里的值为空,相当于CPU切换执行线程时用于记录的小本本。
初探.class神秘的裙底风光
public class test{
public static void main(String[] args){
int x = 500;
int y = 100;
int a = x / y;
int b = 50;
System.out.println(a + b);
}
}
这段代码编译之后会变成什么鬼样子呢,来康康。
第一步先将java编译成class,因为.class是给jvm看的,人类并不能看懂,所以第二步将人类能够看懂的信息保存至test.txt
javac test.java
javap -v test.class > test.txt
打开test.txt,wow,awesome,这是记事本独享的moment
这里直接贴出来吧
Classfile /C:/Users/rice/Desktop/test.class
Last modified 2019-10-15; size 412 bytes
MD5 checksum 10535501b08a5c0e05319708975b247d
Compiled from "test.java"
public class test
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #5.#14 // java/lang/Object."<init>":()V
#2 = Fieldref #15.#16 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Methodref #17.#18 // java/io/PrintStream.println:(I)V
#4 = Class #19 // test
#5 = Class #20 // java/lang/Object
#6 = Utf8 <init>
#7 = Utf8 ()V
#8 = Utf8 Code
#9 = Utf8 LineNumberTable
#10 = Utf8 main
#11 = Utf8 ([Ljava/lang/String;)V
#12 = Utf8 SourceFile
#13 = Utf8 test.java
#14 = NameAndType #6:#7 // "<init>":()V
#15 = Class #21 // java/lang/System
#16 = NameAndType #22:#23 // out:Ljava/io/PrintStream;
#17 = Class #24 // java/io/PrintStream
#18 = NameAndType #25:#26 // println:(I)V
#19 = Utf8 test
#20 = Utf8 java/lang/Object
#21 = Utf8 java/lang/System
#22 = Utf8 out
#23 = Utf8 Ljava/io/PrintStream;
#24 = Utf8 java/io/PrintStream
#25 = Utf8 println
#26 = Utf8 (I)V
{
public test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=5, args_size=1
0: sipush 500
3: istore_1
4: bipush 100
6: istore_2
7: iload_1
8: iload_2
9: idiv
10: istore_3
11: bipush 50
13: istore 4
15: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
18: iload_3
19: iload 4
21: iadd
22: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
25: return
LineNumberTable:
line 3: 0
line 4: 4
line 5: 7
line 6: 11
line 7: 15
line 8: 25
}
SourceFile: "test.java"
这里有好多字节码指令看不懂,没关系,以后慢慢学。
这整段代码可以分为三个部分来看,先来讲讲第一部分,也即是最前面的几行, 分别是版本号和访问标志,版本号的规则JDK5、6、7、8分别对应49、50、51、52。
public class test
minor version: 0 //次版本号
major version: 52 //主版本号
flags: ACC_PUBLIC, ACC_SUPER //访问标志
常用的访问标志如下表
标志名称 | 标志值 | 含义 |
---|---|---|
ACC_PUBLIC | 0X0001 | 是否为public类型 |
ACC_FINAL | 0x0010 | 是否被声明为final,只有类可设置 |
ACC_SUPER | 0x0020 | 是否允许使用invokespecial字节码指令 |
ACC_INTERFACE | 0x0200 | 标志这是一个接口 |
ACC_ABSTRACT | 0x0400 | 是否为abstract类型 |
ACC_ANNOTATION | 0x2000 | 标志这是一个注解 |
ACC_ENUM | 0x4000 | 标志这是一个枚举 |
ACC_SYNTHETIC | 0x1000 | 标志这个类并非由用户产生的 |
接下来偷懒直接截个图,左边是class文件,右边是一些静态常量的枚举。
public test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
上面几行,就是传说的,一直不用我们自己写的,jvm帮我们加的,默认的无参构造函数,呼呼~
然后还有一部分就是最重要的入口函数,main方法了
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=5, args_size=1
0: sipush 500
3: istore_1
4: bipush 100
6: istore_2
7: iload_1
8: iload_2
9: idiv
10: istore_3
11: bipush 50
13: istore 4
15: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
18: iload_3
19: iload 4
21: iadd
22: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
25: return
LineNumberTable:
line 3: 0
line 4: 4
line 5: 7
line 6: 11
line 7: 15
line 8: 25
指令顺序执行,整个过程无非就是不停的将数值入栈、出栈、运算、结果再入栈,最后的剩下的,就是结果,这些助记符的意思这里就不挨个解释了,等下附上指令码表,不过别忘了这个class文件的text是我们通过javap得到的,jvm实际执行的,可不是这些简明易懂的助记符,而是实实在在的16进制指令。
执行过程中,计数器帮我们记住指令执行到的位置,当遇到调用新的方法时,JVM会保留犯罪现场(将当前栈帧压入虚拟机栈),去执行新的任务(创建新的栈帧),也就是在另一个方法里如此循环,直到执行完所有方法,返回最后的结果。
总结
好多都是平时知道一些但总也说不明白,这次总结了一下清晰多了,平时的代码都是IDE帮着编译,这次自己动手感觉竟然有点不错?(我看你是工作不饱和)