JVM内存管理-运行时数据

来源:互联网 发布:野生程序员博客 编辑:程序博客网 时间:2024/05/16 14:20

Java的内存管理是由虚拟机自动进行,程序员不得自行干预,若出现内存溢出等情况,若不了解jvm的运行原理,很难解决问题。此片文章是阅读了JVM规范之后的一些感想,用自己理解的方式进行表述。

运行时数据

根据java虚拟机规范,Java的运行时数据如下图所示:


解释

1、程序计数器
程序计数器是记录程序运行到哪一行的行号。java的多线程,每一时刻,只能有一个线程在进行(根据CPU的时间轮转),为了保证线程之间切换时找到上次执行的位置,JVM引用程序计数器来保存位置。
没个线程都有一个自己的程序计数器(私有内存),这块内存是不可能出现内存泄漏的。
若Java执行的native代码,则程序计数器为null。
2、虚拟机栈
与程序计数器一样,它也是线程私有的,生命周期与线程相同。说到虚拟机栈,不得不引入一个叫做栈帧的概念。
栈帧:jvm为每一个方法所创建的内存单元,每个方法的执行和完成,都伴随着一个栈帧的入栈和出栈的过程。
栈帧中保存这方法运行所需要的局部变量等信息。局部变量所需要的内存大大小在编译期间即可完全确定,也就是说,在创建栈帧时,所需要的大小已经由JVM确定好了。
jvm规范规定,虚拟机栈可以为固定大小,但也可以动态扩展(不知道为什么这么规定),大部分的虚拟机实现都已经实现了栈的动态扩展。这块可能出现两种异常:
第一种情况,固定大小情况,若程序运行时所申请的空间大于虚拟机允许的栈深度,会抛出StackOverFlowError(栈溢出)异常。
第二种情况,虚拟机栈可以动态扩展栈的大小,若此时程序运行所申请的空间大于虚拟机允许的栈深度,则会抛出OutOfMemoryError(内存溢出)异常。
3、本地方法栈

本地方法栈与虚拟机栈类似,区别就是虚拟机栈执行的是Java的字节码,本地方法栈执行的是本地的native方法。虚拟机规范对这块区域没有强制的规定,有的虚拟机甚至将虚拟机栈和本地方法栈合二为一,这块区域也可能抛出StackOverFlowError异常和OutOfMemoryError异常。

4、虚拟机堆(Java堆、GC堆)

这是Java虚拟机中最大的一块区域,主要是用于保存实例对象的,JVM规范对此有明确规定,所有的对象实例和数据都应该分配在此块区域中,但这个并不是那么绝对的。

与栈不同,堆是所有线程所共享的。

这块区域也是java垃圾回收主要负责的区域,大部分的虚拟机实现都是可扩展的(可通过-Xmx和-Xms来控制),如所申请的空间大于堆所允许的空间,会抛出OutOfMemoryError异常。

5、方法区

方法区和堆一样,也是所有线程所共享,同样不需要连续的内存单元,只要逻辑上相连即可(栈所需要的内存是连续的),它主要保存类的相关信息、常量、静态变量等等。Java的垃圾回收在此块区域是比较少见的,主要是针对常量池的回收和类的卸载。此块区域也有可能抛出OutOfMemoryError异常。

补充:

6、运行时常量池

运行时常量池是属于方法区的一部分,用于保存编译时产生的字面值和符号引用。也就是说,这块区域是编译时就一定确定下来的,但是jvm规定可以在运行时动态的改变常量池中的内容,例如String类中intern()方法。

同样也有可能抛出OutOfMemoryError异常。

7、直接内存

顾名思义,这块区域并不属于Java运行时的内存。也不属于Java虚拟机,它是在本地PC机上分配的一块内存。一般使用较少,但在使用native方法时可能会用到,在java堆中保存了该块内存的引用,主要是为了防止本地PC机内存和Java虚拟机内存之间的来回复制。它也有可能抛出OutOfMemoryError异常。

总结

以上为Java虚拟机运行时内存的相关信息,不是很全面,有些简单的原理其他资料上都有,就没有往上写,刚刚接触java的朋友看完之后可能会有些迷茫,主要是写的不系统,慢慢就好了。由此可以看出很多地方都有可能出现内存泄漏(溢出),但出现StackOverFlow的异常就比较容易定位了。
出现了内存方面的问题一般是比较头疼的,但很多接触java不久的朋友遇到的内存问题都比较简单,比如递归时的问题所导致。如果到了项目后期开始运行的时候出现内存问题就比较头疼了。主要是review代码比较麻烦。
我曾经负责的项目就有一次出现了内存溢出问题,当时是一个统计类型的项目导入数据、分析报表等。大约1周左右抛一次异常,找了各种资料,代码走查了3边,都没有分析出错误,最后在一个国外的博客上看到了一篇文章,推荐时在虚拟机中另起一个垃圾回收线程,终于把问题解决了。我感觉应该虚拟机潜在的Bug造成的吧!像我遇到的这么麻烦的问题并不少见,希望大家不会遇到,如果遇到了也有个解决思路,由于时间久远,如何增加一个垃圾回收线程我已经忘了,当时也是找了很久,这里就不写了。
0 0