Java虚拟机在执行java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,都有各自的用户级创建销毁时间。
堆区和方法区是随着虚拟机进程的启动一直存在的.
虚拟机栈、本地方法栈和程序计数器是依赖用户线程的启动和结束而创建和销毁的。
堆 [所有线程共享]
堆区是JVM管理的内存中最大的一块,“几乎”所有的对象实例都在这里分配内存。
堆区里面又区分有新生代、老年代。
新生代里又包含1个Eden区和2个survivor区【From survivor, To survivor】,
堆里的细分是为了更好的分配和回收内存。
方法区 [所有线程共享]
主要用于存储加载的类型信息、常量、静态变量和即时编译器编译后的代码缓存等数据。
在JDK8中已经把里面的永久代给废弃了,改用本地内存的元空间,这样就能加载更多的类型信息。
运行时常量池
用于存放编译期生成的各种字面量与符号引用。
虚拟机栈 [线程私有]
这个也就是我们经常说到的栈区。在每个方法执行的时候都会同步创建一个栈帧,每个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
每一个方法的执行就相当于是压栈和出栈,在每一个方法执行完毕后其对应栈内的数据也就随之被回收了。
所以也就是有些变量我们能够定义为局部变量的尽量定义在局部变量,方便及时回收。
局部变量表
存放了编译期可知的数据类型、对象引用和返回地址类型。
基本数据类型【boolean、byte、char、short 、int、float、long、double】
对象引用:reference类型,不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或者其他于此对象相关的位置。
returnAddress类型:指向了一条字节码指令的地址。
上述数据类型在局部变量表中的存储控件以局部变量槽(Slot)来表示,其中64位的long和double类型数据会占用2个变量槽,其余的数据类型只占用一个。
本地方法栈 [线程私有]
本地方法栈和虚拟机栈其实都一样,唯一区别就是这个是提供给本地方法调用使用的,
并且程序计数器在执行本地方法时的指令值是undefined。
程序计数器 [线程私有]
是一块较小的内存空间,可以看做是当前线程执行的字节码行号指示器。[有的地方也叫PC寄存器]
一些基础功能:
分支、 循环、跳转、异常处理和线程恢复等都依靠它来处理。
每个线程都对应着自己唯一的程序计数器。
计数器记录的是正在执行的虚拟机字节码指令的地址,通过改变计数器的值来选择下一步要执行的指令。
这是唯一一个在JVM规范里不会有OOM的区域。