JVM的内存区域划分 JVM的内存区域划分

JVM的内存区域划分

 

                          JVM的内存区域划分html

  学过C语言的朋友都知道C编译器在划份内存区域的时候常常将管理的区域划分为数据段和代码段,数据段包括堆、栈以及静态数据区。那么在Java语言当中,内存又是如何划分的呢?java

  因为Java程序是交由JVM执行的,因此咱们在谈Java内存区域划分的时候事实上是指JVM内存区域划分。在讨论JVM内存区域划分以前,先来看一下Java程序具体执行的过程:程序员

                                       

  如上图所示,首先Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.class后缀),而后由JVM中的类加载器加载各个类的字节码文件,加载完毕以后,交由JVM执行引擎执行。在整个程序执行过程当中,JVM会用一段空间来存储程序执行期间须要用到的数据和相关信息,这段空间通常被称做为Runtime Data Area(运行时数据区),也就是咱们常说的JVM内存。所以,在Java中咱们经常说到的内存管理就是针对这段空间进行管理(如何分配和回收内存空间)。数组

  在知道了JVM内存是什么东西以后,下面咱们就来讨论一下这段空间具体是如何划分区域的,是否是也像C语言中同样也存在栈和堆呢?数据结构

一.运行时数据区包括哪几部分?

  根据《Java虚拟机规范》的规定,运行时数据区一般包括这几个部分:程序计数器(Program Counter Register)、Java栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(Method Area)、堆(Heap)。多线程

  如上图所示,JVM中的运行时数据区应该包括这些部分。在JVM规范中虽然规定了程序在执行期间运行时数据区应该包括这几部分,可是至于具体如何实现并无作出规定,不一样的虚拟机厂商能够有不一样的实现方式。函数

二.运行时数据区的每部分到底存储了哪些数据?

  下面咱们来了解一下运行时数据区的每部分具体用来存储程序执行过程当中的哪些数据。post

1.程序计数器

  程序计数器(Program Counter Register),也有称做为PC寄存器。想必学过汇编语言的朋友对程序计数器这个概念并不陌生,在汇编语言中,程序计数器是指CPU中的寄存器,它保存的是程序当前执行的指令的地址(也能够说保存下一条指令的所在存储单元的地址),当CPU须要执行指令时,须要从程序计数器中获得当前须要执行的指令所在存储单元的地址,而后根据获得的地址获取到指令,在获得指令以后,程序计数器便自动加1或者根据转移指针获得下一条指令的地址,如此循环,直至执行完全部的指令。url

  虽然JVM中的程序计数器并不像汇编语言中的程序计数器同样是物理概念上的CPU寄存器,可是JVM中的程序计数器的功能跟汇编语言中的程序计数器的功能在逻辑上是等同的,也就是说是用来指示 执行哪条指令的。.net

  因为在JVM中,多线程是经过线程轮流切换来得到CPU执行时间的,所以,在任一具体时刻,一个CPU的内核只会执行一条线程中的指令,所以,为了可以使得每一个线程都在线程切换后可以恢复在切换以前的程序执行位置,每一个线程都须要有本身独立的程序计数器,而且不能互相被干扰,不然就会影响到程序的正常执行次序。所以,能够这么说,程序计数器是每一个线程所私有的。

  在JVM规范中规定,若是线程执行的是非native方法,则程序计数器中保存的是当前须要执行的指令的地址;若是线程执行的是native方法,则程序计数器中的值是undefined。

  因为程序计数器中存储的数据所占空间的大小不会随程序的执行而发生改变,所以,对于程序计数器是不会发生内存溢出现象(OutOfMemory)的。

