JVM(一):Java内存区域与内存溢出异常

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

不同于C和C++语言的开发工程师,在内存管理方面,C和C++开发人员对于内存有着绝对的所有权。

但是对于Java语言开发者来讲,在虚拟机自动内存管理的机制下,并不需要太过于担心内存泄露或者内存溢出方面的问题,但是正是因为我们不去关心这方面的问题,所以一旦虚拟机发生了内存泄漏或者内存溢出,那么我们如何去排查问题,就成了Java语言开发者最为头疼的事情之一。

如果我们想要了解JVM的内存分配及管理,我们就首先就需要去了解JVM到底是如何帮助我们管理内存,以及虚拟机到底在帮我们管理什么。

运行时数据区域

JVM在执行Java程序的时候,会把它所管理的内存划分为几个区域进行分别管理,这些区域都有各自的用途,创建时间以及销毁时间等等,这些区域我们统一称为运行时数据区域,这些区域大致分为5部分。

  1. 程序计数器
  2. Java虚拟机栈
  3. 本地方法栈
  4. Java堆
  5. 方法区

那么这些区域都有着自己对应的作用以及生命周期,我们接下来就逐一分析每个区域的作用,生命周期,以及相互之间的共同点和不同点。

  • 程序计数器

程序计数器是JVM所管理的内存区域中最小的一部分,这一部分的作用就是为了通过改变自身存储的值,来提示JVM下一步应该做什么事情,包括跳转,循环,分支,异常处理等等等等。

但是我们知道,在Java中,方法可以是Java的方法,也可以是Native的方法,对于程序计数器来说,如果下一步要执行的是Java的方法,那么它存储的就是对应字节码的地址,那么如果是Native的方法呢,存储的就是undefined。

这部分内存,是唯一一个虚拟机规范没有规定任何OOM的内存。

  • Java虚拟机栈

对于有一定Java基础的人应该知道,在虚拟机里,栈里面是一个一个的栈帧,一个栈帧从压栈到出栈的过程,其实就是Java方法执行的内存模型,栈帧中存放的是局部变量表,操作数栈,动态链接,方法出口等信息。

局部变量表中存放了所有在编译期间可知的基本数据类型,对象引用和returnAddress类型。

根据虚拟机规范,如果线程请求的栈深度大于虚拟机允许的最大深度,则会出现SOF,如果虚拟机支持动态扩展,但是在扩展的时候申请不到足够的内存,则会抛出OOM。

  • 本地方法栈

本地方法栈的作用基本上和Java虚拟机栈一样,抛出的异常也一样,不同点在于本地方法栈执行的Native方法。

  • Java堆

堆是虚拟机所管理的最大的一块内存,几乎上Java程序中创建出来的类实例都存放在Java堆中(当然也有例外,现在暂时不考虑这些)。

Java堆也是GC主要管理的区域,根据GC分代收集算法,Java堆还可以细分为新生代和老年代,更细的可以分为Eden空间,From survivor空间,To survivor空间,我们之后会更加详细的去讨论分代收集算法相关的一些东西,在此暂且略过,当然无论怎么分,都和储存的内容是无关的。

根据虚拟机规定,如果堆上没有内存能满足实例分配,则会抛出OOM。

  • 方法区

方法区和堆有些类似,所存储的东西都是类相关的,只不过堆存储的是类实例,而方法区存储的是所有属于类的静态的信息,比如静态方法,静态变量,常量,静态代码块等等。

根据虚拟机规范,方法区是属于堆的一个逻辑部分,但是方法区有一个别名叫Non-Heap,也就是非堆,目的就是为了和堆区分开。

对于方法区的回收来讲,GC的成绩非常不令人满意,GC针对方法区的回收主要是常量池的回收的类型卸载,尤其是类型的卸载,条件非常苛刻,但是这部分内存的回收也是必不可少的。

方法区还有一个很重要的组成部分就是运行时常量池,常量池用于存放编译期间生成的各种字面量和符号引用。

根据虚拟机规范,当方法区无法满足内存分配的需求时会抛出OOM。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值