Java虚拟机在运行Java程序时会把它管理的内存划分为多个区域,这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动一直存在,有些区域则是依赖用户线程的启动和结束而建立与销毁。

1.程序计数器:可以看作当前线程所执行的字节码的行号指示器,通过改变这个计数器的值来选取下一条需要执行的字节码指令;它是程序控制流的指示器,分支,循环,跳转,异常处理,线程恢复等基础功能都要依赖这个计数器来完成。

  Java虚拟机的多线程是通过线程的切换轮转,分配处理器执行时间的方式实现的;在任何一个确定的时刻,一个处理器都会执行一条线程中的指令,因此每个线程切换后要回到原来的执行位置,每条线程都需要一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,线程私有。

2. 虚拟机栈:它的生命周期与线程相同,为线程私有,每个方法执行的时候都会同步创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息,每个方法执行完毕的过程对应着一个栈帧在虚拟机中冲入栈到出栈的过程。

  局部变量表存放了编译期可知的java8中基本类型,对象的引用(reference不同于对象本身,它可能指向对象的起始地址的引用指针,可能指向一个代表对象的句柄或其他与次对象相关的位置和返回地址类型),这些数据类型在局部变量表中的存储空间以局部变量槽来表示,其中long和double类型数据会占据2个变量槽,其余占据一个;局部变量表所需的内存空间在编译期完成分配,当进入一个方法时,这个方法需要在栈帧中分配多大的局部变量空看是完全确定的,在方法运行期间不会改变局部变量表的大小,这个大小指的是变量槽的数量。

3 本地方法栈 :是虚拟机用到本地的native方法

4 java 堆:堆是被所有线程共享的一块内存区域,该区域唯一目的就是存放对象实例(由于即时编译技术的进步,尤其是逃逸分析技术的强大,栈上分配,标量替换优化手段已经导致一些微量的变化,对象实例都分配在堆上变的不是那么绝对)

5 方法区:各个线程共享,用于存储已经被虚拟机加载的类型信息,常量,静态变量,即时编译后的代码缓存等数据。说到方法区,就要说到“永久代”,JDK8以前,方法区被很多人叫做永久代,其实是虚拟机设计团队按照分代收集的设计理念扩张至方法区,让收集器能象管理堆一样来管理方法区,但对于其他虚拟机实现,如BEA JRockit,IBM J9等来说,是不存在永久代的概念的,hotpost在JDk7时已经把字符串常量池和静态变量移出,在JDK8后也放弃了永久代,改用本地存储的元空间来替代(因为这个更容易遇到内存溢出)。

6 运行时常量池:是方法区的一部分,class文件中除了包含类的版本,字段,方法,接口等描述信息外,还有一项信息是常量池表,用于存放编译器生成的各种字面量符号引用,这部分内容将在类加载后放到方法区的运行时常量池中;

  运行时常量池相对于Class文件常量池来说还有一个重要特征,具备动态性,并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间可以将新的常量放入池中,例如:本地方法String:intern方法

7 直接内存:它并不是虚拟机运行时数据区的一部分,但这部分也被频繁的使用,而且也可能导致内存溢出,JDK1.4后引入了NIO,一种基于通道(channel)和缓冲区(Buffer)的I/O方式,通过一个存储在java堆中的DirectByteBuffer对象为这块内存的引用进行操作,在某些场景中,因避免了在java堆和native堆中来回复制数据,而显著提升效率。