1、java内存区域

1、java内存区域

运行时数据区

  • 程序计数器:线程私有的。当前线程执行字节码的行号指示器。
  • 虚拟机栈:线程私有的。描述的是java方法执行的线程内存模型。生命周期和线程相同,每个java方法被执行的时候,都会同步创建一个栈帧(方法运行期的基本数据结构)用来保存局部变量表、操作数栈、动态连接、方法出口等信息。每个方法被调用到执行结束的过程,就对应着一个栈帧在虚拟机栈入栈到出栈的过程。
      局部变量表:存放了编译期间可知的java各种基本数据类型、对象引用以及returnAddress(指向了一条字节码指令的地址)类型。这些数据类型在局部变量表的存储空间以局部变量槽来表示,其中64位的long和double类型占用两个局部变量槽,其余的占用一个。局部变量表所需的内存空间在编译期间完成分配。进入一个方法时,这个方法需要在栈帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量的大小,也就是局部变量槽的数量。
       操作数栈:操作数栈是一个后进先出的栈,方法的执行都是在操作数栈中完成的,每一个字节码指令往操作数栈写入和提取的过程,就是入栈和出栈的过程。
       动态连接:
        部分符号引用在运行期间转为为直接引用叫做动态链接。
      虚/非虚方法
        在编译期间将符号引用转化为直接引用的方法称为非虚方法。
        在运行期间将符号引用转化为直接引用的方法叫做虚方法。
      方法出口:当一个方法开始执行后,只有两种方式可以退出这个方法。第一种方式是执行引擎遇到任意一个方法返回的字节码指令(例如:return),这时候可能会有返回值传递给上层的方法调用者(调用当前方法的方法称为调用者),是否有返回值和返回值的类型将根据遇到何种方法返回指令来决定,这种退出方法的方式称为正常完成出口(Normal Method Invocation Completion)。
    另外一种退出方式是,在方法执行过程中遇到了异常,并且这个异常没有在方法体内得到处理,无论是Java虚拟机内部产生的异常,还是代码中使用 throw 字节码指令产生的异常,只要在本方法的异常处理器表中没有搜索到匹配的异常处理器,就会导致方法退出,这种退出方法的方式称为异常完成出口(Abrupt Method Invocation Completion)。一个方法使用异常完成出口的方式退出,是不会给它的上层调用者产生任何返回值的。无论采用何种退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。一般来说,方法正常退出时,调用者的程序计数器的值可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器表来确定的,栈帧中一般不会保存这部分信息。方法退出的过程实际上就等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值(如果有的话)压入调用者栈帧的操作数栈中,调整程序计数器的值以指向方法调用指令后面的一条指令等。
  • 本地方法栈:线程私有,执行本地native方法。
  • 堆:线程共享,用来存放对象的实例。每个线程都有自己的一个TLAB(Thread Local Allocation Buffer)线程本地分配缓冲区,每次分配内存都是在TLAB上面分配的,用来提升对象分配时的效率。
  • 方法区:线程共享。用来存储已被虚拟机加载的类信息、运行时常量池、静态变量以及即时编译器编译后的代码缓存等数据。
      运行时常量池:方法区的一部分。Class文件中,除了版本、字段、方法、接口等描述符外,还有常量池表,用来存放编译期生成的各种字面量和符号引用。
    直接内存:该内存不受虚拟机大小限制,而是受本机物理内存限制,可以通过-XX:MaxDirectMemorySize参数指定,默认与java堆最大值一致。