您的位置:首页 > 其它

JVM内存管理机制--运行时数据区域(详解)

2017-09-15 10:53 633 查看



一、介绍

Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.class后缀),然后由JVM中的类加载器加载各个类的字节码文件,加载完毕之后,交由JVM执行引擎执行。在整个程序执行过程中,JVM会用一段空间来存储程序执行期间需要用到的数据和相关信息,这段空间一般被称作为Runtime Data Area(运行时数据区),也就是我们常说的JVM内存。因此,在Java中我们常常说到的内存管理就是针对这段空间进行管理(如何分配和回收内存空间)。

java虚拟机在执行java程序的过程中,会把它所管理的内存分成不同的区域来存放不同模块的数据信息。他们有不同的用途,创建和销毁时间,有两种情况: 

第一种:随jvm虚拟机启动而创建的数据区:方法区和heap,所有的线程均共有他们。 

第二种:每个java线程独有的数据区:程序计数器、虚拟机栈、本地方法区栈。 

下面逐个详细介绍:


1 方法区

1.方法区又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。 

2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。

3.—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域.

也称”永久代” 、“非堆”, 它用于存储虚拟机加载的类信息、常量、静态变量、是各个线程共享的内存区域。默认最小值为16MB,最大值为64MB,可以通过-XX:PermSize 和 -XX:MaxPermSize 参数限制方法区的大小。 
运行时常量池:是方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译器生成的各种符号引用,这部分内容将在类加载后放到方法区的运行时常量池中. 

当JVM使用类装载器装载某个类时,它首先要定位对应的class文件,然后读入这个class文件,最后,JVM提取该文件的内容信息,并将这些信息存储到方法区,最后返回一个class实例。上面是对类的装载过程作了个简单的描述,看了上面一段文字,也许你会问:方法区是什么?里面存了哪些内容?下面我们将对方法区作一个详细的描述。 
方法区是什么?有哪些特点? 

方法区是系统分配的一个内存逻辑区域,是用来存储类型信息的(类型信息可理解为类的描述信息)。方法区主要有以下几个特点: 

一.方法区是线程安全的。由于所有的线程都共享方法区,所以,方法区里的数据访问必须被设计成线程安全的。例如,假如同时有两个线程都企图访问方法区中的同一个类,而这个类还没有被装入JVM,那么只允许一个线程去装载它,而其它线程必须等待 

二.方法区的大小不必是固定的,JVM可根据应用需要动态调整。同时,方法区也不一定是连续的,方法区可以在一个堆(甚至是JVM自己的堆)中自由分配。 

三.方法区也可被垃圾收集,当某个类不在被使用(不可触及)时,JVM将卸载这个类,进行垃圾收集 
方法区里存放的是哪些内容? 

方法区里存的都是类型信息,也就是类的信息,而类的信息又包括以下内容: 

1.类的全限定名(类的全路径名) 

2.类的直接超类的全限定名(如果这个类是Object,则它没有超类) 

3.这个类是类型(类)还是接口 

4.类的访问修饰符,如public、abstract、final等 

5.所有的直接接口全限定名的有序列表(假如它实现了多个接口) 

6. 常量池 

7.字段、方法信息、类变量信息(静态变量) 装载该类的装载器的引用(classLoader)、类型引用(class) . 

其实,我们没必要全部记住,只要根据上面内容有个大概的了解,然后对类型这个概念有个大概的认识即可。下面我们将主要对常量池和类变量信息作一下分析。 

先说类变量吧,类变量内容少些,描述起来比较容易。类变量,顾名思义,就是属于类的变量,所有类的实例都共享的变量,也就是常说的静态变量。关于类变量,我们只要知道方法区里有个静态区,静态区是专门用来存放静态变量以及静态块的。所有类的实例都共享方法区中的内容。访问类变量的方式可通过实例(对象)来访问,也可通过类型来直接访问,java规范推荐使用类型来直接访问。


2 堆(HEAP)

对于大多数Java程序来说,堆是虚拟机管理的内存最大的一个地方,它存放了几乎所有的实例对象与数组,它被程序中所有线程所共享。java堆是垃圾集收器管理的主要地方。Java中,程序员基本不用去关心空间释放的问题,Java的垃圾回收机制会自动进行处理。因此这部分空间也是Java垃圾收集器管理的主要区域。另外,堆是被所有线程共享的,在JVM中只有一个堆。


3 程序计数器

程序计数器是内存管理中的一个小部分区域,它可以看过每个线程所执行的字节码的行号指示器,字节解释器执行时就是改变这个行号指示器来进行获取下一条需要执行的字节码命令,分支、跳转、循环、异常处理、线程恢复等都依赖这个计数器来完成。 

由于java虚拟机的多线程是通过线程轮流切换、并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器只执行一个线程中的一条指令。因此,为了在每个线程切换后继续获取到正确的指令,每个线程中都存在一个程序计数器来记录每个线程的下个需要执行的指令。各个线程之间的程序计数器均相互不影响。 

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


4 java虚拟机栈

与程序计数器一样,虚拟机栈也是每个线程私有的,他的生命周期与线程一样。虚拟机栈描述的是java方法执行的内存模型,也就是,每个方法在执行的同时都会创建一个栈帧,多个方法执行时会存在多个栈帧,但当前执行的方法根据栈的特性总是在栈顶,如图,每个栈帧包含的信息:


 

每个方法的开始执行到结束,都对应每个栈帧在虚拟机栈中的入栈和出栈的过程。其中图中的局部变量表中存放各种基本类型(char,byte,short,int,long,float ,double ,boolean)、对象引用和returnAddress(指向了一条字节码指令的地址)。


5 本地方法栈

与虚拟机栈基本类似,区别在于虚拟机栈为虚拟机执行的java方法服务,而本地方法栈则是为Native方法服务。

另外:有关垃圾回收的知识,可以参考一遍文章,写的简单明了,很容易接收: 

虚拟机–新生代与老年代GC 
http://my.oschina.net/sunnywu/blog/332870
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: