JVM基础(一):JAVA内存区域
Java内存区域
上面这张图就是jvm运行时的状态。具体划分为如下5个内存空间:
1-3为线程私有,4-5为线程共享
- 1、程序计数器:为了线程切换后能恢复到正确的执行位置。线程私有
- 2、Java虚拟机栈:虚拟机栈描述的是Java方法执行的内存模型:方法被调用时创建栈帧-->局部变量表->局部变量、对象引用。线程私有
- 3、本地方法栈:为虚拟机执使用到的Native方法服务。线程私有
- 4、Java堆:存放所有new出来的东西,在虚拟机启动时创建,垃圾收集器管理的主要区域。线程共享
- 5、方法区:存储被虚拟机加载的类信息、常量、静态常量、静态方法等。线程共享
- 5、1运行时常量池(方法区的一部分)
1.程序计数器
(1)由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。
(2)如果线程正在执行的是一个 Java 方法,计数器记录的是正在执行的字节码指令的地址;如果正在执行的是 Native 方法,则计数器的值为空。
(3)为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器。
(4)程序计数器是唯一一个没有规定任何 OutOfMemoryError 的区域。
举个例子 程序先去执行A线程,执行到一半,然后就去执行B线程,然后又跑回来接着执行A线程,那程序是怎么记住A线程已经执行到哪里了呢?这就需要程序计数器了
2.Java虚拟机栈
虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建之间栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
在Java虚拟机规范中,对这个区域规定了两种异常情况:
(1)如果线程请求的栈深度太深,超出了虚拟机所允许的深度,就会出现StackOverFlowError(比如无限递归。因为每一层栈帧都占用一定空间,而 Xss 规定了栈的最大空间,超出这个值就会报错)
(2)虚拟机栈可以动态扩展,如果扩展到无法申请足够的内存空间,会出现OutOfMemoryError
3.本地方法栈
(1)本地方法栈与虚拟机栈锁发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。
(2)有的虚拟机(比如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。
4.Java堆
(1)Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
(2)Java堆是垃圾收集器管理的主要区域。关于垃圾回收的知识,具体请看我的另一篇博文:JVM基础(三):Java垃圾回收
(3)Java堆还可以细分为:新生代和老年代;再细致一点的有Eden空间、From Survivor空间、To Survivor控件等。
5.方法区
存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
5.1运行时常量
(1)运行时常量池是方法区的一部分,用于存放编译期生成的各种 字面量 和 符号引用。
(2)字面量比较接近Java语言层次的常量概念,如文本字符串、被声明为final的常量值等。
(3)符号引用则属于编译原理方面的概念,包括以下三类常量:类和接口的全限定名、字段的名称和描述符 和 方法的名称和描述符。
(4)因为运行时常量池是方法区的一部分,那么当常量池无法再申请到内存时也会抛出 OutOfMemoryError 异常。
//TODO Java对象的创建与定位
//TODO Java内存异常的产生