Java虚拟机的内存区域

        今天看到java的各种数据类型所处内存的相关知识,感觉很是疑惑,所以就细看了《深入理解java虚拟机》这本书的Java内存管理部分。在这里总结一下,梳理一下思路,也方便以后查找。

         在这里参考了https://www.cnblogs.com/whgk/p/6138522.html,其中的几幅图我感觉画的很形象,有利于理解java虚拟机的内存。

运行时数据区域

        什么叫运行时数据区域呢?Java虚拟机在执行Java程序的过程中会把它所管理的内存划分若干个不同的数据区域。下图可以很好的表示出来。

Java虚拟机的内存区域

        这里值得注意的是:从JDK 1.7之后运行时常量池中的字符串常量池被放到了堆中,可能的原因大概是方法区放不下吧!

    1、程序计数器

                程序计数器是一块较小的内存空间,可以当做是当前线程所执行的字节码的行号指示器。

                当执行Java方法时,它记录的是正在执行的虚拟机字节码指令的地址;当执行Native方法时,这个计数器为空。

                格外注意的是:此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

    2、Java虚拟机栈

                Java虚拟机栈也是线程私有的,也就是我们常说的栈。每个方法在执行的同时都会创建(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。局部变量表存放了编译期可知的各种基本数据类型、对象引用和returnAddress类型(指向了一条字节码指令的地址)。

                这个区域规定了两种异常状况: 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出*Error异常;如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

    3、本地方法栈

                本地方法栈与虚拟机栈类似,区别是本地方法栈为虚拟机使用到的Native方法服务。

                与虚拟机一样,本地方法栈区域也会抛出*Error和OutOfMemoryError异常。

    4、Java堆

                Java堆是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例。所有的对象实例以及数组都要在堆上分配。

                Java堆还可以分为: 新生代和老年代;再细致一点有Eden空间、From Survivor空间、To Survivor空间等。从内存分配的角度来看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区(TLAB)。如果内存不够时,会抛出OutOfMemoryError异常。

    5、方法区

                方法区和Java堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译期编译后的代码等数据。

                这区域的内存回收目标主要是针对常量池的回收和对类型的卸载,尤其是类型的卸载,条件相当苛刻,但是这部分区域的回收确实是必要的。有很多bug都是由于此区域未完全清理导致的。

                当然此区域也存在无法满足内存需求时,将抛出OutOfMemoryError异常。

    6、了解一下直接内存

                《深入理解Java虚拟机》这本书中也提到了直接内存,它并不是虚拟机运行时数据区的一部分,但也要受到本机总内存大小以及处理器寻址空间的限制。为了防止困惑就不详细说了,一般我们也不大了解它,因为它不是在Java虚拟机内存中的。