第2章-Java内存区域与内存溢出

本章将从概念上介绍Java虚拟机内存的各个区域,讲解这些区域的作用、服务对象以及其中可能产生的问题,这是翻越虚拟机内存管理这堵围墙的第一步。

运行时数据区域

Alt text

程序计数器

是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。
作用:分支、循环、跳转、异常处理、线程恢复等基础功能需要依赖这个计数器来完成。

Java虚拟机栈

是线程私有的,与线程生命周期相同。描述的是Java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧。
作用:用来存储局部变量表、操作数栈、动态链接、方法出口等
使用场景:每一个方法从调用直到执行完成的过程,就对应着一个栈帧在虚拟机入栈出栈的过程。

本地方法栈

与虚拟机栈类似,为Native方法服务。
常见异常:StackOverflowError、OutOfMemoryError异常

Java堆

是垃圾收集器管理的主要区域,因此很多时候也被称为“GC堆”。
特点:

  • Java虚拟机所管理的内存中最大的一块
  • 被所有线程共享的一块内存区域
  • 在虚拟机启动时创建

作用:存放对象实例

分类:

  • 粗分:新生代和老年代
  • 细分:Eden空间、From Survivor空间、To Survivor空间
    扩展:-Xmx和-Xms
    常见异常:OutOfMemoryError异常

方法区

与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的。
作用:存储类信息、常量、静态变量、即时编译器编译后的代码等数据。
常见异常:OutOfMemoryError异常

运行时常量(归属方法区)

是方法区的一部分。
作用:用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。
特征:具备动态性,如:String类的intern()方法
常见异常:OutOfMemoryError异常。

直接内存(堆外内存)

不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。
使用场景:NIO(基于通道与缓冲区的IO方式)->分配堆外内存(通过Native函数库)->通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。
常见异常:OutOfMemoryError异常。

HotSpot虚拟机对象探秘

对象的创建

虚拟机遇到new指令->检查加载类过程->为新对象分配内存->将分配到的内存空间都初始化为零值->对对象进行必要的设置。
内存分配方式:

  • 指针碰撞(Java堆内存绝对规整的)
  • 空闲列表(Java堆内存不规整)
  • 依赖关系
    • 内存分配方式->Java堆是否规整->采用的收集器是否带有压缩整理功能。

存在问题:

  • 虚拟机对象创建是非常频繁的?
  • 并发情况下无法保证线程安全?

解决方案:

  • 对分配内存空间的动作进行同步处理(采用CAS配置失败重试的方式保证更新操作的原子性)
  • 把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(TLAB)。配置参数:-XX:+/-UserTLAB

对象的内存布局

  1. 对象头
    存储对象自身的运行时数据(HashCode、GC分代年龄、锁状态标志等。
    Alt text

  2. 实例数据

  • 存储顺序受到虚拟机分配策略参数和字段在Java源码中定义顺序的影响。
  • 默认分配策略:longs/doubles->ints->shorts/chars->bytes/booleans->oops
  1. 对齐填充
    由于HotSpotVM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,因此当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。

对象的访问定位

Java程序需要通过栈上的reference数据来操作堆上的具体对象。

使用句柄

Alt text

直接指针

Alt text

实战:OutOfMemoryError异常

Java堆溢出

配置:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
解决区域异常步骤:

  • 通过内存映像分析工具(分析是内存泄漏||内存溢出)
  • 内存泄漏:通过工具进一步找出泄漏对象到GCRoots的引用链
  • 内存溢出:
    • 检查虚拟机堆参数(-Xmx和-Xms)
    • 代码检查是否存在某些对象生命周期过长、持有状态时间过长的情况,尝试减少程序运行期的内存消耗

虚拟机栈和本地方法栈溢出

配置参数:-Xss(-Xoss参数无效)

方法区和运行时常量池溢出

配置参数:-XX:PermSize=10M -XX:MaxPermSize=10M
异常场景:

  • 在经常动态生成大量Class的应用中,需要特别注意类的回收状况
  • CGLib字节码增强和动态语言
  • 大量JSP或动态产生JSP文件的应用
  • 基于OSGI的应用

本机直接内存溢出

配置参数:-XX:MaxDirectMemorySize(不配置默认与Java堆最大值一样)
特征:在Head Dump文件中不会看见明显的异常