Java内存区域剖析 —— 定位OutOfMemory异常之前的必修课

原创 2017年08月06日 16:37:28

带着问题阅读

  • Java中都有哪些数据需要放进内存?
  • 如果你是JVM的设计者,你会怎样将内存按功能进行划分?
  • HotSpot是怎么划分内存的?


导语

上一讲带着大家踏入了Java虚拟机的大门,从这一讲开始,进入专题的第一个版块——Java虚拟机的自动内存管理机制。

说起内存,大家很容易就想到了内存溢出,的确,对于Java工程师来说,谁的一生不会经历OutOfMemory呢,要么是Heap Space家起火,要么是Stack家淹水了,要么就是PermGen被打劫了。在学习如何定位这些异常发生的原因并提出解决方案之前,我们必须了解一下,Java虚拟机是如何划分自己的内存区域的。

本文是Effective Java专栏Java虚拟机专题的第二讲,如果你觉得看完之后对你有所帮助,欢迎订阅本专栏,也欢迎您将本专栏分享给你身边的工程师同学。

在学习本节课程之前,建议您先了解一下以下知识点:


Java内存区域

一个Java进程启动后,会被划分一块类似于疆土的内存区域,虚拟机将这块大的内存,按照所存储的数据类型,划分为不同的区域进行管理,Java虚拟机的运行时数据区,可以用下面这张图来表示:



下面就来对这张内存区域模型图作详细的讲解。


程序计数器

程序计数器在虚拟机内存中是一块很小的区域,这个计数器记录了当前线程执行到了哪一行的字节码指令。每条线程都拥有一个独立的程序计数器,有了这个计数器,才能在不停的线程切换中,让线程记得下一条要执行的指令

程序计数器是唯一一个在Java虚拟机规范中没有规定任何OutOfMemory异常的区域。


Java虚拟机栈

和程序计数器一样,Java虚拟机栈也是线程私有的。虚拟机栈主要存储的内容是方法执行时的局部变量表,包括各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(指向一个对象地址的指针)和returnAddress类型(指向一条字节码指令的地址)。

其中,64位长度的long和double会占用2个局部变量空间,其余数据类型只占用1个。局部变量表所需的内存空间在编译器就分配完成,也就是说,进入一个方法时,这个方法需要分配多大的局部变量空间是完全确定的

可能这样讲大家还有点模糊,这里上个图:


每个线程都拥有一个虚拟机栈,线程中每个方法执行时都会在栈中创建一个栈帧,栈帧中就包含了上面所说的数据,方法每递归一次,就新建一个栈帧,依此类推。后面讲到虚拟机字节码执行引擎时,会对这张图作更详细的介绍。

在Java虚拟机规范中,这个区域有两种异常情况:

  • 如果线程运行时的栈帧总得大小超过虚拟机限制的大小,会抛出StackOverflow异常,这一点通常发生在递归运行时;
  • 如果虚拟机栈设置为可以动态扩展,并且在扩展时无法申请到足够内存,则会抛出OutOfMemory异常。


本地方法栈

本地方法栈发挥的作用和虚拟机栈是十分相似的,主要都是存储着方法执行的局部变量的信息,不同的是虚拟机栈是为执行Java方法(也就是字节码)服务,而本地方法栈是为虚拟机使用到的本地Native方法服务。

基于两者的相似性,HotSpot虚拟机直接将两者合二为一。

和虚拟机栈一样,本地方法栈也有StackOverflow和OutOfMemory异常。


Java堆

Java堆是Java虚拟机内存中最大的一块,是被所有线程共享的一块内存区域。Java堆负责存放Java对象实例,因此Java堆也是垃圾收集的主要区域。

Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。

可以通过-Xmx和-Xms参数来控制堆的大小,当堆没有内存完成实例分配,且无法继续扩展时,就会抛出OutOfMemory异常。


方法区

前面提到了虚拟机栈是用来管理Java方法执行的内存信息的,因此,很明显,这里的方法区存储的并不是方法执行的信息。

方法区用于存储已被虚拟机加载的类信息、常量、静态变量、JIT编译器编译后的代码等数据,也是各个线程共享的内存区域。


直接内存

直接内存并不是虚拟机运行时数据区的一部分,但是Java可以对这部分的内存进行使用,比如JDK 1.4新加入的NIO,它可以通过Native函数库直接在机器内存中获取内存,然后通过存储在Java堆中的对象,作为这块内存的引用进行操作,避免了在Java堆和Native之间来回复制数据,提高了操作的性能。

直接内存的大小只受到本机总内存的限制,因此,如果直接内存使用过多,超过了机器的物理内存限制,就会导致OutOfMemory异常。


总结

这一讲,主要给大家介绍了Java虚拟机是如何划分内存以及各个内存区域的功能和职责。了解了这些之后,我们就能够理解为什么会发生各种内存异常的异常了,在下一讲,我将在自己的机器上,演示各种内存异常是如何发生的。


