Java内存区域与内存溢出异常小结

来源:互联网 发布:阿里云机顶盒破解 编辑:程序博客网 时间:2024/06/03 21:34

运行时数据区域

程序计数器

  • 当前字节码的行号指示器
  • 线程私有

栈区

  • 线程私有

Java虚拟机栈

  • 为虚拟机执行Java方法(即字节码)服务
  • 存储局部变量表、操作数栈、动态链接、方法出口等
  • 局部变量表:编译期间完成分配
    存储基本数据类型、对象引用

本地方法栈

  • 为执行Native方法服务
  • HotSpot将本地方法栈和虚拟机栈合一

Java堆

  • 线程共享
  • 目的:存放对象实例
  • 垃圾回收器管理的主要区域
  • 通过-Xmx和-Xms动态扩展

方法区

  • 虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等
  • 内存回收目标主要是针对常量池的回收和对类型的卸载(类型卸载较负载但必要)
  • 运行时常量
    • 存放编译期生成的各种字面量和符号引用
    • 具有动态性:运行期间可以放入新的变量

直接内存

  • NIO类,可使用Native函数库直接分配堆外内存,通过存储在堆中的DirectByteBuffer对象作为这块内存的引用进行操作

HotSpot虚拟机对象探秘

对象的创建

  • 创建新对象(new 指令),先检查指令的参数能否在常量池中定位到类的符号引用,并检查是否被加载。如果没有,需进行类加载过程
  • 内存分配:
    • 为新生对象分配内存:
      • 内存规整:指针碰撞
      • 内存不规整:空闲列表
    • 并发情况下的线程安全:
      • 对分配内存空间的动作进行同步处理,保证原子性
      • 每个线程在Java堆中预先分配一小块内存,即本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)。在每个线程自己的TLAB上分配内存,只有分配新的TLAB时,才需要同步锁定
  • 对象的初始化和设置:
    • 将分配的内存空间初始化为零值(使用TLAB可以在分配TLAB时初始化)
    • 设置对象头
  • <init>方法,将对象按照程序员的意愿初始化

对象的内存布局

  • 对象头Header:存储对象自身的运行时数据
    • Mark Word:HashCode、GC分代年龄、锁状态标志、线程只有的锁、偏向线程ID、偏向时间戳等
    • 类型指针:指向类元数据的指针,用于确定对象是哪个类的实例(非必要)
    • 对齐填充(非必要):占位符,因为对象必须是8字节的整数倍

对象的访问定位

需要通过栈上的reference数据操作堆上的具体对象

  • 句柄访问:
    • 在堆中划分一块内存作为句柄池,reference中存储对象的句柄地址,句柄中包含对象实例数据类型数据各自的具体地址信息
    • 优点:稳定的句柄,在对象移动(如gc操作)时,不需要改变reference
  • 直接指针访问:
    • 堆对象的布局中需要考虑如何放置类型数据,reference中存储的是对象实例数据地址
    • 优点:速度快,节省了指针定位的时间开销(HotSpot)