Java虚拟机内存区域分为五部分:程序计数器、Java虚拟机栈、本地方法栈、堆、方法区。其中程序计数器、Java虚拟机栈、本地方法栈属于线程私有内存区,其生命周期与线程相同,随线程的产生而产生,随线程的消亡而消亡。这几个区域的内存在方法或线程结束时,天然就跟着回收了。所以这三个区域的内存分配和回收具备肯定性,不须要考虑内存回收的问题。而Java堆和方法区属于线程共享内存区,全部线程均可以访问。只有在程序运行期间才能知道建立哪些对象,所以这两个区域的内存分配和回收都是动态的,也是垃圾收集器收集的主要区域。html
下面分别介绍这五个区域。程序员
程序计数器是一块较小的内存空间,能够当作线程执行的字节码行号指示器。字节码解释器工做的时候就是经过改变程序计数器的值,来选取下一条所要执行的字节码指令,分支、跳转、循环、异常处理、线程恢复等都须要依赖程序计数器来完成。程序计数器是Java虚拟机中惟一不会发生内存溢出(OutOfMemoryError)的区域。数组
Java虚拟机的多线程是经过线程间的轮流切换并分配处理器执行时间的方式来完成的,任意时刻一个处理器只会执行一条线程中的指令。为了使线程切换后能恢复到正确的执行位置,每一个线程都有一个独立的程序计数器,各线程之间独立存储、互不影响,所以是线程私有内存区。多线程
Java虚拟机栈描述的是Java方法执行的内存模型:每一个方法在执行时都会建立一个栈帧用于存储局部变量表、操做数栈、动态连接、方法出口等信息。每一个方法从调用到执行结束都伴随着一个栈帧在虚拟机栈中从入栈到出栈的过程。学习
Java虚拟机栈也是线程私有内存区,生命周期与线程相同。基于栈的内存区域均可能发生两种异常:栈溢出和内存溢出。若是线程请求栈的深度大于虚拟机栈所容许的最大深度,而且没法扩展时,将会发生栈溢出(StackOverflowError)异常;若是能够扩展,在扩展时没法申请到足够的内存,将会发生栈溢出(OutOfMemoryError)异常。spa
局部变量表用于存放方法参数和方法内部定义的局部变量,其数据类型是编译期可知的各类基本数据类型、对象引用类型(reference)和返回(returnAddress)类型(它指向了一条字节码指令的地址)。局部变量表所需的内存空间在编译期间完成分配,即在Java程序被编译成Class文件时,就肯定了所需分配的最大局部变量表的容量。当进入一个方法时,这个方法须要在栈中分配多大的局部变量空间是彻底肯定的,在方法运行期间不会改变局部变量表的大小。操作系统
本地方法栈与虚拟机栈所发挥的做用很是类似,只是虚拟机栈为虚拟机执行Java方法服务(即程序员本身写的方法),而本地方法栈则为使用到的本地操做系统(Native)方法服务。本地方法栈也是基于栈,所以也会发生栈溢出或内存溢出异常。线程
Java堆是虚拟机所管理的最大一块内存,是全部线程共享的一块内存区域,在虚拟机启动时建立。Java堆用来存放对象实例,几乎全部的对象实例和数组都在这里分配内存。Java堆是垃圾收集器管理的主要区域,所以不少时候也被称为“GC堆”。htm
Java堆能够处于物理上不连续的内存空间中,只要逻辑上连续便可。在实现时,既能够是固定大小的,也能够是可扩展的,目前主流虚拟机都是可扩展的。若是在堆中没有足够的内存可分配,且没法扩展时,将会抛出内存溢出异常。对象
从垃圾收集的角度来看,Java堆还能够细分为新生代和老年代。新生代包括Eden、Survivor from、Survivor to,详情请见下面下面一章。
方法区也是线程共享的内存区域,用于存储被虚拟机加载的类的信息、常量、静态变量、即时编译器编译后的代码等数据。
和堆同样,方法区一样能够处于物理上不连续的内存空间中,只要逻辑上连续便可。在实现时,既能够是固定大小的,也能够是可扩展的。方法区能够不进行垃圾收集,相对而言,方法区的垃圾收集行为不多,主要是对常量池的回收和对类型的卸载。当方法区没有足够的内存完成分配时,会发生内存溢出异常。
平时所说的“永久代”(PermGen space)指的是方法区。
“运行时常量池”是方法区的一部分,用于存放编译期生成的各类字面量和符号引用。运行时常量池具备动态性,常量既能够在编译期产生并进入常量池,也能够在运行期间进入常量池,常见的是String类的intern()方法。运行时常量池是方法区的一部分,所以也会发生内存溢出异常。
转载请注明出处 https://www.cnblogs.com/Y-oung/p/9762682.html
工做、学习、交流或有任何疑问,请联系邮箱:yy1340128046@163.com