本文由larrylgq编写,转载请注明出处:http://blog.youkuaiyun.com/larrylgq/article/details/7395261
作者:吕桂强
邮箱:larry.lv.word@gmail.com
jvm中所有线程共享的数据区有方法区,堆。线程隔离的数据区有虚拟机栈,本地方法栈和程序计数器。
程序计数器:
是用于当前线程执行字节码的行号指示器,是一块很小的内存空间,每个线程都有一个各线程计数器互不影响,独立存储。在工作时通过改变计数器的值来选取下一条需要执行的字节码指令,从而实现分支,循环,跳转,异常处理,线程恢复等基础功能,当执行Native方法时计数器的值为空。
java虚拟机栈:
线程私有,生命周期与当前线程相同,线程中每个方法执行的时候会同时创建一个栈帧(stack frame)来存储局部变量表,操作数栈,动态连接,方法出口等信息,方法执行完成,该栈帧会在虚拟机栈中移除。
其中局部变量表存放了编译器可知的8中基本数据类型,对象引用,和returnAddress(指向一条字节码指令的地址)
注意:(与其它语言尾递归相比较)
1:当线程请求的栈深度大于虚拟机的最大允许深度会抛出StackOverflowError
2:如果虚拟机栈可以动态扩展,当扩展时申请不到足够的内存会抛出OutOfMemoryError
本地方法栈:
和虚拟机栈相似,不过为虚拟机使用到的Native方法服务,(Sun HotSpot虚拟机直接将本地方法栈和虚拟机栈合在一起)
java堆:
所有线程共享的一块内存区域,在虚拟机启动时创建,用于存放对象实例。java堆处于物理上不连续的内存空间,只需要在逻辑上是连续的即可,目前主流的虚拟机是按照扩展的方式实现(通过-Xmx和-Xms控制),当堆中没有内存进行实例分配且无法扩展时会抛出OutOfMemoryError
方法区:
堆的逻辑部分,各个线程共享的内存区域,用于存储被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等,该区域的内存回收主要是常量池的回收和类型的卸载。当方法区无法内存分配时将跑出OutOfMemoryError
运行时常量池:
用于存放class文件中的常量池(存放编译生成的字面量和符号引用http://blog.youkuaiyun.com/larrylgq/article/details/7240329),具有动态性,运行期间也可能将新的常量放入池中eg:String的intern()
直接内存:(Direct Memory)
jdk1.4之后使用非直接缓冲区时会使用Native函数库直接分配堆外内存,然后通过java堆里的DirectByteBuffer对象作为这块内存的引用来进行操作,可以避免在java堆和Native堆中来回复制数据
对象存放:
java中最简单的访问也要涉及java栈,堆,方法区3个最重要的内存区域,
eg:
方法体中出现下列代码Object obj = new Object();
其中Object obj作为一个reference类型数据存放在java栈的本地变量表中
new Object()部分则存放在堆中,存储Object类型的对象中所有实例字段数据的结构话内存
另外在堆中还需要查找到此对象类型,弗雷,实现接口,方法的地址信息,这些存放在方法区中
由于reference类型在jvm规范中只规定了指向对象的引用,没有定义定位的方式,因此一般虚拟机多采用句柄方式或者直接指针方式来定位
sun hot spot使用指针方式来定位即reference中直接存储的就是对象地址