在我们Java语言,我们把内存的控制权利交给我们的JVM虚拟机
线程共享的地方有堆Heap,
线程私有的有虚拟机栈(VM statck)还有本地方法栈(Native mathod Stack),还有程序计数器(Program counter register)
本地内存有 元空间(MateSpace) 线程共享
还有直接内存 Direct Memory 线程共享
程序计数器是一个比较小的内存空间,可以看作为当前线程所执行的字节码的行号指示灯,字节码解释器工作时候通过改变这个计数器的值选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都是依赖这个计数器实现的。
jvm栈是jvm的核心,除了本地方法所又的方法调用都是通过栈进行实现的,每一次方法调用都需要栈进行传递,每一次方法调用都有一个对应的栈针轧入栈中,每一个方法调用结束都会弹出,每个栈帧中都有:局部变量表,操作数栈、动态链接、方法返回值
局部变量表就是我们熟知的8大数据类型还有我们的对象饮用reference类型,他不同于对象本身,可能是一个指向一个对象起始地址的引用指针,也可能是指向一个代表对象活着其他与此对象相关的位置。
**操作数栈:**操作数栈就是我们方法调用中转所之用的,用于存放方法执行过程中产生的中间计算结果还有计算过程中产生的临时变量。
**动态链接: **主要服务一个方法需要调用其他方法的场景。Class文件的常量池里保存大量的符号饮用,当一个方法调用其他方法的时候,需要将常量池中指向方法的符号引号转化为其在内存地址中的直接饮用。动态链接的作用就是为了将符号饮用转换为调用方法的直接饮用。
栈内存不是无限的,当我们线程请求的栈深度超过当前java虚拟机栈的最大深度就会跑出StackOverFlowError的错误。
我们的栈的内存是可以动态扩展的,如果我们无法申请到更大的内存就会跑出OutOfMemoryError异常。但是要注意我们的HotSpot虚拟机是不支持的动态扩展的,如果申请内存成功就不会抛出OutOfMemoryError,否则就会抛出。
本地方法栈和jvm栈一样,只不过本地方法栈是为了java服务。本地方法栈也会出现StackOverFlowError和StackOverFlowError
主要存储对象的实例和数组的内存区域,也是我们垃圾回收器主要管理的区域。
在jdk1.8及以后的版本,方法区被称为元空间(Metaspace)。元空间是JVM用于存储类的元信息、常量池、静态变量、编译器优化后的代码等数据的内存区域。
在元空间中动态内存分配,内存空间是动态分配的,不会受限于固定大小, 这就意味着元空间需要动态地分配内存和释放内存,而从避免了方法区可能出现的内存溢出的问题。
在元空间类的元信息存储(类的结构、字段、方法、注解等)都是存储在本地内存的,这样就有了更大的灵活性,因为本地内存是根据需要进行调整的。
**在元空间类的卸载 :**在元空间中我们的原信息都是存储在本地内存的,所以更容易对类进行卸载,就也就可以让我们的程序可以动态的家在和卸载类。
**元空间的参数设置:**我们可以使用命令or参数对我们的jvm进行调整元空间的大小、垃圾回收策略等。这样我们就可以根据需求对我们的元空间进行调整。
元空间是不会被垃圾回收的,因为它的内存是由操作系统直接分配和管理的,不是又JVM垃圾回收管理的。
在1.8版本存储在原空间。他为类的价值、解析和运行提供了必要的数据支持,它存储了字面量和符号引用解析、静态变量和常量的存储、运行时常量池的动态扩展。
**存储字面量:**存储源代码中出现的常量
**符号引用的解析:**存储了累和接口的符号引用,JVM可以通过常量池中的符号引用来解析和定位对应的方法、字段或类。
**静态变量和常量值的标记:**静态变量和常量在编译期被放置在运行时常量池中。当类被加载时,静态变量和常量的初始值会从运行时常量池中获取。
**运行常量池的动态扩展:**运行时常量池拥有动态扩展的能力,可以在运行时将新的常量添加到常量池中以及常量的替换和修改
在JDK1.8的时候,字符串常量池在堆中,每次创建一个字符串常量的时候首先回去字符串常量池看是否存在,如果存在就用字符串常量池的字符串引用,不存在则创建。
直接内存是一个特殊的内存缓冲区,并不在Java堆或方法区中分配,而是通过JNI的方法在本地内存上分配的。
直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机内存中规范定义的一部分。但是这块内存也会被频繁使用也会出现OutOfMemoryError错误的出现,
直接内存可以通过Java的NIO库进行直接的读写操作,则无需从Java堆复制内存到直接内存。直接内存的目的就是减少java方法区的压力。
NIO就是一种基于Channel和Buffer的IO方法,它可以直接使用本地函数库直接分配内存,然后通过一个存储在Java堆中DirectByteBuffer对象作为这块内存的引用进行操作。这样就避免了Java堆在Native堆中来回复制数据。