JAVA技术:在虚拟机层面隐藏了底层技术的复杂性以及机器与操作系统的差异性。从而导致一旦出现Java内存区域和内存溢出异常时,如果不了解JVM的工作机制,排查错误将十分艰难。
1. JVM对其管理的内存区域划分
所划分的这些数据区域是线程隔离的。
1)程序计数器
时一块较小的内存空间,可作为线程所执行字节码的行号指示器。JVM的多线程通过其保证线程切换时能够恢复到正确位置,每条线程都需要一个独立的程序计数器,各线程计数器互不影响,独立存储。
2)JAVA虚拟机栈(JVM Stacks)
线程私有的,生命周期与线程相同。虚拟机栈描述的Java方法执行的内存模型:每个方法执行时都会创建一个栈帧以存储局部变量表、操作数栈、动态链接、方法出口等信息
## 局部变量表存放了编译器的基本数据类型、对象引用。局部变量表所需的内存空间在编译期间完成分配,方法运行时不会改变表的大小。当线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError。如虚拟机栈可扩展,但扩展也无法申请到足够内存时,抛出OutOfMemoryError。
3)本地方法栈(Native Method Stacks)
JVM Stacks为JVM执行的Java方法(字节码)服务,而NMS则是为虚拟机使用到的Native方法服务。虚拟机规范并没有对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,具体的虚拟机可以自由的实现它。
4)堆
JVM所管理的内存中最大的一块。堆时被所有线程共享的一块内存区域,在虚拟机启动时自动创建。用于存放对象实例,堆是垃圾收集器管理的主要区域,很多时候称为GC堆。目前收集器多采用分代收集算法,Java堆可以细分为:新生代和老年代。堆可以处于物理上不连续的内存空间中。
5)方法区
各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
## 运行时常量池
是方法区的一部分,用于存放编译期生成的各种字面量和符号引用
2. 对象的访问
Object obj = new Object();
1)Object obj存放于栈的本地变量表中,作为一个reference类型数据
2)new Object() 存放于堆中,形成一块存储里Object类型所有实例数据值得结构化内存,根据具体类型以及JVM实现的对象内存布局不同,这块内存的长度也不固定。
3)此对象类型数据的地址信息(对象类型、父类、实现接口、方法)存储在方法区中。
访问方式:使用句柄或直接指针
1)使用句柄
堆中划分出一块内存作为句柄池,reference中存储的是对象的句柄地址,句柄中包含对象实例数据和类型数据各自的具体地址信息
优点:对象被移动(垃圾回收时移动对象很普遍)只需改变句柄中的实例数据指针,reference本身无需更改
2)直接指针
堆中要考虑如何放置访问类型数据的相关信息,reference中直接存储对象地址
优点:速度快,少了一次定位指针的时间开销