一、概念
(1)、jvm在运行时,会将它管理的内存区域分为堆、栈、方法区、程序计数器等区域,
(2)、堆(head)和方法区是线程共享的,是存储数据的区域,解决的是数据存储的问题,
即数据怎么放、放在哪儿。
(3)、栈(stack)和程序计数器是线程私有的,是运行时的区域,解决程序的运行问题,
如何处理数据。
二、分别的作用:
(1)、堆(head)
是所有线程共享的区域。
堆是jvm管理的内存中最大的一块,用来存放对象,总体分为老年代和新生代,
其中新生代又分为三个部分,eden空间、from Survivor空间、to survivor空间,
默认按照8:1:1比例分配。
(2)、栈(stack)
是线程私有的区域,一个线程对应一个栈。
栈是当前线程内,方法运行时独立出来的内存空间,他的生命周期与线程同步,线程被回收这个
栈也就消失了。
栈本质是是一种FILO(先进后出的)的数据结构,执行方法时,
栈里面就加入一个栈帧(入栈),方法执行完成之后就弹出一个栈帧(出栈)。
所以方法的返回顺序,是后执行的方法先返回结果。
同一线程内,一个方法对应一个栈帧,而一个栈帧里面存放的是以下数据:
<1>、局部变量表:
存放当前栈帧的变量,也就是方法内部的局部变量
<2>、操作数栈
任何数据的处理,都是经过操作数栈来操作的,
比如先将常量或者局部变量表内的数据加载到操作数栈(入栈),
然后做运算或者直接弹出到局部变量表(出栈)。
也就是局部变量表里面是无法直接做运算的,
需要另外的一块内存空间做完运算之后再返回到局部变量表。
相当于一个数据运算中心,从常量池和局部变量表拿源数据,
加工后再返回到局部变量表。
<3>、动态链接
在编译阶段,会将方法和变量引用以符号引用的方式存入常量池
在运行阶段,当前方法里面调用另外一个方法,,当前栈帧里面会有一个动态链接指向
常量池对应的方法的符号引用, 利用这个符号引用转换为调用方法的直接引用,这样就实现了方法里面调方法。
也就是在栈帧的动态链接里面存的是常量池的某个地址,
根据常量池里面对应的符号引用转换为直接引用,
这样就可以在当前栈帧里面调用到另外一个方法。
<4>、方法返回值
方法执行到最后,会将执行结果返回到前一个栈帧里面的局部变量表中,
也就是结果回返回到调用者的局部变量表中。
比如main方法里面的add(),
执行完add最后一条指令是return,
会将此时操作数栈里面的值弹出到外层方法局部变量表中的对应变量中去。
<5>、操作数栈与局部变量表的交互过程图:
实际上就是将常量或者局部变量表中的变量值压入操作数栈进行运算,然后弹出操作数栈到局部变量表的指定变量中。
线程请求的栈深度大于虚拟机所允许的深度,
将抛出StackOverflowError异常,一般这种情况是死循环或者无限递归造成的。
当无法申请到足够的内存时会抛出OutOfMemoryError异常,
一般这种情况是jvm的gc机制回收垃圾数据回收不了,导致堆内存占满造成的。
(3)、方法区
存储已被虚拟机加载的类信息、常量、静态变量、编译后的代码等数据。
也就是类加载时就可以确认的数据。
(4)、程序计数器:
记录当前线程下一条要执行的指令
因为多线程是会切换执行不同线程指令的,所以当前线程需要记录一个标记,
方便线程切换回来的时候知道从哪条指令继续执行。
是线程私有的,各个线程之间的计数器不会相互影响。