JVM内存结构

​ java虚拟机在运行过程中会将其所管理的内存划分为多个区域,根据各自用途可以大体划分为以下四个区域:方法区程序计数器,其中栈又可分为本地方法栈虚拟机栈,下图可以直观的显示内存中各个区域的划分情况。

mark

程序计数器

​ 程序计数器在内存中只占了很小一部分,用来指向下一条指令的地址,字节码解释器在执行过程中根据程序计数器的值选取执行的指令。

​ java通过在不同线程之间切换来执行多线程任务,同一个处理器同一时刻只能处理一个线程,为了保证切换之后能获取之前的执行状态,因此每个线程都拥有一个单独的程序计数器,多个线程之间的计数器互不影响。

​ 若当前线程执行的是java方法,程序计数器的值为所执行指令的地址;若执行的native方法(非java代码),则值为undefined。

​ 此区域是唯一一个不存在OutOfMemoryError的区域。

堆(heap)

​ 堆是虚拟机管理的内存中最大的一块,在虚拟机启动时即被创建,所有线程共享该区域。堆上存放对象的实例和数组。主流虚拟机堆内存的大小都是可调节的(通过-Xms和-Xmx控制),若堆中没有内存可供实例分配,将抛出OutOfMemoryErroe错误。

​ 堆是垃圾管理器管理的主要区域,由于现在的GC基本采用分代收集算法,因此堆还可以分为:新生代老年代,新生代又可细分为EdenFrom SurvivorTo Survivor。(见参考文章 1)

方法区

​ 方法区又叫静态区域,与堆一样,所有线程共享该区域。方法区存放被虚拟机加载的类的信息常量静态变量编译后的代码,当方法区无法满足内存分配时,将抛出OutOfMemoryErroe错误。

运行时常量池

运行时常量池是方法区的一部分,受方法区内存限制,内存不足时将会抛出OutOfMemoryErroe错误。要理解运行时常量池需要理解以下三个概念:

  • 常量池(Constant Pool):常量池数据编译期被确定,是Class文件中的一部分。常量池中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic Reference)。
    • 字面量:文本字符串、声明为final的常量值等。
    • 符号引用:类和接口的完全限定名(Fully Qualified Name)、字段的名称和描述符(Descriptor)、方法的名称和描述符。
  • 字符串池/字符串常量池(String Pool/String Constant Pool):是常量池中的一部分,存储编译期类中产生的字符串类型数据。
  • 运行时常量池(Runtime Constant Pool):方法区的一部分,所有线程共享。虚拟机加载Class后把常量池中的数据放入到运行时常量池。

mark

java虚拟机栈

​ 和程序计数器一样,虚拟机栈也是线程私有的,生命周期跟线程相同。虚拟机栈表示Java方法执行的内存模型,每调用一个方法就会为每个方法生成一个栈帧(Stack Frame),用来存储局部变量表、操作数栈、动态链接、方法出口等信息(见参考资料4)。每个方法被调用和完成的过程,都对应一个栈帧从虚拟机栈上入栈和出栈的过程。

mark

本地方法栈

​ 作用和虚拟机栈类似,虚拟机栈执行java方法,本地方法栈执行native方法(非java代码)。

参考资料

  1. https://www.cnblogs.com/E-star/p/5556188.html
  2. http://blog.csdn.net/bluetjs/article/details/52874852
  3. http://blog.csdn.net/sunshine__me/article/details/49992909
  4. https://www.cnblogs.com/Codenewbie/p/6184898.html
  5. 《深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)》