本文内容基于个人对特定技术的理解和实践,或为对相关技术内容的整理与分享。请读者批判性阅读,如有疏漏或不准确之处,恳请斧正。
接续 2.JVM的类加载机制
类被加载到内存后,会保存在方法区或元空间中,根据JVM的划分,这部分内存归属于为线程共享的内存空间。
以下JVM内存空间是按照JVM Spec 中线程访问的规则进行划分(直接内存不属于JVM Spec):
线程共享的内存区域
- 堆内存
创建的对象和数组一般分配在堆内存中,所以在多线程下访问对象的时候需要确保线程安全。堆内存设置为动态的,其空间是由GC进行处理的,为对象分配内存时,如果堆内存占用满了,并且GC还无法收回需要的空间,就会触发OOM。 - 方法区/元空间
其中存储着每个类的结构,例如运行时常量池、字段和方法数据以及方法和构造函数代码。JIT compiled code 也是该区域的一个重要内容。这个区域可以选择是否为动态大小以及是否可以被垃圾回收(针对常量池的回收和对类型的卸载)。在逻辑上方法区与堆的性质是一样的,如果内存空间不足以分配,也会触发OOM。 - 堆外内存
包含直接内存以及JVM使用的内存(例如 元空间),可以避免JVM管理、回收对象所造成的资源、性能的浪费。避免了数据在Java堆和操作系统本地缓冲区之间的来回复制,提高了I/O效率(即零拷贝)。如果堆外内存不足以支持JVM的分配,也会触发OOM。
线程私有的内存区域
- 程序计数器
每个线程持有一份私有的,在JVM 中保存着当前线程正在执行的字节码的地址(执行本地方法时为空),宽度足够支撑当前平台所有的指令, - 虚拟机栈
每个线程持有一份虚拟机栈,由多个栈帧构成。执行方法时,会为该方法创建一个栈帧并压入栈中。栈帧是方法运行所需数据的结构化表示。
栈帧的结构和大小在编译时确定,其中动态链接就是触发类加载的一个条件,当动态链接转换为具体方法时,没有找到对应的类,就会触发加载。