课后思考

如果要你在自己的机器上模拟Java堆的内存溢出,你会怎么做?欢迎在评论区写下你的想法,O(∩_∩)O谢谢。


上一讲课后思考题的答案

上一讲的问题是——“我们经常看到有人说他掌握J2SE、J2ME、J2EE,也看过有人说他很懂Java SE、Java ME、Java EE,那么到底应该叫是J2XX还是Java XX呢?”

这个问题涉及到Java发展的历史,1998年12月4日,JDK1.2发布,Sun将Java技术体系拆分为3个方向——J2SE、J2ME、J2EE;而到了2006年12月11日,JDK1.6发布,Sun终结了J2XX的命名方式,启用Java SE、Java ME、Java EE的命名方式。因此,在面试或者平时写作的时候,建议大家采用Java XX的方式吧,就像Java的首字母要大写一样。


参考资料


版权声明:本文为博主原创文章,未经博主允许不得转载。

JAVA内存区域--OOM(outofmemory)异常

堆溢出异常堆栈信息:java.lang.OutOfMemoryError: Java heap space。 JVM配置:gc -Xms10M -Xmx10M -Xmn7M -Xss1M -XX:+P...
  • u010711294
  • u010711294
  • 2017年02月09日 18:27
  • 104

深入理解JAVA虚拟机(一):内存区域与内存溢出异常

1、内存区域 Java虚拟机在执行JAva程序的过程中会把它所管理的内存划分为若干个不同的数据区域,称为运行时数据区域,如图所示。 Java虚拟机内存结构中的程序计数器、虚拟机栈和本地方法栈...
  • supersnow0622
  • supersnow0622
  • 2016年08月24日 15:10
  • 430

定位JVM内存溢出问题思路总结

JVM的内存溢出问题,是个常见而有时候有非常难以定位的问题。定位内存溢出问题常见方法有很多,但是其实很多情况下可供你选择的有效手段非常有限。很多方法在一些实际场景下没有实用价值。这里总结下我的一些定位...
  • xishanxinyue
  • xishanxinyue
  • 2013年11月11日 12:03
  • 1869

poi解决内存消耗过大溢出问题

poi是一个excel的文件的解析引擎,我目前接触到的就两种文件格式xsl,xlsx,xsl是早先版本的excel文件格式,xlsx是后期版本的规范 poi解析引擎的解析速度是非常快的,一般2000条...
  • wujiang88
  • wujiang88
  • 2016年03月06日 23:20
  • 3470

Java内存区域与异常

Java虚拟机在运行时会把其管理的内存划分为若干不同的数据区域。《Java虚拟机规范》规定的数据区域通常包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区、运行时常量池以及直接内存。这些...
  • thesingularityisnear
  • thesingularityisnear
  • 2016年07月28日 00:14
  • 165

Java内存区域与模拟内存区域异常

我把Java的内存区域画了一张思维导图,以及各区域的主要功能。 模拟Java堆溢出 Java堆用于存储对象实例,只要不断地创建对象并且保证GC ROOTS到对象之间有可达路径避免被回收机制...
  • xlinsist
  • xlinsist
  • 2015年07月24日 18:39
  • 604

java内存泄漏的定位与分析

1、为什么会发生内存泄漏 java 如何检测内在泄漏呢?我们需要一些工具进行检测,并发现内存泄漏问题,不然很容易发生down机问题。 编写java程序最为方便的地方就是我们不需要管理内存的...
  • kingmax54212008
  • kingmax54212008
  • 2016年07月08日 10:38
  • 1053

java异常的深入学习以及异常处理时一些内存问题

通过如下链接系统学习java异常框架,并通过文章的评论和自己的测试得到如下总结 http://blog.csdn.net/hguisu/article/details/6155636 小结: ...
  • zhanghongzheng3213
  • zhanghongzheng3213
  • 2016年06月14日 10:26
  • 405

Java中抛出的内存异常总结

Java中抛出的内存异常总结 根据上述的总结:可以整理出来,JVM在运行的时候大致会分为五个区域。方法区、堆区、本地方法栈、虚拟机栈、程序计数器。 其中程序计数器不会报出OutOfMemoryEr...
  • daguairen
  • daguairen
  • 2016年10月19日 20:51
  • 692

paip.提升性能---C#.NET程序内存占用过多以及OutOfMemory解决方法

paip.提升性能---C#.NET程序内存占用过多以及OutOfMemory解决方法 作者Attilax ,  EMAIL:1466519819@qq.com  需要的效...
  • attilax
  • attilax
  • 2013年04月22日 22:21
  • 5157
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Java内存区域剖析 —— 定位OutOfMemory异常之前的必修课
举报原因:
原因补充:

(最多只允许输入30个字)