Java虚拟机之内存区域

会Java编程,当然要了解Java虚拟机,学习并笔记之。

内存区域

Java虚拟机管理的内存包括以下几个运行时数据区域:

程序计数器

  • 可以看作字节码行号指示器;
  • 执行Java方法时,记录的是字节码制冷地址;执行native方法时,计数器值为空(Undefined);
  • 没有规定OutOfMemoryError情况;
  • 属于线程私有;

Java虚拟机栈

  • 方法执行会创建栈帧,存储局部变量表、操作数栈、动态链接以及方法出口等信息;

  • 局部变量表编译期确定基本类型、引用类型和returnAddress类型;

  • long和double占用两个Slot,其余数据类型占用1个,内存空间大小也是编译期确定;
  • 存在两种异常:StackOverflowError和OutOfMemoryError;

本地方法栈

  • 于虚拟机栈类似,区别在于为Native方法服务;
  • 用于实现的语言、方法和数据结构没有强制规定;

Java堆

  • 线程共享;
  • 虚拟机规范:所有对象实例以及数组都在堆上分配;(随着技术发展不绝对)
  • 分带收集算法:新生代老年代;
  • 线程私有分配缓冲区TLAB;
  • 不要求物理连续;
  • OutOfmemoryError;

方法区

  • 线程共享;
  • 存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据;
  • HotSpot用永久代实现方法区;
  • 1.7以后HotSpot把原本放在永久代的字符串常量池移出;
  • 此区域回收目标主要是针对常量池回收和类型卸载;
  • OutOfmemoryError;

运行时常量池

  • 方法区一部分;
  • Class文件的常量池在类加载后进入方法区的运行时常量池存放;
  • 一般来说,运行时常量池除了保存Class文件中描述的符号引用外,还会把翻译出来的直接引用也存储在其中;
  • 具备动态性,在运行期间也可以将新的常量放入池中,见String的intern()方法;
  • OutOfmemoryError;

直接内存

  • 非运行时数据区的一部分;
  • 非Java虚拟机规范定义的内存区域;
  • 不受Java堆大小的限制;
  • 忽略直接内存可能导致动态扩展时出现OutOfmemoryError;

对象探秘

对象的创建

  • 分配内存的时候,有两种方式,一种是“指针碰撞”,一种是”空闲列表”。前者在规整的内存上挪动指针,后者针对相互交错的内存维护一个列表,记录那些内存块可用;
  • 对象创建的线程安全问题,一般有两种解决方案,一种是对分配内存的动作同步处理(CAS配上失败重试),一种是利用TLAB;

对象的内存布局

分为三块区域:对象头、实例数据和对齐填充。

  • 对象头:一部分用于存储对象自身的运行时数据,一部分是用于确定对象属于哪个类实例的类型指针(不都是必须的);
  • 实例数据:真正的有效信息,存储各类型的字段内容;
  • 对齐填充:非必需,没有特定含义,起占位符作用;

对象的访问定位

主流的访问对象的方式有使用句柄和直接指针两种。

  • 使用句柄:内存划分出一块作为句柄池,引用存储的是对象的句柄地址,而句柄包含了实例数据与类型数据各自的具体地址信息;
  • 直接指针:引用直接存储对象地址,Sun HotSpot采用这一种;

小结

本篇涉及一些概念,可以通过线程私有和线程共享来划分理解。

参考

What’s the difference between StackOverflowError and OutOfMemoryError