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

Wesley13
• 阅读 567

Java的内存管理是一个老生常谈的问题,虽然Java号称可以自动管理自己的内存,使程序员从内存管理的围墙解放出来,但是一连串的内存泄漏和溢出方面的问题,使得我们不得不去深入了解Java的内存管理机制。本篇文章将从Java的内存区域开始剖析Jvm的内存机制,阐述内存溢出异常产生的原因。

运行时数据区域

众说周知,Java程序是运行在Java虚拟机中的,虚拟机顾名思义,就是一个虚拟的计算机。所以Java虚拟机也拥有一些与真实计算机相近的概念,比如栈,堆,程序计数器等,通常我们在这些概念面前加上虚拟机,以表明特指Java虚拟机的栈。

Java程序运行时,Java虚拟机会对内存进行管理,划分为若干个不同的数据区域,每个数据区域都有其不同的功能。根据《Java虚拟机规范(Java SE 7版)》的规定,Java虚拟机所管理的区域会包括以下几个运行时区域,如下图所示。

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

下面我们一一介绍每个区域的不同功能。

程序计数器

程序计数器,即PC。学过计算机组成原理的同学一定对这个概念不陌生,在计算机组成原理中PC指的是PC寄存器,用来存放计算机执行的指令的所在内存区域的地址。而在Java虚拟机中,PC也有类似的作用,它的作用是存储当前线程所执行的字节码的行号指示器,通过改变这个计数器的值来选取下一条需要执行的字节码指令。与计算机PC不同的是,在Java虚拟机中,PC只是一块较小的内存空间,而不是寄存器。

由于Java虚拟机是多线程的,为了在线程之间进行隔离,每一个线程都会拥有一个独立的程序计数器。因此,在进行线程调度的时候,每个线程的执行互不影响。我们称这类内存区域为“线程私有”的内存。

程序计数器记录的只是正在执行的虚拟机字节码指令的地址,如果执行的是Native方法,那么计数器的值则为空。该内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

Java虚拟机栈

Java虚拟机栈与系统栈也有些类似,都用来存储程序运行过程中创建的栈帧,不过Java虚拟机栈存储的是方法的栈帧而已,它与程序计数器一样,都是线程私有的。

在Java方法执行时创建的栈帧是用来存储局部变量表、操作数栈、动态链接,方法出口等信息,我们平常所说的方法的入栈和出栈就是一个方法从执行到结束的过程。虚拟机栈的特性与一般的栈一样,同样是后进先出,递归调用的原理就是基于此。

一般来说,对于Java虚拟机栈,我们主要关心的部分是它的局部变量表的存储。在我们定义一个变量的时候,变量到底被存放在哪里是我们经常遇到的问题。对于基本数据类型,如boolean、byte等以及对象的引用(reference类型,一个指向对象的指针或者是一个句柄,不是对象本身)和returnAddress类型(指向了一条字节码指令的地址)。

局部变量表的大小是在编译期就已经完全确定下来的,在方法运行期间不会改变局部变量表的大小。同时,对于64位长度的long和double类型的数据会占用两个局部变量空间,其余的只占用一个。

在Java虚拟机规范中,对这个区域规定了两种异常情况:StackOverflowError异常和OutOfMemoryError异常。

本地方法栈

本地方法栈与虚拟机栈类似,唯一的区别是本地方法栈是用来执行Native方法的。

Java堆

Java堆是我们在编写Java程序中所能使用的最大的一块的内存区域了,也是我们经常需要调整的区域。这个区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存,包括数组(因为数组也是引用数据类型)。Java堆与虚拟机栈不同,它是被所有线程共享的区域,在虚拟机启动的时候创建。

Java堆还可以进一步分为:新生代和老年代;再细致一点的有Eden空间、From Survivor空间、To Survivor空间等,这些更细致的分区是在Java堆垃圾收集器进行垃圾管理的时候需要考虑的。

Java堆的大小可以是固定的,也可以是不固定的,可以通过-Xmx和-Xms控制,前者是最大值,后者是最小值,在两者相同时,堆的大小就是固定的。在内存中如果没有足够的空间来分配,将会抛出OutOfMemoryError异常。

方法区

方法区和Java堆一样,是各个线程共享的内存区域,用来存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

这个区域也是属于需要进行垃圾回收的区域,主要是回收常量池和对类型的卸载,一般来说,回收的效果不会太理想,但是却是必须的。

根据Java虚拟机的规范规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

运行时常量池

该区域是方法区的一部分,用于存放编译期生成的各种字面量和符号引用,有时候直接引用也会放入,这部分内容将在类加载后进入方法区的运行时常量池中存放。

运行时常量池有一定的动态性,对String类有所了解的同学应该明白,在运行期间通过String类的intern()方法可以动态往常量池里动态添加常量。

直接内存

直接内存不属于Java虚拟机运行时数据区的一部分,而是属于操作系统管理的区域。这部分的使用很频繁,利用的好,可以大大提升程序的运行效率,比较优秀的使用例如基于NIO的Netty框架等。

