Java内存模型
Java的内存模型划分为5个部分:
1.方法区:线程共享的
2.堆区:线程共享,存放对象
3.虚拟机栈:线程私有,存放的是线程运行时局部变量等
4.本地方法栈:线程私有的,native方法
5.程序计数器:线程私有的
线程私有的内存区域:生命周期都是和线程同步
程序计数器
程序计数器是一块较小内存空间,用来记录当前程序所执行的字节码的行号。
字节码解释器工作时通过改变这个计数器的值来选取下一条要执行的字节码指令,分支,循环,跳转,异常处理等都需要依赖这个程序寄之前来完成。
线程切换后也需要恢复到正确的执行位置,因此,每一个线程都有一个独立到的程序计数器,各个线程的计数器相互独立,互不影响,因此线程计数器是必须私有的
作用:
1、字节码解释器通过改变程序计数器来依次读取执行,从而实现代码的流程控制
2、在多线程下,程序计数器用于记录当前线程执行的位置,方便在程序切换后能够继续正确执行
注意:程序计时器是唯一一个不会抛出OutOfMemoryError的(OOM)内存区域
程序计数器的生命周期是和线程同步的,随着线程的创建而创建,随着线程的消亡而死亡。
虚拟机栈
与程序计数器一样,虚拟机栈也是线程私有的,生命周期和线程相同。
注意:在程序执行过程中,可以理解为方法调用方法,每一个方法在虚拟机栈上对应就是一个栈帧,
每一个栈帧包含信息:局部变量表、操作数栈、动态链接、方法出口信息
局部变量表:主要存放编译器可知的各种数据类型(boolean、byte、char、short、int、float、double、long),对象的引用(Reference类型,他不是对象本身,是一个执行对象起始位置的引用指针,也可以是指向一个代表对象的句柄)
虚拟机栈会出现两种异常:StackOverflowError和OutOfMemoryError
1.StackOverflowError:若虚拟机栈的大小不允许动态扩容,当线程请求的栈深度超过当前虚拟机栈的最大深度的时候,就会出现StackOverflowError的异常。
2.OutOfMemoryError:若虚拟机栈的大小允许动态扩容,当前的线程请求栈是内存用完了,无法再动态扩容了,此时抛出OutOfMemoryError异常
虚拟机栈是线程私有的,生命周期随着线程创建而创建,随着线程消亡而死亡。
本地方法栈
本地方法栈和虚拟机栈作用非常相似
区别:虚拟机栈为虚拟机执行Java方法(字节码)服务,本地方法栈为虚拟机使用到的native方法服务
本地方法栈执行时候,也会为本地方法创建一个个栈帧,栈帧中也包含局部变量表,冬天连接、操作数栈、出口信息等,方法执行完成后也会出栈并释放掉内存空间
也会抛出StackOverflowError和OutOfMemoryError的异常
堆
java虚拟机所管理的内存区域最大的一个内存空间就是堆,是线程共享的一块内存区域,在虚拟机启动的时候创建,内存区域用来存放对象实例,几乎所有的对象实例以及数据都在这里分配内存
GC区域即垃圾回收主要作用的就是对象实例,因此GC所作用的区域就是堆区域。
从垃圾回收的角度,堆可以细分为新生代和老年代
新生代细分为:Eden空间,From Survivor空间和To Survivor空间,更细的划分是尾了更高的回收内存或更快的分配内存
永久代:
在jdk 1.6及之前,常量池就存在永久代
在JDK1.7时,有,逐步去“永久代”
在JDK 1.8之后,无永久代, 取而代之的是一个“元空间”(Metaspace)区域,永久代是属于JVM堆中的内存空间,元空间使用的物理内存,直接受到本机的物理内存限制。
该内存区域也是会抛出OutOfMemoryError异常。
方法区
方法区和堆一样,是线程共享的区域,存储被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据都存放在方法区
生命周期和堆相同,随着JVM的创建而创建随着JVM的消亡而消亡
也会抛出OutOfMemoryError的异常。