2.Java栈

  Java栈也称做虚拟机栈(Java Vitual Machine Stack),也就是咱们经常所说的栈,跟C语言的数据段中的栈相似。事实上,Java栈是Java方法执行的内存模型。为何这么说呢?下面就来解释一下其中的缘由。

  Java栈中存放的是一个个的栈帧,每一个栈帧对应一个被调用的方法,在栈帧中包括局部变量表(Local Variables)、操做数栈(Operand Stack)、指向当前方法所属的类的运行时常量池(运行时常量池的概念在方法区部分会谈到)的引用(Reference to runtime constant pool)、方法返回地址(Return Address)和一些额外的附加信息。当线程执行一个方法时,就会随之建立一个对应的栈帧,并将创建的栈帧压栈。当方法执行完毕以后,便会将栈帧出栈。所以可知,线程当前执行的方法所对应的栈帧一定位于Java栈的顶部。讲到这里,你们就应该会明白为何 在 使用 递归方法的时候容易致使栈内存溢出的现象了以及为何栈区的空间不用程序员去管理了(固然在Java中,程序员基本不用关系到内存分配和释放的事情,由于Java有本身的垃圾回收机制),这部分空间的分配和释放都是由系统自动实施的。对于全部的程序设计语言来讲,栈这部分空间对程序员来讲是不透明的。下图表示了一个Java栈的模型:

  局部变量表,顾名思义,想必不用解释你们应该明白它的做用了吧。就是用来存储方法中的局部变量(包括在方法中声明的非静态变量以及函数形参)。对于基本数据类型的变量,则直接存储它的值,对于引用类型的变量,则存的是指向对象的引用。局部变量表的大小在编译器就能够肯定其大小了,所以在程序执行期间局部变量表的大小是不会改变的。

  操做数栈,想必学过数据结构中的栈的朋友想必对表达式求值问题不会陌生,栈最典型的一个应用就是用来对表达式求值。想一想一个线程执行方法的过程当中,实际上就是不断执行语句的过程,而归根到底就是进行计算的过程。所以能够这么说,程序中的全部计算过程都是在借助于操做数栈来完成的。

  指向运行时常量池的引用,由于在方法执行的过程当中有可能须要用到类中的常量,因此必需要有一个引用指向运行时常量。

  方法返回地址,当一个方法执行完毕以后,要返回以前调用它的地方,所以在栈帧中必须保存一个方法返回地址。

  因为每一个线程正在执行的方法可能不一样,所以每一个线程都会有一个本身的Java栈,互不干扰。

3.本地方法栈

  本地方法栈与Java栈的做用和原理很是类似。区别只不过是Java栈是为执行Java方法服务的,而本地方法栈则是为执行本地方法(Native Method)服务的。在JVM规范中,并无对本地方发展的具体实现方法以及数据结构做强制规定,虚拟机能够自由实现它。在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一。

4.堆

  在C语言中,堆这部分空间是惟一一个程序员能够管理的内存区域。程序员能够经过malloc函数和free函数在堆上申请和释放空间。那么在Java中是怎么样的呢?

  Java中的堆是用来存储对象自己的以及数组(固然,数组引用是存放在Java栈中的)。只不过和C语言中的不一样,在Java中,程序员基本不用去关心空间释放的问题,Java的垃圾回收机制会自动进行处理。所以这部分空间也是Java垃圾收集器管理的主要区域。另外,堆是被全部线程共享的,在JVM中只有一个堆。

5.方法区

  方法区在JVM中也是一个很是重要的区域,它与堆同样,是被线程共享的区域。在方法区中,存储了每一个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。

  在Class文件中除了类的字段、方法、接口等描述信息外,还有一项信息是常量池,用来存储编译期间生成的字面量和符号引用。

  在方法区中有一个很是重要的部分就是运行时常量池,它是每个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被建立出来。固然并不是Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,好比String的intern方法。

  在JVM规范中,没有强制要求方法区必须实现垃圾回收。不少人习惯将方法区称为“永久代”,是由于HotSpot虚拟机以永久代来实现方法区,从而JVM的垃圾收集器能够像管理堆区同样管理这部分区域,从而不须要专门为这部分设计垃圾回收机制。不过自从JDK7以后,Hotspot虚拟机便将运行时常量池从永久代移除了。

  以上为我的见解和观点,若有不正之处但愿谅解并欢迎指正。

  参考资料:

  http://blog.csdn.net/ns_code/article/details/17565503

  http://www.cnblogs.com/sunada2005/p/3577799.html

  《深刻理解Java虚拟机》

  《Java虚拟机规范 SE7》

  转载请标明地址:http://www.cnblogs.com/dolphin0520/p/3613043.html

做者: Matrix海子
         
本博客中未标明转载的文章归做者 Matrix海子和博客园共有,欢迎转载,但未经做者赞成必须保留此段声明,且在文章页面明显位置给出原文链接,不然保留追究法律责任的权利。