4、本地方法栈

  本地方法栈与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则为虚拟机使用到的Native方法服务。

  与Java虚拟机栈一样本地方法区也会抛出StackOverflowError和OutOfMemoryError异常。

5、Java堆

  Java堆是Java虚拟机所管理的内存中最大的一块,Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。该内存区域存在的唯一目的就是存放对象的实例,几乎所有的对象和数组(static关键字、Native关键字所修饰的方法除外)的实例都在Java堆中进行内存分配。

  Java堆是垃圾收集管理的主要区域,由于现在的垃圾收集器基本都采用分代收集算法,所以Java堆可以分为:新生代和老年代;再细致一点可以将新生代分为Eden区、Survivor区域(Survivor可以分为两个大小相同的区域分别为From Survivor和To Survivor,且两个区域可以进行角色互换)。绝大多数情况下,对象首先分配在Eden区,在一次新生代回收后(YGC),如果对象还存活,则会进入s0或者s1区(即From Survivor或者To Survivor区),之后经过若干次的垃圾回收之后,如果对象一直没有被回收,生存得足够长,该对象就会被移入老年代。

  根据Java虚拟机规范的规定,Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像磁盘空间一样。Java堆的容量可以使固定的,也可以随着程序执行的需求动态扩展,并在不需要过多空间时自动收缩(可以通过-Xmx和-Xms来进行Java堆大小的控制)。如果实际所需的堆超过了自动内存管理系统提供的最大容量,那么Java虚拟机将会抛出OOM异常。

6、方法区

  在Java虚拟机中,方法区是JVM内存区域非常重要的一块内存区域,与Java堆类似,方法区是各个线程共享的内存区域,它存储了每一个类的结构信息,例如,运行时常量池、字段、方法数据、构造函数和普通方法的字节码内容,还包括一些在类、实例、接口初始化时用到的特殊方法。

  方法区在虚拟机启动的时候创建,虽然方法区是堆的逻辑组成部分,但是简单的虚拟机实现可以选择在这个区域不实现垃圾收集与压缩(Java虚拟机规范),但是该区域却有一个别名叫做Non-Heap(非堆),目的应该是与Java堆区分开来。方法区的容量可以是固定的,也可以随着程序执行的需求动态扩展,并在不需要过多空间时自动收缩。方法区在实际内存中可以是不连续的。可以通过-XX:PermSize和-XX:MaxPermSize来控制持久代大小。如果方法区的内存空间不能满足内存分配请求,那么Java虚拟机将会抛出一个OOM异常。

7、运行时常量池

  运行时常量池是方法区的一部分。class文件中每一个类或接口的常量池表的运行时表现形式,包括了若干种不同的常量,从编译期可知的数值字面量到必须在运行期解析后才能获得的方法或字段引用。当创建类或接口时,如果构造运行时常量池所需要的内存空间超过了方法区所能提供的最大值,那么Java虚拟机将会抛出OOM异常。

8、直接内存

  直接内存并不是Java虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。在JDK1.4中新加入了NIO类,引入了一种基于通道与缓冲区的IO方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作,这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。

  本机的直接内存的分配不会受到Java堆大小的限制,既然是内存,肯定还是受到本机总内存大小以及处理器寻址空间的限制。在实际工作中该区域也会存在OOM异常。