程序计数器
程序计数器是一块较小的内存空间它可以看作是当前线程所执行的字节码的信号指示器,字节码解释器工作时就是通过改变计数器的值来选取下一条需要执行的字节码指令,分支,循环,跳转,异常处理,线程恢复等,每个线程都有自己独立的程序计时器。
栈
栈也是线程私有的,它的生命周期和线程一致,描述的是Java方法执行的内存模型,每一个方法执行时都会创建一个栈帧,栈帧中存储着局部变量表,操作数栈,动态链接,方法出口等信息,
每一个方法的执行就是栈帧在虚拟机的入栈和出栈过程,
局部变量表:
存放编译期可知的各种基本数据类型、对象引用类型和returnAddress类型(指向一条字节码指令的地址:函数返回地址)。
64位长度的long、double占用两个局部变量空间Slot。其余的数据类型只占用一个
局部变量表所需的内存空间在编译期确定,当进入一个方法时,方法在栈帧中所需要分配的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小
异常:线程请求的栈帧深度大于虚拟机所允许的深度抛出StackOverFlowError异常,如果虚拟机栈可以动态扩展(大部分虚拟机允许动态扩展,也可以设置固定大小的虚拟机栈),但是无法申请到足够的内存抛出OutOfMemorError异常。
本地方法栈
虚拟机栈执行的是Java的方法,本地方法栈是执行native方法,native方法就是别的语言写的方法,例如C。java在和底层系统交互的时候会用到C,还有用到一些C写的工具的时候,就会用到native方法,它和上面提到的栈作用基本相似。
java堆
堆是被所有线程共享的内存区域,存放对象实例和数组,该区域的内存由垃圾收集器管理,
下面是一个简单的小程序使堆内存溢出。
import java.util.*;
public class Test10
{
static class Test{}
public static void main(String args[]){
List<Test> list=new ArrayList<Test>();
while(true){
list.add(new Test());
}
}
}
方法区
和堆一样是被所有线程共享的区域,用于储存已被虚拟机加载的类信息,常量,静态变量,
这部分内存也归垃圾收集器管理,这个区域主要是常量池的回收,和对类型的卸载。
jvm在方法区储存的信息如下:
类型信息:这个类的完整有效名,这个类的直接父类的完整有效名,这个类的修饰符(public abstract,final等)这个类的接口的有序列表,
还有类型的常量池,域,方法信息,static变量。
静态常量池:.class文件的常量池包含字符串等字面量,还包括类方法的信息,主要存放的就是俩个类常量,字面量和符号引用量,
运行时常量池:类在被加载后,class文件的常量池进入内存,保存在方法区中,运行时常量池就是指方法区中的运行时常量池。运行时常量池的一个大特点就是,动态性。运行期间有可能有新的常量放入池中。
直接内存
直接内存不是虚拟机运行时数据区的一部分,也不是虚拟机规范定义的内存区域,在java1.4中有NIO类,引入了一种基于通道和缓冲区的I/O方式,它使用native函数直接在堆外分配内存,然后再堆内创建一个对象作为引用,这样在一些场景中能显著提高性能,避免了在java堆和native堆中来回复制数据。