(一)JAVA内存区域
本文讲解以sun公司的 HotSpot虚拟机为例
根据Java虚拟机规范,将jvm分为如下五个区域
- 程序计数器
程序计数器是一块较小的内存空间,它可以看作是当前线程执行字节码的行号指示器
- 它是线程私有的
- JVM规范中唯一没有规定OutOfMemoryError情况的区域
- 如果正在执行的是Native方法,则这个计数器值为空
- 方法区
方法区是所有线程共享的,用于存储已被虚拟机加载的类信息、static变量、常量
运行时常量池
是方法区的一部分,class文件除了有类的字段、接口、方法等描述信息之外,还有常量池用于存放编译期间生成的各种字面量和符号引用
- Java栈(虚拟机栈)
栈描述的是Java方法执行的内存模型
每个方法被执行的时候都会创建一个栈帧用域存储局部变量,操作栈,动态链接,方法出口信息等。每一个方法被调用的过程就是对应一个栈帧,再虚拟机栈中从入栈到出栈的过程【栈先进后出】。
- 他是线程私有
- 用域存储基础数据类型对象、自定义对象引用
- 线程请求的栈深度大于虚拟机允许的栈深度,将抛出StackOverflowError
- 虚拟机栈空间可以动态扩展,当动态扩展是无法申请到足够的空间时,抛出OutOfMemory异常
-
本地方法栈
本地方法栈是与虚拟机栈发挥的作用十分相似,区别是虚拟机栈执行的是Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的native方法服务,可能底层调用的c或者c++,我们打开jdk安装目录可以看到也有很多用c编写的文件,可能就是native方法所调用的c代码。 -
堆
堆是java虚拟机管理内存最大的一块内存区域,因为堆存放的对象是线程共享的,所以多线程的时候也需要同步机制。因此需要重点了解下。
java虚拟机规范对这块的描述是:所有对象实例及数组都要在堆上分配内存,但随着JIT编译器的发展和逃逸分析技术的成熟,这个说法也不是那么绝对,但是大多数情况都是这样的。
即时编译器:可以把把Java的字节码,包括需要被解释的指令的程序)转换成可以直接发送给处理器的指令的程序)
–
逃逸分析:通过逃逸分析来决定某些实例或者变量是否要在堆中进行分配,如果开启了逃逸分析,即可将这些变量直接在栈上进行分配,而非堆上进行分配。这些变量的指针可以被全局所引用,或者其其它线程所引用。
–
注意:它是所有线程共享的,它的目的是存放对象实例。同时它也是GC所管理的主要区域,因此常被称为GC堆,又由于现在收集器常使用分代算法,Java堆中还可以细分为新生代和老年代,再细致点还有Eden(伊甸园)空间之类的不做深究。
–
根据虚拟机规范,Java堆可以存在物理上不连续的内存空间,就像磁盘空间只要逻辑是连续的即可。它的内存大小可以设为固定大小,也可以扩展。
–
当前主流的虚拟机如HotPot都能按扩展实现(通过设置 -Xmx和-Xms),如果堆中没有内存内存完成实例分配,而且堆无法扩展将报OOM错误(OutOfMemoryError)