运行时数据区域
程序计数器
当前线程执行字节码的行号指示器。在虚拟机的概念模型里,通过这个行号指示下一条要执行的指令,分支、循环、跳转、异常处理、线程恢复等。线程独立。当执行java方法的时候,指示的是虚拟机字节码指令的地址;执行native方法是,这个值为空。
唯一一个没有在虚拟机规范中规定任何OutOfMemoryError的区域。
虚拟机栈
线程私有。栈帧的入栈和出栈过程。每个栈帧存储局部变量表、操作数栈、动态链接库、方法出口等信息。可以抛出StackOverflowError异常,栈本身不够用;OutOfMemoryError,扩充栈的时候没有申请到足够的空间。
本地方法栈
运行native方法服务。也会抛出上述两个异常。
堆
虚拟机管理的最大一块内存。虚拟机规范要求,所有对象实例以及数组都要在堆上分配。分代:新生代和老年代;新生代分为Eden区和两个Survivor区。规范规定,堆可以处于物理上不连续的内存空间中,只要逻辑上连续即可,就像磁盘一样。当堆中没有足够的内存分配,且堆无法扩展的时候,将会跑出OutOfMemoryError异常。
方法区
线程共享,存储被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。HotSpot将其称为永久代,是因为GC分代收集扩展至方法区。回收目标针对常量池的回收和类型的卸载。也会跑出OutOfMemoryError异常。
运行时常量池
方法区的一部分,存放各种字面量和符号引用。String类的intern()返回这里的值。同意可能返回OutOfMemoryError异常。
直接内存
Direct Memory并不是虚机运行时数据区的一部分,也不是虚拟机规范定义的内存区域。堆内的DirectByteBuffer对象对堆外内存引用进行操作,避免在java堆和native堆间来回复制数据,一些场景可以提高性能。
对象访问
对象类型信息存储在方法区。
栈和堆之间的访问方式一般两种:1.使用句柄,reference中存储的是稳定的句柄地址,对象移动时只会改变句柄中的实例数据指针;2.使用之间指针,节省一次指针寻址。