理解JVM——Java内存区域

版权声明:http://blog.csdn.net/anxpp https://blog.csdn.net/anxpp/article/details/51920314

 转载请注明出处:http://blog.csdn.net/anxpp/article/details/51920314,谢谢!

     知其然,知其所以然。

    了解Java的内存相关知识,还是有必要的。


1、概述

    我专业是嵌入式,所以自然对C++是比较了解的,但是后来自学的Java,其内存的自动管理(一开始也不是很习惯...),可谓减少了相当大的开发量。

    使用C++的时候,一不小心,就会造成相当严重的内存泄露,而这在Java中,看起来就美好多了,不过如果没有较好的编程习惯,也是有可能会造成内存泄露的,可参考:JAVA 内存泄露详解(原因、例子及解决)

    如果理解Java的内存分配,以及不同的内存区域,将使我们更容易编写出更出色的代码。

    本文介绍Java虚拟机在运行Java程序时的不同数据区。


2、运行时数据区

    下图描述的是Java虚拟机所管理的不同运行时数据区:

01

    这些不同的区域都有各自的用途。

    2.1、程序计数器

    学习汇编时,里面的程序计数器的值总是指向当前执行的下一个地址,Java虚拟机中的程序计数器也扮演这这样一个角色。

    这是一块比较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时,通过改变这个计数器的值,来选取下一条需要执行的字节码指令。

    程序计数器为分支、循环、跳转、异常处理和线程恢复等提供了基础支持。

    多线程中,单个处理器(即一个内核)任何时候都只能执行一条线程中的指令,Java是通过轮流切换线程并分配处理器执行时间的方式来实现的,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,个线程间的计数器互不影响,独立存储。所以这块内存区域是“线程私有”的内存。

    如果线程正在执行的是Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址。如果执行的是本地方法,这个计数器的值为空(Undefined)。

    Java虚拟机规范中对此区域没有规定任何OutOfMemoryError情况。

    2.2、虚拟机栈

    其实这块内存区域,称作Java方法栈会更容易理解。

    Java虚拟机栈也是线程私有的,生命周期与线程相同。

    虚拟机栈描述的是Java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(用于支持虚拟机进行方法调用和方法执行的数据结构),用于存储局部变量表、操作数栈、动态链接和方法出口等信息。每一个方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

    局部变量表存放了编译器可知的各种基本数据类型(即8种Java基本数据类型),对象引用和returnAddress类型。

    当要进入一个方法时,这个方法需要在栈中分配多大的内存是完全确定的,并在编译期间就完成了分配,并在方法运行期间不会发生改变。long和double会占用2个局部变量空间,其他类型都只占用一个。

    在Java虚拟机规范中,该区域可能会发生两种异常

    1、StackOverFlowError:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出该异常。

    2、OutOfMemoryError:如果虚拟机栈动态扩展时,无法申请到足够的内存,会抛出OutOfMemoryError异常。

    对内存溢出异常的介绍,后面会专门写一篇文章,此处暂不多介绍。

    2.3、本地方法栈

     本地方法栈与Java虚拟机栈作用是相似的,不过它是为本地方法服务的。

     介于此,在很多虚拟机(如我们最常使用的Sun HotSpot)实现时将本地方法栈和虚拟机栈合二为一,当然,内存溢出情况也与Java虚拟机栈类似。

    2.4、堆

    这可能是我们最想关心的内存区域了,Java堆通常是Java虚拟机所管理的内存中最大的一块,是线程共享的,在虚拟机启动时创建。

    此内存区域唯一的目的就是存放对象实例,几乎(Java规范规定的是所有对象实例和数组)所有的对象实例都在这里分配内存。

    Java堆是垃圾回收器管理的主要区域,也常被称为GC堆。

    如果堆已经内有足够的内存以完成对象实例的分配,并且堆无法扩展时,会抛出OutOfMemoryError异常。

    2.5、方法区

    方法区是线程共享的,存储的是已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。因此,也常将方法区称为“永久代”。

    如果方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

    运行时常量池

    运行时常量池是方法区的一部分。

    Class文件中除了有类的版本、字段、方法、接口等信息,还有常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。

    因为运行时常量区也是方法区的一部分,所以如果常量区不发再申请到内存时,会抛出OutOfMemoryError异常。

    2.6、直接内存

    直接内存实际是JVM外的主机内存,不是虚拟机运行时数据区的一部分。

    这部分内存也常被频繁使用,也容易导致OutOfMemoryError异常。

    直接内存的分配不受Java堆大小的限制。

    但是在JVM的内存设置不合理时,比如各个内存区域综合大于物理内存时,动态扩展时就很容易出现OutOfMemoryError异常。

 

    其他的,之后在添加文章介绍吧。

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页