前言
最近在学习java虚拟机的知识,看书的同时也归纳一些知识。
对于java来说,内存的管理和控制都交给了java虚拟机,也不容易出现内存泄漏的和内存溢出的问题,但是一旦出现问题,不了解虚拟机就很难处理和排除这些问题。
运行时数据区域
java虚拟机将程序运行时内存分为若干个不同的区域。
每个区域都有自己的用途,以及创建和销毁的时间,有的是随着虚拟机进程的启动存在而有的是依赖线程的启动结束而创建和销毁。
(1)PROGRAM COUNTER REGISTER(程序计数器)
一块较小的内存空间,可以看成是当前线程所执行的行号指示器。java中的多线程是通过线程轮流切换并分配处理器执行时间来实现的,为了让线程切换后恢复到正确的执行位置,每个线程都有一个独立的程序计数器,来保证线程互不影响。称这类内存区域为“线程私有”的内存。执行java方法计数器记录的是正在执行的虚拟机字节码指令的地址,如果是Native方法,计数器是空的。唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError的区域。
(2)Java Virtual Machine Stacks(java虚拟机栈)
java虚拟机栈也是线程私有的,生命周期和线程相同。每个方法在执行时都会创建一个栈帧用来存储局部变量表、操作数帧、方法出口等信息。每个方法从调用到执行完成,都是一个栈帧在虚拟机中入栈到出栈的过程。
虚拟机栈为虚拟机执行java方法服务,我们通常说的栈就是虚拟机栈,或者说是虚拟机栈中的局部变量表。局部变量表存放了编译器可知的基本类型数据和对象引用(不是对象本身)和returnAddress类型(指向一条字节码指令的地址)。
该区域规定了两种异常:
当线程请求的栈深度大于虚拟机所允许的深度,会抛出*Error异常。
当扩展时无法申请到足够的内存,会抛出OutOfMemoryError异常。
(3)Native Method Stack(本地方法栈)
和虚拟机栈的作用相似,只是本地方法栈为执行Native方法服务。常用的Sun HotSpot虚拟机把虚拟机栈和本地方法栈合二为一。
以上都是线程独有的内存空间,下面是线程共享的内存区域。
(4)Java Heap(Java堆)
堆是虚拟机中管理内存中最大的一块,被所有线程共享的内存区域,在虚拟机启动时创建,唯一的目的就是存放对象实例。在java虚拟机规范中描述的就是:对象实例和数组都要在堆上分配。堆不需要连续的内存。java堆是垃圾收集器管理的主要区域,也被称为“gc堆”。为了更好的回收和更快的分配,堆还可以细分,这里大致了解,分为新生代,老年代,再细致点可分为Eden空间、From Survivor空间、To Survivor空间。
java堆可实现成固定大小的和可扩展的,可扩展的可通过-Xmx和-Xms来分别设置堆的最小值和最大值控制,当堆中没有内存来分配实例,也不能扩展时,就会抛出OutOfMemoryError异常。
(5)Method Area(方法区)
所有线程共享的内存区域,用来存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 java虚拟机规范把方法区规范为堆得一个逻辑部分,但有个别名叫“None-Heap”,为了把它和堆区分。当无法满足内存分配时,会抛出OutOfMemoryError异常。
(6)Runtime Constant Pool(运行时常量池)
运行时常量池也是方法区中的一部分。class文件中除了类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池,用于存放编译器生成的各种字面量和符号引用,翻译出的直接引用也会存储在运行中运行常量池中。运行时常量池相当于Class文件常量池的另一个重要特征就是具有动态性,不要求只有在编译器才产生,运行时也可以将新的常量放入池中,用的较多的就是String类的intern()方法。会抛出OutOfMemoryError异常。