java内存区域与内存溢出异常

运行时数据区

程序计数器/PC寄存器

  • 当前线程所执行的字节码的行号指示器
  • 每条线程都是独立的,独立存储,线程私有内存
  • 当线程执行一个java方法时,记录正在执行的虚拟机字节码指令地址. 若为native方法,则为空
  • 唯一一个java虚拟机中没有规定OOM情况的区域

虚拟机栈/方法栈

  • 每个方法在执行时都会创建一个栈帧用于存放局部变量表,操作数栈,动态链接,方法出口等信息.每一个方法从调用到完成对应这一个栈帧在虚拟机中的入栈到出栈

    • 局部变量表:存放编译期可知的基本类型,对象的引用(引用指针,对象句柄或其它与对象相关的位置信息 非对象本身),returnAddress类型(指向了一条字节码指令的地址)

64位(long,double)会占用2个局部变量空间(slot),其余占用一个

在方法运行时不会改变大小

  • 线程私有的,生命周期与线程保持一致
  • 常说的栈内存指的就是虚拟机栈
  • 异常信息:
    StackOverFlowError:线程请求的栈深度大于虚拟机允许的栈深度
    OutOfMemoryError:虚拟机可动态扩展,但扩展时无法申请到足够内存

本地方法栈

  • 与虚拟机栈基本一致
    区别:
    仅保存Native方法信息

java堆/GC堆

  • 存放对象实例

  • 被所有线程共享,虚拟机启动时创建

    • 共享总大小内存,对于每一个线程可能划分出私有的分配缓冲区
  • 垃圾管理器主要区域,所以也被称为GC堆

    • 因此分出年轻代(eden,survivor),老年代(old) 1:1:8
  • -Xms -Xmx

  • OOM

方法区(Non-Heap)

  • 用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码

  • 所有线程共享

  • 在HotSpot也被称为永久代

    • 在JDK1.7版本的HotSpot中已将放在永久代的字符串常量池移出
  • 方法区可以不实现垃圾收集

  • OOM

运行时常量池

  • 方法区 的一部分

  • 用于存放编译期生成的字面量,符号引用,直接引用,当类加载完毕后加入

  • 具备动态性,运行期间也可将新的常量放入池中, String.intern()

    • String.intern()

      • jDK1.6 会将实例复制到永久代,并返回这个复制的引用
      • JDK1.7 不在复制实例,仅在常量池中记录首次出现的实例引用
      • String str = “java”;
        String str2 = new String(“java”);
        boolean flag = str == str2 ;
        flag 是false
        new String() 创建一个对象并在堆上,str2保存堆对象地址
        “java“str保存的是常量池中的java地址;
  • OOM

直接内存

  • 不属于运行时数据区,也不是java虚拟机规范中定义的内存区域
  • OOM

对象

对象的创建

  • 1.类加载检查. 先查询参数是否在常量池中有一个类的符号引用, 检查这个符号引用代表的类是否已经被加载,解析 初始化过,如果没有 ,则需要进行相应的类加载过程

    1. 虚拟机分配内存,并初始化为0(不包含对象头)
    • 2.1 指针碰撞,要求java堆中内存绝对规整

    • 2.2 空闲列表

    • 2.3 线程安全问题

      • 2.3.1 采用CAS和失败重试机制保证更新的原子性
      • 2.3.2 本地线程分配缓冲(TLAB)
  • 3.虚拟机对对象进行必要设置,实例信息,元数据信息,对象hash码,GC分代信息等,这些信息在对象头中

  • 执行init方法 代码:P45

对象的内存布局

  • 对象头

    • 运行时数据 Mark Word

      • HashCode,GC分代年龄,锁状态标示,线程持有的锁,偏向线程ID,偏向时间戳
      • 32位:对象处于未锁定状态 25bit HashCode 4bit 分代年龄 2bit 锁标志位 1bit 固定0
    • 类型指针

      • 对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象时那个类的实例
  • 实例数据

    • 存储对象的有效信息,定义的字段内容,受到java编写顺序和虚拟机分配策略参数影响
  • 对齐填充

    • 非必然存在,仅仅是占位符的作用

对象的访问定位

  • 通过栈中的reference数据来操作堆中的具体对象

    • 直接指针 reference 中直接保存对象地址
      SUN HotSpot 采用直接指针

      • 实例数据
      • 类型数据指针
    • 使用句柄 reference中保存句柄地址

      • 在堆中维护一个句柄池

        • 实例数据指针
        • 类型数据指针

OOM

参数设置: -verbose:gc -Xms20M -Xmx20m -Xmn10m -XX:+PrintGCDetails -XX:SurvivorRatio=8

  • java堆溢出

    • java heap space 会有dump文件 ,通过分析dump文件确定是溢出还是泄漏

      • 泄漏: 查看GC roots 的引用链定位泄漏代码
      • 溢出:调整物理机内存大小,查看对象的生命周期是否过长,持有状态过长, 减少程序运行期间的内存消耗
  • 虚拟机栈和本地方法栈溢出

    • 单线程

      • 仅会抛出StackOverflowError
    • 大小: 操作系统分配进程内存2G(32位 win) – Xmx(最大堆容量) – MaxPermSize(方法区) 剩余部分可大致认为是其大小
      程序计数器忽略,忽略消耗

      • 所以当无法减少运行的线程数和无法换成64位机器时,可以考虑减少堆内存和方法区的内存大小来达到提升栈内存
  • 方法区和运行时常量池溢出

    • 动态生成大量的class时有可能发生,常见的有:动态生成大量JSP文件,基于OSGi的应用
  • 本机直接内存溢出

    • 通过-XX: MaxDirectMemorySize指定,默认与堆最大值一致(-Xmx)
    • Heap Dump 文件中不会看见明显的异常,若OOM后Dump文件很小,程序中直接或间接使用了NIO则可能出现

高清思维导图版请关注公众号‘伊人网络’ 回复 ‘内存’ 即可领取

版权声明:本文为diandiandian原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/diandiandian/p/13192369.html