您的位置:首页 > 其它

一:JVM学习一JVM内存模型

2016-02-20 00:00 246 查看
摘要: JVM,java虚拟机,内存模型

以前自己只是用java去编写程序,没有深入了解过,空闲下来自己看看书,学习JVM相关知识,这也是自我深造学习。虽然现在网上有很多这方面的博文之类,但是我认为我还有必要自己去整理写一下,毕竟看得多不如自己多动手亲自去写一遍。

JAVA程序的之所以能够实现一次编译处处运行,得益于他强大的虚拟机,虚拟机会将编译好的字节码文件加载到虚拟机内存中,经过一系列的验证之后,初始化,最后在卸载加载的class文件。日常我们用的最多的就是Sun公司的HotSpot虚拟机,此外还有BEA 的JRockit虚拟机和IBM的J9虚拟机。

闲话不多说,下面图表是一个java虚拟机运行时数据区,图片的来源于http://www.cnblogs.com/AloneSword/p/4262255.html



由图表可以看出方法去和堆区是虚拟机中线程共享的,虚拟机栈和本地方法区以及程序计数器是线程私有的。

程序计数器:

是一块较小的内存空间,它的作用可以看做是当前线程所执行自己吗的行号指示器,在虚拟机的概念模型李,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支,循环,跳转、异常处理、县城恢复等基础功能都需要依赖这个计数器去完成。

由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式实现,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)只会执行一条线程中的指令。因此,为了线程都需要一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,因此我们把这类内存区域称为“线程私有”的内存区域。

若线程正在执行一个java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址,如果执行的native方法(简单地讲,一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。这个特征并非java所特有,很多其它的编程语言都有这一机制,比如在C++中,你可以用extern "C"告知C++编译器去调用一个C的函数。),这个计数器的值就为空,这块区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError的区域。

JAVA虚拟机栈:

和程序计数器一样,java虚拟机栈也是线程私有的。它的生命周期和线程相同。虚拟机栈中描述的是java方法执行的内存模型:每个方法在执行的时候都会同时创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息,每一个方法被调用到执行完成工程就对应这个一个栈帧在java虚拟机栈中从入栈道出栈的过程。



局部变量表:

局部变量表是一组变量值的存储空间,用于存放方法参数和方法内部定义的局部变量。改空间的大小一般在java程序被编译为class文件的时候就会被确定下来。

操作数栈:

顾名思义,他就是java程序在执行方法时候的一个操作数据的栈,是一个后入先出栈。同局部变量表内存空间大小一样,该部分容量在编译的时候已经确定。操作栈中的每一个元素可以是任意的java数据类型,32位数据占用的栈容量为1,64位数据占用的栈空间容量为2.

当方法刚开始执行的时候,这个方法的操作数栈内是空的,在方法执行过程中会有各种字节码指令向操作数栈中写入和提取内容,也是就入栈和出栈操作,例如在做数学运算就是通过操作数栈来进行的。

动态链接方法:

每个栈帧中包含一个指向运行时常量池中该栈帧所属方法的引用(运行时常量池是方法区中的一部分),持有该引用是为了支持方法调用过程中的动态链接。class文件中的常量池中存有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用为参数,这些符号引用一部分会在类加载阶段或者第一次使用的时候转化为直接引用,另一部分将在每一次的运行期间转化为直接引用,这部分称为动态链接。

返回地址:

当一个方法在执行后,有两种方式退出方法,一种是执行引擎遇到任意一个方法返回的字节码指令,这种方法的退出方式为正常退出,另一种是在执行过程中遇到异常退出。但是无论采用哪种方式退出,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行,方法返回的时候需要在栈帧中保存一些信息,用来帮助恢复他的上层方法的执行状态。

本地方法栈:

本地方法栈和虚拟机栈发挥的作用类似,区别就是虚拟机栈执行java方法也就是字节码服务,而本地方法栈执行的虚拟机用到Native方法服务。改区域也是线程私有的。

由于sun公司的HotSpot虚拟机直接把本地方法栈和虚拟栈合二为一,所以在设置该区域大小的时候可以通过-Xss参数进行设置,该区域会抛出StackOverflowError和OutOfMemoryError异常。

JAVA堆:

该区域是java虚拟机管理的内存中最大的一块,java堆是被所有线程共享的一块内存区域,在虚拟机启用时创建,该区域的唯一目的就是存放对象的实例,几乎所有的对象实例都在这里分配,这一点在java虚拟机规范中的描述是:所有的对象实例以及数组都要在该区域进行分配空间。

改取用可以通过参数-Xms和-Xmx进行设置大小。

java堆是进行垃圾收集器管理的主要区域,因此很多时候被称为GC堆,由于现在收集器基本采用的分代收集算法,因此java堆中还可以细分为新生代和老年代,新生代再细致一点就是Eden空间,From Survivor空间和To Survivor空间,他们的分配比例一般是8:1:1,。该区域会抛出OutOfMemoryError异常。



方法区:

和java堆一样,是线程共享的内存区域,它主要用于存放已被虚拟机加载的类信息,常量、静态变量、即时编译器编译后的代码等数据。虽然JAVA虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却又一个别名非堆(Non-Heap)。

运行时常量池:

该区域也是方法区的一部分,它主要指的是在编译期被确定,并被保存在已编译的class文件中的一些数据。除了包含类中定义的一些基本数据(int,long)和对象型(String,Integer)的常量值,还包含一些以文本形式出现的符号引用,比如1:类和接口的限定名,2:字段的名称和描述符,3:方法的名称和描述符。

虚拟机必须为每个装载的类型维护一个常量池。常量池就是该类型用到的一个常量的有序集合,包括直接常量和其他类型,字段,方法和符号引用。对于String常量,她的值存储在常量池中,而JVM的常量池在内存中是以表的形式存在的,对于String类型,有一个固定长度的CONSTANT_String_info表存储字符串的值。

常量池中的项目类型:CONSTANT_Utf8_info 标志1,代表的是UTF-8编码的额字符串;CONSTANT_Integer_info,标志3,整型字面量;CONSTATN_Float_info,标志4,浮点型字面量;

CONSTANT_Long_info,标志5,代表Long类型的字面量;CONSTANT_Double_info,标志6,代表双精度浮点型字面量;CONSTANT_Class_info,标志7,代表类和接口的符号引用;CONSTANT_String_info,标志8,代表字符串类型字面量;CONSTANT_Fielderf_info,标志9,代表字段的符号引用;CONSTANT_Methodref_info,标志10,代表勒种方法的符号引用;CONSTANT_InterfaceMethodref_info,标志11,代表接口中方法的符号引用;CONSTANT_NameAndType_info标志12,代表字段或方法的部分符号引用。

对于在HotSpot虚拟机上开发和部署的程序开发者而言,很多人愿意把它称为永久代(Permanent Generation),但是两者并不等价,仅仅是因为HotSpot虚拟机设计团队选择把Gc分代收集扩展到方法区。

该区域除了和java堆一样不需要选择连续的内存和可以选择固定大小或者可扩展外,还可以选择不实现垃圾收集。相对而言,垃圾收集行为在该区域比较少,但是并非数据进入该区域就会“永久”存在,这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载。

根据java虚拟机规范,当方法区无法满足内存分配需求的时候,将会抛出OutOfMemoryError异常。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  JVM内存