一、内存划分总览
JVM在执行JAVA程序的过程中会把他管理的内存划分为几个区域,根据JVM的规范可以分为程序计数器、虚拟机栈、本地方法栈、方法区、堆,这五个区域,每个区域管理的职责不同,有些是共享的,有些是线程私有的,有些会出现内存溢出,有些是会被GC回收掉的。
下图为JVM运行时的内存分布图:
二、认识每个数据区
(1)程序计数器:
线程私有的区域,每个线程都有自己的程序计数器,用于记录当前执行的指令的位置--当前线程所执行字节码的行号指示器。字节码解释器通过这个计数器的值取下一条指令,程序执行的顺序逻辑都依靠它来完成。
JAVA多线程是通过线程轮流切换并分配处理器执行时间的方式实现的。为了切换后能够使线程恢复到正确执行位置,因此计数器必须是线程私有的。
如果JVM正在执行Java方法,那计数器的值应该是字节码指令的地址,如果是一个native方法,那计数器的值为空。
在JVM规范中,唯一一个不会出现OOM的区域
(2)虚拟机栈
描述Java方法执行时的内存模型。也是线程私有,跟随线程的生命周期创建和销毁。
Java方法在执行时,在都会创建一个“栈帧”,用于存储局部变量表,操作数栈,动态链接,方法出口等信息,每一个方法从调用到返回的过程都对应一个栈帧在虚拟机栈中入栈和出栈的过程。
大致示意图:
其中局部变量存放基础类型变量(存放的是变量本身)、对象(存放的是对象的引用,即指向对象的起始地址的指针,也可能是对象的句柄)、返回地址(指向一条字节码指令的地址)
当进入一个方法时,这个方法需要的栈帧的空间是确定的,运行期间不会改变栈帧的大小,局部变量表所需的内存是在编译阶段就确认了。
此区域会出现StackOverflowError、OOM两类异常
(3)本地方法栈
该区域与Java虚拟机栈类似,只不过此区域描述的是native的方法。
(4)方法区 -- non heap 也被成为永久区。 JDK1.7之前常量池存在这个区域,后面迁移到堆
线程共享的区域,存放了程序所需的类的结构信息、常量、静态变量等
这个区域也被称为永久区,也就是用永久区的方式来处理这个区域的内存回收。JVM对于类定义和常量的回收非常严格,因此这个区域在内存回收上也无法收到什么成效。
此区域也会出现OOM
(5)Java堆
JVM规范中内存区域最大的一块区域,所有线程共享。也是内存回收管理最频繁的一个区域。
所有的对象实例都在这个区域申请内存空间,JVM规范的原文:the heap is the runtime data area from which memory for all class instances and arrays is allocated.
此区域根据垃圾分代算法可以分为,新生代、老年代。也可以分为,eden区、from survivor、to survivor
此区域的大小可通过参数-Xmx 和-Xms控制,如果申请内存时超过了限制,就会出现OOM
三、内存溢出的总结
待更新...
四、对象的创建和内存布局
待更新...