为什么使用直接内存可以提升性能呢,因为可以避免在Java堆和Native堆中来回复制数据的开销。

这部分的内存使用不会收到Java堆大小的限制,但会收到本机的内存大小限制。因此,在操作这部分内存时需要谨慎,一旦出问题,可能会影响到本机的其它服务。 当各个内存区域总和大于物理内存限制,抛出OutOfMemoryError异常。

点赞
收藏
评论区
推荐文章
红烧土豆泥 红烧土豆泥
2年前
(转载)Java内存区域(运行时数据区域)和内存模型(JMM) - czwbig
转载自:Java内存区域和内存模型是不一样的东西,内存区域是指Jvm运行时将数据分区域存储,强调对内存空间的划分。而内存模型(JavaMemoryModel,简称JMM)是定义了线程和主内存之间的抽象关系,即JMM定义了JVM在计算机内存(RAM)中的工作方式,如果我们要想深入了解Java并发编程,就要先理解好Java内存模型。Java
九路 九路
3年前
1 Java内存区域与内存溢出异常
1java虚拟机对内存的管理java虚拟机在执行java程序的时候把内存分为若干个不同的区,这些区各自有不同的用处,以及创建和销毁时间.有的区随着虚拟机的启动而启动,有的区则依赖用户线程的启动和结束而启动和结束.根据java虚拟机规范,java虚拟机将内存分为下面几个部分:如下图image(https://imghelloworld.o
Wesley13 Wesley13
2年前
java虚拟机(四)
 学习了java运行时数据区,知道每个内存区域保存什么数据,可以参考:https://www.cnblogs.com/huigelaile/p/diamondshine.html,然后了解内存溢出和内存泄露是很有必要的,一方面是为了面试,更重要是的在工作中能够快速定位错误原因并且解决内存溢出分类:1、java.lang.OutOf
Wesley13 Wesley13
2年前
Java 几种常见的OOM
Java虚拟机内存有好几个运行时数据区会有OOM的异常,如果能够区分根据报错区分出是哪些区域报出来的异常,会更便于定位问题,解决问题。1.Java堆溢出原因:由于不断创建对象实例,当对象数量达到了最大堆的容量限制后产生内存溢出异常。现象:java.lang.OutOfMemoryError:Javaheapspace解决方法:1)首
Easter79 Easter79
2年前
Tomcat中JVM内存溢出及合理配置
Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个Java虚拟机。Tomcat的内存溢出本质就是JVM内存溢出,所以在本文开始时,应该先对JavaJVM有关内存方面的知识进行详细介绍。一、JavaJVM内存介绍JVM管理两种类型的内存,堆和非堆。按照官方的说法:“Java虚拟机具有一个堆,堆是运行时数据区域,
Wesley13 Wesley13
2年前
Java系列笔记
Java垃圾回收概况  JavaGC(GarbageCollection,垃圾收集,垃圾回收)机制,是Java与C/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代码,对内存泄露和溢出的问题,也不需要像C程序员那样战战兢兢。这是因为在Java虚拟机中,存在自动内存管理和垃圾清扫机制。概括地说,该机制对JVM(J
Stella981 Stella981
2年前
JVM 运行时内存分配
Java内存分配在解释这个问题之前,我想简单的记录一下Java虚拟机对内存的分配管理。!(https://static.oschina.net/uploads/space/2017/0207/160723_gnLQ_1054538.jpg)简单的说,Java运行时内存区域,就由上面几部分构成。青绿色标记的,是每个线程私有的内存区域,其
Wesley13 Wesley13
2年前
Java 内存区域和GC机制
Java垃圾回收概况  JavaGC(GarbageCollection,垃圾收集,垃圾回收)机制,是Java与C/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代码,对内存泄露和溢出的问题,也不需要像C程序员那样战战兢兢。这是因为在Java虚拟机中,存在自动内存管理和垃圾清扫机制。概括地说,该机制对JVM
Stella981 Stella981
2年前
JVM笔记二:Java内存区域
Java程序在虚拟机自动内存管理的机制的帮助下,不容易出现内存泄露和内存溢出问题,这也就要求程序员需要了解虚拟机处理内存的机制,以解决OOM问题。运行时数据区域!Java虚拟机运行时数据区(https://oscimg.oschina.net/oscnet/3755e1d9e9bf4068b2b3b77b4c0b6bf99b8.jpg)
Stella981 Stella981
2年前
JVM探秘3:内存溢出
在Java虚拟机内存区域中,除了程序计数器外,其他几个内存区域都可能会发生OutOfMemoryError,这次通过一些代码来验证虚拟机各个内存区域存储的内容。在实际工作中遇到内存溢出异常时,需要做到能根据异常信息快速判断是哪个内存区域的溢出,知道什么样的代码会导致这些区域内存溢出,并且知道出现内存溢出后如何处理。Java堆溢出Jav