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

一、运行时数据区域

运行时数据区
方法区 虚拟机栈(线程私有 本地方法栈
程序计数器(线程私有
  1. 程序计数器(Program Counter Register)

    1. 线程私有,唯一不会产生OOM的地方。
  2. Java虚拟机栈(Java Virtual Machine Stack)

    1. 它是线程私有的;
    2. 它主要描述的是:
      java方法执行的线程内存模型,每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame,它是方法运行期间重要的基础数据结构)用于存储局部变量表、操作栈、动态链接、方法出口等信息。
      每个方法被调用直至完毕的过程,就对应一个栈帧在虚拟机栈中从入栈到出栈的过程。
    3. 两类异常状况:
      如果线程请求的栈深度大于虚拟机允许的深度,将会抛出StackOverflow异常。
      如果Java虚拟机的栈可以动态扩展,当栈扩展时无法申请到足够的内存,将会抛出OOM异常。
      需要注意Hotspot虚拟机的栈容量不可以动态扩展,Classic虚拟机可以。
  3. 本地方法栈(Native Method Stacks)

    1. 线程私有;
    2. 本地方法栈 与 java虚拟机栈的作用相似,区别是,java虚拟机栈为java方法服务,本地方法栈为虚拟机执行本地(Native)方法服务。
    3. 本地方法栈 与 java虚拟机栈类似,在栈深度溢出或者栈扩展失败时分别抛出StackOverflow和OOM异常。
    4. Hotspot虚拟机将 虚拟机栈 和 本地方法栈 合二为一。
  4. java堆(java heap)

    1. 线程共享。
    2. java堆基本上是java虚拟机所管理的内存中的最大一块,它在虚拟机启动时创建。
    3. 堆的目的是存放对象实例,java中几乎所有的对象实例都在 堆 里面分配内存。
    4. java堆是垃圾收集器管理的内存区域。
    • 将java堆 进行细分是为了更好的进行内存回收、分配,其存储的还都是java实例对象。
    • Java堆既可以被实现成固定大小的,也可以是可扩展的。当前主流的java虚拟机都是按照可扩展来实现的(通过-Xmx和-Xms设定)。如果java堆中没有内存完成java实例分配,并且堆也无法扩展时,Java虚拟机将会抛出OOM异常。
  5. 方法区(Method Area)

    1. 线程共享
    2. 用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
    3. 方法区有一个别名——非堆(Non-heap),目的是与java堆 区分开来。
  6. 运行时常量池(Runtime Constant Pool)

    • 运行时常量池 是 方法区的一部分,受到方法区内存的限制,当常量池无法申请到内存的时候,将会抛出OOM异常。
  7. 直接内存(Direct Memory)

    • 直接内存不是java虚拟机运行时数据区的一部分。但是这部分内存也被频繁使用,也可能导致OOM异常。主要时NIO场景下的堆外内存。
    • 配置-Xmx等参数的时候,需要注意直接内存的占用,各个区域的总内存之和不能大于物理机的内存限制;防止OOM的异常。

二、HotSpot虚拟机对象探秘

  1. 对象的创建
  2. 对象的内存布局
    对象在堆内存的存储布局可以划分为三个部分:

    1. 对象头 (Header)
    2. 实例数据 (Instance Data)
    3. 对齐补充 (Padding)
  3. 对象的定位

2.垃圾收集器与内存分配策略

垃圾收集(Garage Collection,简称GC);需要完成的三件事:

  • 那些内存需要回收?
  • 什么时候回收?
  • 如何回收?

java虚拟机中程序计数器、虚拟机栈、本地方法栈这三个区域随线程而生,随线程而灭。
垃圾回收器主要针对java方法区中的内存分配、回收的情况进行处理。

3.虚拟机性能监控、故障处理工具

4.调优案例分析与实战