java运行时数据区域
java虚拟机在执行java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。 这些区域每一个都有自己的用途。java虚拟机所管理的内存包括以下几个运行时数据区域。
程序计数器
程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型例,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都需要以来这个计数器完成。
由于java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每一条线程都要有一个独立的程序计数器。各线程之间的计数器互不影响,独立存储。这种,线程之间独立的区域,我们管他们叫做线程私有的内存。因此,程序计数器是线程私有的内存区域。
java虚拟机栈
和程序计数器一样,java虚拟机栈也是线程私有的,他的生命周期和线程相同。虚拟机栈描述的是java方法执行的内存模型,每一个方法在执行的同时都会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法从调用直到执行完成的过程,就对应这一个栈帧在虚拟机栈中入栈到出栈的过程。
局部变量表存放了编译期间可知的基本数据类型、对象的引用(注意不是对象,而是对象的引用)。其中基本变量中的64位长度的long和double会占用2各局部变量空间,其他数据类型只会占据一个。
局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在栈帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。
在Java虚拟机规范中,对这个区域规定了两种异常:如果线程请求的栈的深度大于虚拟机所允许的深度,将抛出stackOverllowError异常;如果虚拟机可以动态扩展,如果扩展时无法申请到足够的内存,那么就会抛出OutOfMemoryError异常
本地方法栈
本地方法栈于虚拟机栈所发挥的作用非常相似,他们之间的区别是一个执行Java方法,一个执行本地方法——一个Native Method就是一个java调用非java代码的接口。
java堆
java堆是虚拟机所管理内存最大的一块。java堆内存是被所有的线程共享的。java堆里面存放的就是对象的实例,几乎所有的对象实例都在这里分配内存。 在java虚拟机规范中描述,所有的对象实例以及数组都是要在堆上分配内存的。但是随着一些JIT编译器的发展与逃狱分析技术的逐渐成熟,所有对象都在堆上分配内存并不是那么绝对了。
java堆是垃圾收集器的主要管理区域。从内存回收的的角度来看,现在收集器基本都是采用分代收集算法,java堆还可以分为:新生代和老生代。
根据java虚拟机的规范,java堆可以处于物理上不连续的内存空间,只要逻辑上是连续的即可,就像我们的磁盘空间一样,在实现的时候,既可以实现固定大小的,也可以实现可扩展的的,如果在分配内存的时候,已满,并且不可扩展,那么将会抛出OutOfMemoryError异常
方法区
和java堆一样,是各个线程共享的内存区域,它用于已被虚拟机加载的类信息,常量、静态变量,即时编译器编译后的代码等数据,法区也有一个别名叫做Non-Heap(非堆),用于与Java堆区分。对于HotSpot虚拟机来说,方法区又习惯称为“永久代”(Permancent Generation),但这只是对于HotSpot虚拟机来说的,其他虚拟机的实现上并没有这个概念。相对而言,垃圾收集行为在这个区域比较少出现,但也并非不会来收集,这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载上。根据Java 虚拟机规范的规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError 异常。
运行时常量池
运行时常量池是方法区的一部分。 Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量表,用于存放编译期生成的各种字面常量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放(JDK1.7开始,常量池已经被移到了堆内存中了)。 也就是说,这部分内容,在编译时只是放入到了常量池信息中,到了加载时,才会放到运行时常量池中去。运行时常量池县归于Class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入Class文件中常量池的内容才能进入方法区的运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用的比较多的是String类的intern()方法。
当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常,常量池属于方法区,同样可能抛出OutOfMemoryError异常。
补充:
直接内存
直接内存并不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域,但是这部分内存被经常使用。
在JDK1.4中加入NIO(new Input/Output)类,引用了一种基于通道与缓冲区的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。本机的直接内存不会受到java堆大小影响,但是会受到本机总内存大小以及处理器寻址空间的限制。
以上就是《深入理解JAVA虚拟机》中关于java内存区域的知识点了。