会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