内存模型
主存储器与工作存储器
- 主存储器
- 方法区(Method Area)
方法区用于存储类的信息, 常量, 静态变量, 即时编译器编译后的代码. 当方法区无法满足内存分配需求时, 将抛出OutOfMeoryError. 值得注意的是在方法区中存在一个叫运行时常量池(Runtime Constant Pool)的区域, 用于存放编译器生成的各种字面量和符号引用, 这些内容将在类加载后存放在运行时常量池, 便于以后使用.
- 堆(Heap)
堆在虚拟器启动时创建, 是JVM所管理的内存中最大的一块, 主要用于存放对象实例, 几乎所有的对象实例都在这里进行分配. 这也是GC的主要区域, 因此也教做GC堆, 如果堆中没有内存完成实例分配, 堆也无法进行扩展时, 将会抛出OutOfMemoryErrir异常.
主存储器就是实例位置所在的区域, 所有的实例都在主存储器中. 尤其实例所拥有的字段即位于主存储器内的区域, 主存储器为所有的线程所共有.
- 工作存储器
- 程序计数器(Program Counter Register)
代表当前线程所执行的字节码行号指示器. 字节码解释器工作时, 通过改变PC的值来选取下一条需要执行的字节码指令, 分支, 循环, 跳转, 异常处理, 线程恢复等基础功能都需要依赖PC.
- 栈(Java Virtual Machine Stacks)
代表着Java方法执行的内存模型, 每个方法执行时都会创建一个栈帧来存储方法的变量表, 操作数栈, 动态链接方法, 返回值, 返回地址等信息. 每个方法从调用到结束就对应于栈帧在栈的入栈出栈.
- 本地方法栈(Native Method Stacks)
本地方法栈与虚拟机栈作用类似, 只是前者为本地方法服务, 后者为Java方法服务. 在某些JVM中, 这两者是合二为一的.
工作存储器为各个线程所专有. 在工作存储器内, 存在有主存储器中必要部分的拷贝, 称之为工作拷贝.
需要注意的是, JMM本身是一种抽象的概念, 并不是真实存在的. 他描述的是一组规则或规范, 通过这组规范定义了程序中各个变量(包括实例字段, 静态字段和构成数组对象元素)的访问方式.
字段引用
线程无法对主存储器直接进行操作, 因此也无法直接引用字段的值, 当线程希望引用字段的值时, 会将值从主存储器拷贝到工作存储器上. 通过此次拷贝的值, 会成为工作拷贝. 拷贝完成后, 线程就会引用工作拷贝. 当线程再次引用同一个字段的值时, 可能会使用刚才的工作拷贝, 也可能重新进行一次工作拷贝. 这具体是由Java的执行系统决定.
同样的, 对于将值更新到字段的过程, 也是先将值赋给工作拷贝, 再由工作拷贝映像到主存储器上. 至于何时映像, 也是有Java执行系统决定.