JAVA虚拟机运行时数据区
java 虚拟机在执行java程序的过程中,会划分为不同功能作用的数据区域:
同虚拟机进程生命周期的数据区域:方法区、堆
同java线程生命周期的数据区域:虚拟机栈、本地方法栈、程序计数器
一、程序计数器
程序计数器(program counter register)是一块较小的内存空间 ,可以看作是当前线程执行字节码的行号指示标,字节码解释器、程序的控制流程 If、while、for 等指令的执行也是依赖程序计数器完成。
多条线程在单核cpu环境下,单核cup同时只能执行一个线程(涉及到cpu时间片、上下文切换),线程切换就是通过程序计数器记录执行位置。
如果当前线程执行java方法,程序计数器记录虚拟机指令字节码指令的地址;如果执行的是Native方法,这个计数器值为空。
程序计数器是唯一一个不会发生内存溢出的区域。
如果是native方法,计数器为空解释:
当执行java方法是,”pc寄存器“执行的是字节码指令地址,实现上可能有两种方式存储:1.字节码地址的偏移量,叫 bytecode index,2.存放java字节码内存地址,叫
bytecode pointer。对于native方法而言,不是由java字节码构成,所以,jvm规范,如果是native方法,程序计数器的值未定义--可以是任何值。
native 方法执行:
Java线程总是需要以某种形式映射到OS线程上。映射模型可以是1:1(原生线程模型)、n:1(绿色线程 / 用户态线程模型)、m:n(混合模型)。
以HotSpot VM的实现为例,它目前在大多数平台上都使用1:1模型,也就是每个Java线程都直接映射到一个OS线程上执行。此时,native方法就由原生平台直接执行,并不需要理
会抽象的JVM层面上的“pc寄存器”概念——原生的CPU上真正的PC寄存器是怎样就是怎样。就像一个用C或C++写的多线程程序,它在线程切换的时候是怎样的,Java的native方
法也就是怎样的。
java 堆是虚拟机占用内存最多的一块区域,也是所有线程共享的一块区域,主要用于创建对象实例,虚拟机所有对象都会在堆上创建。
java 堆也是垃圾回收处理区域,从垃圾回收角度,现代收集器都是分代回收,分为新生代和老年代;从内存区域角度,将java堆分成多个线程私有的分配缓冲区,但是存储内容没有变化,仍然存对象实例。进一步划分是为了更好的创建实例和更好的回收内存。
根据java虚拟机规范,java堆可以是连续或者不连续的内存空间,类似于磁盘空间,java堆内存空间是可宽展的,可以通过(-xms,或-xmm)控制,当对象分配的空间大于扩展后的内存 空间,抛出 outOfMemoryError 异常。
五、方法区
方法区是存放已被虚拟机加载的类、变量、静态变量、即时编译后的代码信息,方法区也是线程共享的区域。
java虚拟机规范把方法区描述为堆的一个逻辑部分,但方法区有别名叫“非堆”,目地是为了与堆区分。
java虚拟机规范对方法区的限制还是比较宽松的,可以像堆一样不需要连续的内存空间,并且可扩展,垃圾回收也是可选择的,这个区域主垃圾回收的主要目标是常量池的
回收和对类型的卸载,若不选择垃圾回收,也不是像“永久代”名字一样数据永久不回收,只是回收规则比较苛刻,回收不理想,为避免造成OutOfMemory异常,还是有必要使用垃圾回收器。