java虚拟机内存模型
一个类的执行过程。
黄色的是线程共享的。
蓝色的是每一个线程独有的。
程序计数器
你的程序马上要执行的下一行代码对应的地址指针
方法区
- 静态变量
- 常量
- 类元信息,把类文件解析成不同的部分
Math.class字节码文件,主要被类装载子系统装载到方法区
加入有new了两个math对象,那么在堆中,就会有两个math对象,并且这些对象都会指向Math.class在方法区里面的类原信息。
堆
new的对象存放在堆里面。栈上面有局部变量表,存放math,指向堆上面的math对象,堆上面的对象通过动态链接,指向方法区里面的类元信息。
栈
栈是线程内部的局部变量的空间。
每一个线程都有一个栈内存空间,也就是运行的局部变量。也就是上面的a b c
栈的内部还有一块区域,叫做栈帧
每调用一个方法,都会在栈里面开辟一片独立的空间,叫做栈帧
栈是一块内存空间,同时也是一个数据结构。math()方法结束之后,到main()方法的栈帧里面。
本地方法区
native方法代表的就是一个本地方法。
start0就是一个本地方法。是c语言实现的。会找c语言的库函数xx.dll
现在有一些跨语言的框架来进行交互。
c语言执行本地方法栈的之后,也会有一个栈帧。本地方法栈也是每一个线程独有的。
栈帧
- 局部变量表
- 操作数栈
- 动态链接
- 方法出口
将Math.java进行反汇编:javap命令
分析math()方法对应的字节码:
iconst_1 将int类型的常量1压栈 将1放入操作数栈上面
istore_1 将1存入到局部变量a上面
iconst_1 将int类型的常量2压栈
istore_2 将int类型的变量传入局部变量2
这个时候,对应的程序计数器指向的是4,也就是iload_1
iload_1 从局部变量1中装载int类型的值。
iload_2 从局部变量2中装载int类型的值。
idd 执行int类型的加法
从栈顶弹出两个元素,然后做四则远算。
bipush 10
将一个8位带符号的整数10压操作数栈
imul
从栈顶弹出两个元素,然后做四则运算
iload_3将第三个局部变量放在操作数栈。
最后return
方法出口
更新程序计数器的值,以及返回现场的一些信息。
动态链接
new出来的对象都放在堆里面。
通过具体的指针,指向方法区里面的同一块类元信息,这个就是一个动态链接的过程。
堆的内部结构
在栈里面,不管是局部变量表,还是操作数栈,在运行完毕之后,都会出栈,空间都会回收,那么堆里面的数据怎么回收呢,这个就有GC机制。
堆的内部结构图:
1. Eden区
new出来的对象都是先放在Eden区的,如果太大,可能会放在老年代。
程序运行的时候,可以分配堆的大小的,默认情况下,老年代会占400M内存空间。年轻带200M内存空间
一旦Eden区放满,那么调用GC,垃圾收集机制。
GC是执行引擎的一小部分。当没有任何指针指向这两个对象的时候,称为垃圾对象。
GC会把无引用的对象进行清理,有引用的对象在GC完成之后,会移动到Survivor的From区。
2. From区
执行第一次GC的时候,有引用的对象会从Eden区移动到From区,From区满了之后,也会做GC,那么剩下的有用对象会移动到To区
math对象这个时候没有引用了,就会被垃圾收集机制进行回收。
3. To区
当对象都移动到From区之后,之前的From变为To区,之前的To编程From区,一般一直轮询15次GC轮询。
4. 老年代
15次GC都还没清理,那么会放在老年代,比如线程池对象,或者程序运行过程中非常有用的对象。
当老年代放满之后,也会执行GC,那就就是full GC,之前的都是micro GC
full GC可能会卡主程序Stop the world,来进行垃圾清理。
OOM内存溢出
当触发full GC之后,不能释放任何资源,但是仍然有新的对象过来,这个时候就会造成内存溢出。