一篇文章了解JVM内存区的分布和作用的时候可以对比一下。注意,本文是基于JDK8来介绍的。首先,程序计数器是线程私有的。它也称为代码的行号指示符。字节码解释器通过改变程序计数器的位置来决定下一行要执行的代码。它没有OOM。如果线程正在执行Java方法,那么它记录正在执行的虚拟机字节码指令的地址,如果是native方法,它的值为空。Java虚拟机栈也是线程私有的,其语句周期与线程一致。每个线程创建的时候都会创建一个虚拟机栈,里面保存了每个栈帧,每个栈帧对应一个方法调用。既然知道虚拟机栈中存放的是每一个栈帧,那么就不难猜到虚拟机栈中存放的是什么了。Java虚拟机栈中存在OOM。当线程申请的栈深度大于虚拟机栈深度或者虚拟机栈可以动态扩展时,当栈扩展时无法申请到足够的内存,就会抛出OOM。虚拟机栈的内部结构:局部变量表:主要存储方法的参数,所有基本类型的数据和对象地址,以及返回地址类型(returnaddress)。它使用可变槽作为最小存储单元。Java虚拟机并没有规定一个variableslot占用多少内存空间,但是规定了一个variableslot可以存放32位以内的数据类型。如果存储的数据类型超过32位,如long、double,则使用两个变量槽进行存储。操作数栈:操作数栈是一个先进后出的操作数栈。当一个方法开始执行时,会相应地创建一个新的栈帧。该方法的操作数栈为空,主要用于保存计算过程的中间结果,同时作为变量在计算过程中的临时存储空间。如果被调用的方法有返回值,返回值会被压入当前栈帧的操作数栈。操作数栈不使用索引访问数据,而是通过push和pop操作完成数据访问。动态链接:用简单的英语来说,对方法的引用保存在堆栈帧中。当方法执行时,可以拿这个引用到运行时常量池中去寻找方法。动态链接的作用是将这些方法的符号引用转换为对调用方法的直接引用。方法返回地址:方法执行完成后,返回下一个要执行的代码位置的值,即程序计数器的值。那么,除了方法正常执行后退出之外,还有一种情况是方法异常退出。在这种情况下,不会返回任何值。对于抛出的异常,不会在栈帧中做任何记录,而是记录在异常表中。本地方法栈Java虚拟机栈为虚拟机执行Java方法服务,本地方法栈为虚拟机使用的本地方法服务。和JVM一样,有很多C语言写的方法,这就需要一个本地的方法栈来执行。Java堆Java堆是虚拟机中最大的内存空间。它由所有线程共享,并在虚拟机启动时创建。它的唯一目的是存储对象实例。如果面试问,是不是所有的对象实例都分配在堆中?这时候你必须回答,没有。随着即时编译技术的发展和进步,尤其是逃逸分析技术的日益强大,栈上分配、变量替换等优化方式使得实例“只能在堆上”分配不再是绝对的。Java堆是垃圾回收的主要区域。Java堆中经常出现新生代、老年代、永久代等。这里需要注意的是,这些并不是Java堆的物理内存布局。它被划分为垃圾收集器。内存布局。方法区方法区也是线程共享的区域。主要用于存放虚拟机加载的类型信息、常量、静态变量、即时编译器编译的代码缓存等数据。方法区可以被垃圾回收器回收,主要是类型卸载和常量池回收。方法区也会产生OOM。当方法区不能满足新的内存分配要求时,会抛出OutOfMemoryError异常。运行时常量池运行时常量池是方法区的一部分。Class文件中除了类版本、字段、方法、接口等信息外,还有一个常量池表,用于存放编译过程中产生的各种字面量和符号引用。动态链接看不懂,那就看运行常量池,回过头来看看好不好理解。
