Nivelle 开拓视野冲破艰险看见世界 身临其境贴近彼此感受生活

jvm内存区域

2017-10-11
nivelle
jvm

jvm体系结构

JVM的全称是java Virtual Machine(java虚拟机),它通过模拟一个计算机来达到一个计算机所具有的计算功能.JVM和实体机一样也必须有一套合适的指令集,这个指令集能够被JVM解析执行.这个指令集我们称为JVM字节码指令集,符合class文件规范的字节码都可以被JVM执行.

image

jVM运行时,首先需要类加载器加载所需要的字节码,加载完毕后由执行引擎执行,执行过程中需要一段空间来存储数据(类比CPU与主存).这段内存空间的分配和释放过程正是我们要关注的,称为运行时数据区.

运行时数据区

如上图,运行时数据区包括:

  • 程序计数器(即PC寄存器)

每条虚拟机中的线程都有自己的寄存器,称之为程序计数器(pc).为了保证线程之间的独立性,因而pc内的空间是私有的. 线程私有: 只有本线程访问的区域,其他线程无权访问.

程序计数器的作用: 虚拟机中的多线程通过线程轮转调度,为每条线程分配时间片来实现并发执行.同一时刻,处理机只能执行一条线程.当切换到另外一条线程时,若不保存当前未执行完线程的执行位置,下次处理机再执行这条线程时,又要重新开始执行.

程序计数器的机制: 记录正在执行的虚拟机字节码指令的地址.如果运行的是Native方法,计数器的值为Undefined.

程序计数器是唯一没有OutOfMemoryError异常的区域.

  • java虚拟机栈(VM Stack)

每个Java方法执行时,需要分配内存空间来存储局部变量表,操作数栈,动态链接,方法出口灯信息.将这部分内存称之为栈帧(Stack Frame).虚拟机栈用于存储栈帧,是java方法执行的内存模型.

显然我们需要为每个执行的方法分配栈空间,因此java虚拟机栈也是线程私有的.

虚拟机栈的作用: 虚拟机栈记录java方法执行的过程.每个方法开始执行时,为之创建一个栈帧记录信息;方法执行到完成的过程,对应栈帧在虚拟机栈中入栈到出栈的过程.

image

局部变量表:局部变量表是栈中的重要部分.存放编译期定义的基本数据类型,对象引用(相当于对象地址),以及returnAddress类型(字节码指令地址)

局部变量表空间在编译期间分配,执行方法的过程不会改变其大小.

异常:

  1. 线程请求的栈深度大于所允许的深度,抛出StackOverflowError异常
  2. 长度不够时,虚拟机栈可进行动态扩展,申请内存.若无法申请到足够的内存,抛出OutOfMemoryError异常.

本地方法栈: 本地方法栈与虚拟机栈类似,区别是虚拟机栈记录执行的java方法,本地方法栈记录Native方法.

本地方法同样会抛出StackOverflowError与OutOfMemoryError异常.

  • java 堆(heap)

java堆用于存储对象实例,为所有对象分配内存空间.

所有对象实例都要在堆上分配空间,因此java堆是所有线程的共享区域.对象的生命周期结束后,java堆还要负责内存回收,因此java堆也常称为GC堆(Garbage Collected Heap).

java堆得内存模型: 从内存回收角度,java堆可以分为新生代(yong Generation)与老生代(old Generation).这种划分的方式是为了更好低回收内存(老生代内存会被优先回收)

image

如上图,新生代分为Eden空间,From Survivor空间,To Survivor空间.

永久代(Permanent Generation)用于存储静态类型数据,与垃圾收集器关系不大.

注意:本图展示的是JVM堆的内存模型,JVM堆内存包括Java堆区域和永久代区域.因此,永久代不属于java堆.

异常: java堆同样可拓展(-Xmx与-Xms参数)

方法区: 方法区存储类信息,常量,静态变量等数据,是线程共享的区域.为与java堆区分,方法区还有一个别名Non-Heap(非堆).垃圾收集在这个区域比较少出现,但是并非数据进入了这个方法区就如永久代的名字一样“永久存在”,这个区域的内存回收主要目标是针对常量池的回收以及类型的卸载,这个区域的回收成绩比较令人满意,尤其是对类型的卸载,条件相当苛刻。

方法区不等于永久代

HotSpot虚拟机选择用永久代来实现方法区,从而省去了为方法区编写内存管理代码的工作.这只是一种实现方式,其他虚拟机(BEA JRockit,IBM J9)都不存在永久代这一概念.

通过永久代来实现方法区容易造成内存溢出,未来也可能会被替代.在虚拟机规范中,方法区的实现没有明确的规定,因此不能将方法区等同于永久代.

异常: 当方法区无法满足内存分配的需要时,抛出OutOfMemoryError异常.

运行时常量池: 运行时常量池(Runtime Constant Pool)用于存放编译器生成的各种字面量和符号引用.运行时常量池具备动态性,使得运行期间也可将新的常量放入池中.例如String的intern()方法.当常量池无法再申请到内存的时候便会抛出OutOfMemoryError异常。


public class Main1 {
    public static void main(String[] args) {
        String s0= "I'm coding";   
        String s1=new String("I'm coding");   
        String s2=new String("I'm coding");   
        System.out.println( s0==s1 );  
        System.out.println( s0==s1.intern());   
        s2=s2.intern();  
        System.out.println( s0==s2 );   
          
    }
}

输出结果:

false
true
true

本例中,s0直接保存在常量池,s1与s2的对象实例存储在java堆中. == 直接比较对象的hashCode,因此第一行输出false.s1.intern()方法返回s1在常量池中的引用,没有则创建.s1存放的字符串已经在常量池中存在,直接返回s0的引用,第二行输出true.同理,s2接收了s2.intern()的返回值,字符串值与s0相同,第三行输出true.

运行时常量池是方法区的一部分,因此受方法区内存的限制.当无法申请到内存时,抛出OutOfMemoryErroe异常.

  • 本地方法栈(Native Method Stack)

转发至 http://www.cnblogs.com/ACFLOOD/p/5608292.html


下一篇 应用级缓存

评论