说一说 JVM 内存区域划分,哪些区域可能发生 OOM

您所在的位置:网站首页 内存分为哪些 说一说 JVM 内存区域划分,哪些区域可能发生 OOM

说一说 JVM 内存区域划分,哪些区域可能发生 OOM

2024-07-10 12:21| 来源: 网络整理| 查看: 265

640?wx_fmt=jpeg

通常情况下,我们可以把 JVM 的内存区域划分为以下几个部分,其中,有的区域是以线程作为单位,而有的区域则是整个 JVM 进程唯一的:

1.程序计数器

在 JVM 规范中,每个线程都有自己的程序计数器,并且任何时间一个线程只有一个方法在执行,也就是所谓的当前方法。程序计数器会存储当前线程正在执行的 java 方法的 JVM 指令的地址;但是,如果正在执行的是本地方法,则未指定值。

2.Java 虚拟机栈

虚拟机栈,早期也被称之为 Java 栈。每个线程在被创建时,都会创建一个虚拟机栈,其内部保存了一个个栈帧,对应着一次次 Java 方法的调用。

在上面的计数器中,我们提到了任何一个时间点,一个线程只能执行一个方法,也就是当前方法,类似的,任何一个时间点,一个线程只会有一个活动栈帧,通常叫当前帧,方法所在的类为当前类。如果说该方法中又调用了其他方法,对应的会创建新的栈帧,这个栈帧成为了新的当前帧,一直到它返回结果或者执行结束为止。

JVM 直接对 Java 栈的操作只有两个,即对栈帧的压栈和出栈。

栈帧中存储着局部变量表,操作数栈,动态链接,方法正常退出或者异常退出的定义等。

3.堆

堆是 Java 内存管理的核心区域,用来放置 Java 实例对象。堆被所有的线程共享,在启动虚拟机时,我们可以通过指定 -Xmx 来指定最大的堆空间等指标。

堆也是垃圾回收器重点照顾的对象,所以堆内空间还会被不同的垃圾回收器进一步细分,其中最有名的就是新生代,老年代的划分。

4.方法区

方法区也是被所有线程共享的一块内存区域,用于存储元数据,例如类的结构信息,以及对应的运行时常量池,字段,方法代码等。

在早期的 JVM 实现中,很多人习惯将方法区称为永久代,但是在 JDK 8 中已经将其删除,同时新增了元数据区。

5.运行时常量池

它是方法区的一部分。如果你有分析过反编译的类文件结构,你能看到版本号,字段,方法,父类,接口等各种信息,还有一项信息就是常量池。Java 的常量池可以存放各种常量信息。

6.本地方法栈

它和虚拟机栈很类似,支持对本地方法的调用,同样也是每个线程创建一个。

下面上一张 JVM 内存区域划分的图:

640?wx_fmt=png 哪些区域可能发生 OOM(Out of memory)?

内存溢出通俗的讲就是内存不够用了,并且 GC 通过垃圾回收也无法提供更多的内存。实际上除了程序计数器,其他区域都有可能发生 OOM, 简单总结如下:

堆内存不足是最常见的 OOM 原因之一,抛出错误信息 java.lang.OutOfMemoryError:Java heap space,原因也不尽相同,可能是内存泄漏,也有可能是堆的大小设置不合理。

对于虚拟机栈和本地方法栈,导致 OOM 一般为对方法自身不断的递归调用,且没有结束点,导致不断的压栈操作。类似这种情况,JVM 实际会抛出 StackOverFlowError , 但是如果 JVM 试图去拓展栈空间的时候,就会抛出 OOM.

对于老版的 JDK, 因为永久代大小是有限的,并且 JVM 对老年代的内存回收非常不积极,所以当我们添加新的对象,老年代发生 OOM 的情况也非常常见。

随着元数据区的引入,方法区内存已经不再那么窘迫,所以相应的 OOM 有所改观,出现 OOM,异常信息则变成了:“java.lang.OutOfMemoryError: Metaspace”。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3