内存结构

1.7和1.8的区别

程序计数器:线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。唯一一个不会OOM的

如果线程正在执行一个Java方法, 这个计数器记录的是正在执行的虚拟机字节码指令的地址;

如果线程正在执行一个本地方法,这个计数器的值应为空

虚拟机栈:线程私有,每个方法被执行时,Java虚拟机都会同步创建一个栈帧用于存储局部变量表,操作数栈,动态连接,方法出口等信息。

每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

  • 局部变量表:存放了编译器可知的各种Java虚拟机基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它并不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或者其它与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址),当 Java 源代码文件被编译成 class 文件的时候,局部变量表的最大容量就已经确定了
  • 动态链接:指向运行时常量池中该栈帧所属方法的引用
  • 操作数栈:临时存放操作数以及计算结果,最大深度在编译的时候就确定了
  • 方法返回地址:方法执行完毕后返回的地址

本地方法栈:线程私有,和虚拟机栈作用类似,本地方法栈内部执行的是本地方法,在hotspot里和虚拟机栈合二为一了

Java堆:线程共享,几乎所有的对象都在堆上分配(逃逸分析可能导致对象在栈上分配)

线程分配缓冲区:线程私有,提升对象分配时的效率

方法区:线程共享,存储已经被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

方法区一直是个概念上的区域,在不同jdk版本有不同实现

jdk6及6之前,方法区是使用永久代实现的,发现放到永久代不太好,决定开始放到本地内存

jdk7把原本放在永久代的字符串常量池、静态变量等移出,此时运行时常量池还是在永久代中

jdk8彻底废弃永久代的概念,将永久代的剩余内容(主要是类型信息)全部移到元空间,使用元空间实现方法区

永久代时,默认大小64MB;元空间时,大小不受JVM限制

运行时常量池:当类被加载时,类中的字面量与符号引用被放入运行时常量池,由符号引用翻译出来的直接引用也存储在运行时常量池

直接内存:并不是Java虚拟机规范中定义的内存区域