Java内存区域划分以及内存溢出异常

前言

Java虚拟机的内存区域主要分为:程序计数器,虚拟机栈,本地方法栈,Java堆,方法区🖊

其中,描述方法执行,及方法的局部变量和引用描述对象方法区描述和常量

其中,栈结构线程私有,堆结构线程共享(方法区描述类,也是堆结构)

Java虚拟机运行时数据区域

Java虚拟机运行时数据区

上图理解:

结构线程共享,描述所有的对象(类在方法区中,方法区也是堆);

和计数器线程私有,描述方法执行,和存放引用

Java虚拟机在运行时会把它所管理的内存划分为若干个不同的数据区域。大致分为以下:

  1. 程序计数器:在Java中,每个线程都有一个独立的程序计数器,独立存储,该段内存是线程私有的

  2. Java虚拟机栈:描述方法执行的内存模型,以及存放局部变量,对象引用

    • 每个方法在执行的同时会创建栈帧用于存储局部变量表、动态链接等信息。

    • 其中,局部变量表存放基本类型、对象引用、returnAddress类型。其内存空间大小分配在编译期间就可以完全确定,之后也不会改变。

    • 线程私有的,生命周期与线程相同。
    • 当线程请求的栈深度大于虚拟机允许的深度,抛出StackOverFlowError异常
    • 可动态扩展,可能会产生OutOfMemoryError异常(OOM)
  3. 本地方法栈:描述Native方法的内存模型,其他与虚拟机栈一样。

  4. Java堆存放对象实例,几乎所有的对象实例都在这里分配内存。

    • 所有线程共享这一块内存区域,是Java虚拟机所管理的最大一块内存
    • 堆是GC垃圾收集器管理的主要区域(在普遍使用的分代手机算法中,堆可分为:新生代和老年代)
    • 对线程来说,还划分出多个线程私有的本地线程分配缓冲区(TLAB)
    • 以上无论如何划分,堆存放的都是对象实例,划分只是为了更好地回收和分配内存
    • 可动态扩展,可能会产生OutOfMemoryError异常(OOM)
  5. 方法区存储Class文件的相关信息:已被加载的类信息常量、(类变量)静态变量、编译后的代码等

    • 所有线程共享
    • 运行时常量池:Class文件的常量池的信息,在类加载后进入方法区的运行时常量池
      • Java不要求常量一定要编译时产生,可以运行期间产生放入方法区常量池,例如String.intern()
      • (此外Class文件中还有类版本、字段、方法、接口等描述信息)
    • 这部分的GC较少,在此区域GC主要为了针对常量池回收和类型卸载。
    • 其实它也是堆结构,但是要与Java堆区分,称为“Non-Heap”非堆
    • 可动态拓展,可能会产生OutOfMemoryError异常(OOM)
  6. 直接内存

    • 可动态拓展,可能会产生OutOfMemoryError异常(OOM)

OutOfMemoryError异常(内存溢出)

申请的内存空间超过了系统实际分配的空间(系统无法满足内存申请) ,就会发生OutOfMemory异常(以下简称OOM)

除了程序计数器外,其他几个运行时区域都有可能发生OOM异常

Java堆溢出

堆用于存储对象实例,只要不断创建对象,并且保证GC Roots到对象之间有可达路径以避免对象被垃圾回收,那么对象数量在到达堆最大容量限制是抛出内存溢出OOM异常。

分清楚是内存泄漏还是内存溢出

内存泄漏:申请内存后,无法释放已申请的内存空间 。

比如,你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。

内存溢出:申请内存时,没有足够的内存供申请者使用。

比如:给了你一块存储int类型数据的存储空间,但是你却存储long类型的数据,内存不够用,就会报OOM,此时内存溢出。

又比如:一个盘子用尽各种方法只能装4个果子,你装了5个,结果掉倒地上不能吃了。

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

(HotSpot虚拟机不区分这两个栈)

StackOverFlowError:线程请求的栈深度大于虚拟机允许的深度

OutOfMemoryError:扩展栈时无法申请到足够的内存

单线程测试下,无论是栈容量少,还是栈帧太大,都抛出了StackOverFlow异常

而在多线程下,通过不断建立内存用量大的线程,迅速耗尽内存空间,会抛出OOM

方法区溢出

通过不断产生动态类(如反射动态代理JSP会加载Class的操作),在运行时产生大量的类去填满方法区,直到溢出,抛出OOM。

本文标题:Java内存区域划分以及内存溢出异常

文章作者:Aaron.H

发布时间:2018年07月21日 - 19:07

最后更新:2018年09月07日 - 08:09

原始链接:https://uncleaaron.github.io/Blog/Java/Java内存区域划分以及内存溢出异常/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。