今天有同学问我多线程中方法内的变量为什么是线程安全的,讲了好久他都没明白,后来他都把方法区和方法搞成一样的了,好艰难,看看了解jvm内存是件多么重要的事情。
概念
java虚拟机在运行的时候,系统会分配一块内存给它,它会分配成不同的区域进行管理。全部为运行时数据区域大致是这样个样子的:
线程共享的区域为橘黄色
线程私有的区域为白色
线程共享的区域
方法区
- 说明:方法区是线程共享的内存区域。
- 功能:存储已被虚拟机加载的类信息,常量,静态变量等。
- 异常:由于可以选择固定大小的内存或者可扩展,内存中可以是不连续的,所以当方法区无法满足内存分配需求时,将会抛出OutOfMemoryError异常。
据说可以称之为永久代,因为方法区是用永久代来实现的(垃圾收集方式)。
Java堆
- 说明:java堆是线程共享的内存区域,是java虚拟机所管理的内存中最大的一块。
- 功能:存储对象实例和数组。
- 异常:如果堆中没有内存完成实例分配,并且堆也无法再扩展时,同java堆一样,将会抛出OutOfMemoryError异常。
java堆还可以细分为新生代和老年代,不论怎么分,存储的都是对象实例,只是为了更好的回收内存。
线程私有的区域
虚拟机栈
- 说明:线程私有的,生命周期与线程相同。
- 功能:描述的是java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等信息。
- 异常:
- 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。
- 如果虚拟机可以动态扩展,那么当扩展时无法申请到足够内存时,将会抛出OutOfMemoryError异常。
每一个方法的调用开始到结束的过程,就对应着一个栈帧在虚拟机中的入栈到出栈。
局部变量表中存储的为各种基本数据类型,对象引用(reference类型,可能是引用指针)。
本地方法栈
同虚拟机栈大致一样。
区别:
1. 虚拟机栈为虚拟机执行java方法(字节码)服务。
2. 本地方法栈为虚拟机执行Native方法服务。
Sun HostSpot虚拟机直接把本地方法栈和虚拟机栈合二为一了。
程序计数器
- 说明:是一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器。
- 功能:在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,例如分支,循环,跳转,异常处理,线程恢复等。
- 异常:此内存是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
由于java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器,都只会执行一条线程中的指令。
注:对于多核处理器来说是一个内核。