jvm---1、内存区域划分

时间:2022-07-28 15:12:51

JVM的内存区域划分

jvm---1、内存区域划分

jvm 内存可分为 
方法区、堆   ----------------------  线程共享的,也就是说在这里面的变量线程不安全。
虚拟机栈、本地方法栈、程序计数器 --- 线程独享
直接内存 ----- 图中没有画出,记得UnSafe 有方法好像用的是直接内存, 如果 DirectBufferCache 就是使用的直接内存做缓存的

其中堆又分为年轻代,老年代。年经代又分为一个Eden区和两个Survivor 区(两个Survivoer 一个小from  一个叫to ),两者比例默认是8:1(后面再详解,这个和内存分配、GC有关系 )。

作用
1、方法区    
用于存储已被加虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码数据。提一下:有叫“非堆”的,HotSpot 上叫“永久代”,使用了永久代来实现方法区而已。永久代改为Native Memory 实现了,jdk1.7中已经把永久代的字体串常量移出。
该区域内存回收,主要是常量池回收和对象类型卸载,这个区域的内存回收率比较小,尤其是类型卸载条件比较苛刻。
无法满足内存分配的话会抛出 OutOfMemoryError 异常

2、堆
内存中最大的一块,唯一的目的就是存放对象实例,几乎所有的实例对象在这时分配内存(JVM 规范:所有对象及数组都要在堆上分配)。JIT 编译器及逃逸分析技术成熟分导致一些微妙的变化。
无法满足内存分配的话会抛出 OutOfMemoryError 异常

3、虚拟机栈
线程私有的,生命周期和线程相同。虚拟机本描述的是Java 方法执行的内存模型:每个方法在执行的同时会创建一个栈贞(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用到执行完成过程,就是栈贞在虚拟机栈中入栈到出栈的过程。
局部变量表所用内存在编译期间已经确定了(基本数据类型和引用占用一个字节,double long 占用两个字节),不会改变。
如果线程请求的栈深度大于虚拟机所允许的深度,会抛出*Error。虚拟机栈是可以动态扩展的,不过是允许固定长度的虚拟机栈。如果在扩展过程中无法申请到足够内存会抛出OutOfMemoryError异常

4、本地方法栈   Native Method Stack 
与虚拟机栈作用相似,一个是为虚拟机执行java 方法服务的,一个是虚拟机调用Native 方法的服务的。
也会抛出:*Error 和 OutOfMemoryError

5、程序计数器
一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。jvm 多线程就是通过线程轮流切换并分配处理器时间的方式来实现的,在任何一个时间处理器只能处理一条线程中的指令。所以为了线程切换后能恢复到正确的指令位置,每一个线程都有一个独立的程序计数器。
如果是在执行一个Native 方法,这个计数器是空(Undefined), jvm 规范中唯一一个没有OutOfMemoryError 的区域

6、直接内存  Direct Memory
NIO 可以使用Native 函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer 对象作为这块内存的引用进行操作(有一些堆缓存的实现也是这种方式实现的),这样就避免了java 堆和Native 堆中来回复制数据,所以NIO效率就高。
直接内存不受java堆大小限制,但是受物理内存和处理器的寻址空间限制,所有会抛出OutOfMemoryError

参考:《深入理解jvm虚拟机》