您的位置:首页 > 编程语言 > Java开发

JVM虚拟机中的运行时数据区域概念描述

2016-09-26 00:00 197 查看
java虚拟机所管理的内存包含以下几个运行时数据区域

1.方法区(method Area) 2.虚拟机栈(vm stack) 3.本地方法栈(native method stack) 4.堆(heap) 5.程序计数器

1和4是由所有线程共享的数据区 2,3,5是线程隔离的数据区

程序计数器:

这是一块很小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。

在jvm的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。

另外 分支 循环 跳转 异常处理 线程恢复等基础功能都需要他来完成。

JVM多线程实现:通过线程轮流切换并分配处理器执行时间的方式来实现的。

所以为了切换换成后能恢复到正确的执行位置,每条线程都要有一个独立计数器,互不影响,独立存储,我们称这类内存区域为线程私有的内存。

如果线程正在执行java方法,则计数器记录的是正在执行的虚拟机字节码指定地址。如果正在执行native方法,则计数器值为空。

程序计数器是jvm中没有规定内存溢出错误的区域。

栈:

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

栈被称为局部变量表。存放各种基本数据类型,对象引用和returnAddress类型(指向一条字节码指令的地址)

64位的long和double会占用2个局部变量空间slot,其余数据类型占1个。栈需要的内存空间,编译时期就完成分配。

jvm规范中定义的两种异常:如果线程请求的栈深度大于虚拟机锁允许的深度将抛出stackOverflowError异常。如果栈拓展时无法申请到足够内存,就会抛出内存溢出异常。

本地方法:

他和栈发挥的作用差不多,区别是栈为虚拟机执行java方法服务,本地方法栈为虚拟机使用到的native服务。本地方法栈和栈抛出的异常一样。

堆:

java堆是jvm虚拟机管理的内存中的最大一块。是被所有线程共享的一块内存区域。在虚拟机启动时创建,目的是存放对象实例。几乎所有的对象都在这里分配内存。jvm虚拟机规范中说明:所有的对象实例以及数组都要在堆上创建。

java堆是垃圾收集器的主要区域。也被成为GC堆。收集器现在都采用分代收集算法。所以java堆细分一下为:

新生代和老年代。更细致的有Eden空间,From Survivor空间,To Survivor空间。

线程共享的java堆中可能划分出多个线程私有的分配缓冲区。

java堆可以处于物理上不连续的内存空间中,逻辑上是连续的即可。他的大小是有-Xmx 和 -Xms 控制。

如果在堆中没有内存完成实例分配,并且堆也无法拓展时,会抛出内存溢出。

经典问题:堆和栈,其实java中不止这两个内存区域。

方法区:

方法区和堆差不多,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。它也叫 non-heap 非堆。

HotSpot中设计团队将GC分代收集拓展到了方法区,这样容易遇到内存溢出情况,永久代有-XX:MaxPermSize上限。JDK7貌似正在使用native Memory管理方法区。

运行时常量池:

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

运行期间也可以将新的常量放入池中。

直接内存:

直接内存不属于jvm运行时内存数据区。

JDK1.4z中新加入的NIO 引入了一种基于通道与缓冲区的IO方式,它可以使用native函数库直接分配堆外内存。然后通过一个存储在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。

HotSpot虚拟机对象探秘:

虚拟机中对象的创建:

虚拟机遇到一条new指令时,首先将检查这个指令的参数是否能在常量池中定位到一个类的符号引用。并且检查这个符号引用代表的类是否已经被加载,解析和初始化过。如果没有,要先执行相应的类加载过程。类加载检查通过后,jvm将为新生对象分配内存,对象所需内存大小在类加载完成后便可完全确定。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  jvm java