JVM:JVM 内存分区

Java 虚拟机在执行Java 程序的过程中会把它所管理的内在划分为若干个不同的数据区域。这些区域都各有用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁。
(来自:《深入理解Java虚拟机》)

1.程序计数器
    这是一块相对来说比较小的内存空间,它的作用是用来存储线程执行到哪个字节码的行号。程序运行时就是不断地执行字节码,不断地改变该空间中的存储的行号。在多线程环境中,由于每一个线程执行到的位置都不会时刻相同的,所以程序计数器保存的是每一条线程各自的字节码行号,也就是说,程序计数器是线程私有的。如果线程当前执行的是一个Java方法,那计数器记录的就是虚拟机字节码指令的地址;如果正在执行的是Native 方法,那这个计数器值则为空。(来自:《深入理解Java虚拟机》)
    在执行Native 方法的时候计数器的值为空了,那有没有疑问它是怎么往下走的呢?都不知道下一步到哪行了。其实我也觉得它表达得不清楚,其实,在执行Native 方法的时候是新起一条线程来执行的,该条线程上的计数器值为空,这么说才对。原来的线程就会阻塞,直到执行Native 方法的那条线程完结后原来的线程才继续执行下去。
    此内在区域是唯一一个在 Java 虚拟机规范中没有规定任何 OutOfMemoryError (内在溢出错误)情况的区域。(来自:《深入理解Java虚拟机》)

2.Java 虚拟机栈
    虚拟机栈也是线程私有的,生命周期与线程相同。虚拟机栈是用于存储方法执行时所产生的:局部变量表、操作数栈、动态链接、方法出口等。
    正如其名,该内存中所用到的数据结构就是栈,所有相关操作也就是栈的操作。上面所提到的存储内容就是存储在一个个的栈桢中的。每个方法都有自己的栈桢,栈桢入栈那就方法开始执行,出栈就是方法执行完成。栈是线程私有的嘛,每条线程都有自己的一条栈。在活动的线程中,只有栈顶的栈桢是有效的,称为当前栈。
    局部变量表:编译期可知的各种基本数据类型(boolean 、byte、char、short 、int、float、long、double)、对象引用和returnAddress (指向了一条字节码指令的地址)。这些都是在编译的时候就确定的,虚拟机使用局部变量表完成参数值到参数变量列表的传递过程。也就是说,局部变量区是一个经字长为单位的数组,访问是通过索引进行的。如果是实例方法,那么局部变更表的第0位索引默认是用来传递方法所屋对象的实例的引用,在方法中通过this 访问。系统不会为局部变量赋予初始值,所以我们在方法中定义局部变量的时候一般都是要给它赋值的。
    操作数栈:这个地方是干嘛用的呢,我们的变量有地方存了,但我们执行方法时临时的东西没地方放呢。比如一条计算公式:int i=0;i=3+4;3+4这一步是怎么算出来然后再赋值级i的呢,这就要靠操作数栈了。  操作数栈就是虚拟机的工作区。它虽说和局部变量区一样用的也是以字长为单位的数组,但它并不是靠索引来访问的,然后真正的栈操作--出栈各压栈(每一次都是在栈顶操作)。当解释到 i=3+4这一步时发现两个临时数 值3、4,于是是把它两压到栈中,然后收到指令是加法运算,就是将3、4从栈中弹中进行运算,得到7把压入栈中,然后遇到要给i进行赋值的指令,再把7从栈中弹出放到局部变量区中去。通过以上形象的描述是不是明白了操作数栈的作用了啊。
    动态链接:我们知道class 文件是源代码经过编译后得到的字节码。到了运行的时候再从字节码中取到类然后将其生成。何为动态链接,就是我用到哪个类,就将哪个类加载到内在中,在这里当然就是要用到哪个方法就加载哪个方法,如何定位到该方法呢,在一个叫常量池的地方保存着这个方法的符号引用,找到这个符号引用就能找到方法了,这个引用就是动态链接。
    
3.本地方法栈
    本地方法栈发挥的作用与虚拟机栈是相似的,区别只是虚拟机栈是为Java 方法服务,而本地方法栈是为本地方法(native 方法)。有些虚拟机(比如 sun sotspot )就直接将本地方法栈与虚拟机栈合二为一了。
    
4.Java堆
    堆是虚拟机中管理的最大的一块内在了。Java 堆被所有线程共享,它在虚拟机启动的时候就创建了。它只有一个目的,那就是存放对象的实例(包括数组)。堆的大小是可以扩展的,通过 -Xmx 和 Xms 来控制。(Xms 表示初始分配的堆内存,也就是程序初始化的时候,给你分配的堆内存的大小,Xmx 表示允许的最大的堆内存,也就是说堆内存不够的时候你给你继续分配,直到达到这个最大值为止,但无论怎么样,这个值都不应该超过物理上的内存值)

5.方法区
    这个区域也是线程共享的,它存储着类的相关信息,常量,静态变量,即时编译后的代码等数据。

6.运行时常量池
    运行时常量池是方法区的一部分。Class 文件除了有类的版本、字段、方法、接口等描述的信息名,还有一项信息是常量池,用来存放编译期生成的各种字面量和符号引用。这部分内容将在类加载后就进入方法区的运行时常量池中存放。(来自:《深入理解Java虚拟机》)

7.直接内存
    在JDK1.4后加入一个NIO类,它是基于通道和缓冲区的I/O方式,它可以使用Native 函数库直接分配堆外面的内存,然后通过存储在堆中的DirectByteBuffer 对象作为这块内存的引用进行操作。避免了Java 堆 和 Native 堆中来回复制数据。

猜你喜欢

转载自blog.csdn.net/mottohlm/article/details/79201589