内存管理示意图:
我们通常所说的堆内存和栈内存。堆内存指定是线程共享区的堆;栈内存指的是线程独占区的虚拟机栈,具体说就是虚拟机栈中局部变量表这么一部分。
方法区和堆是分配给进程的,所以就是所有的线程共享。
虚拟机栈、本地方法栈和程序计数器是分配给每个独立线程的,是运行过程中必不可少的资源。
程序计数器
- 程序计数器是一块较小的内存空间,他可以看做是当前线程所执行的字节码的行号指示器。
- 程序计数器处于线程独占区
- 如果线程执行的是Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址。如果是native方法,这个计数器的值就是undefined。
- 此区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域(因为基本没有方法可以操作程序计数器,程序计数器一般都由JVM维护)。
- goto语句可以操作程序计数器,但是现在废弃了
虚拟机栈
存放方法运行时的数据
- 虚拟机栈描述的是Java方法执行的动态内存模型
- 方法A运行的过程:
- 首先进入方法A,方法A会创建一个栈帧,栈帧进入虚拟机栈被执行,若中途发现方法A调用其他方法B,那么方法B会创建一个栈帧进入虚拟机栈,直至方法B执行结束,会弹出虚拟机栈被销毁,才会继续执行方法A。
- 故虚拟机栈可以看成描述方法运行的过程。
- 方法A运行的过程:
- 栈帧
- 每个方法运行,都会创建一个栈帧,伴随着方法从创建到执行完成。用于存储局部变量表,操作数栈、动态链接、方法出口等。
- 局部变量表
- 存放编译期可知的各种基本数据类型,对象引用(reference类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置),returnAddress类型(指向了一条字节码指令的地址)
- 局部变量表的内存空间是在编译期就已经完成了分配,党进入一个方法时,这个方法在栈帧中需要分配多少内存是固定的,在方法的运行期间是不会改变的。long和double类型的数据会占用2个局部变量空间(Slot),其余的数据类型只占用一个。
- 在方法中调用了应用类型User,User对象有字段为String类型的name,那么在方法运行的过程中name的值是动态的,怎么保证局部变量的内存空间固定
- 这是因为局部变量表中保存的知识这个对象的引用,对象的创建会创建到堆内存中去。所以在方法运行过程中局部变量表的大小是固定的。
- 大小
- StackOverflowError(栈内存溢出)
- 这种是因为在虚拟机栈中不停的调用方法,直至虚拟机栈内存溢出。
- OutOfMemoryError
- 如果虚拟机栈可以动态的扩展,虚拟机在扩展栈时无法申请到足够的内存空间。
- StackOverflowError(栈内存溢出)
可以说基本与虚拟机栈一直,特别在Hotspot中是在一起的,不会做区分。
唯一的区别:
- 虚拟机栈是为虚拟机执行Java方法服务
- 本地方法栈是为虚拟机执行Native方法服务
堆
- 存放对象实例
- 垃圾收集器管理的主要区域
- 新生代、老年代、Eden空间
- 因为现在收集器基本采用分代收集算法,所以堆可以分为新生代和老年代。再细致一点的有Eden空间、From Survivor空间等。
- 区分这些,主要是为了垃圾回收
方法区
- 存储虚拟机加载的类信息,常量、静态变量,即时编译器编译后的代码等数据
- 类信息
- 类的版本
- 类的字段
- 类的方法
- 类的接口
- 类信息
- 方法区和永久代
- 方法区就称为永久代,也就是说使用永久代来实现的方法区,仅仅是对于Hotspot两者是等价的,对于其他虚拟机则不一定了。
- 垃圾回收在方法区的行为
- 回收效率很低,故很少在方法区
- 包括常量的回收、对象类型的卸载
- 回收效率很低,故很少在方法区
- 异常的定义
- OutOfMemory
- 运行时常量池
- 运行时常量池是在方法区中的,用于存放编译期生成的各种字面量和符号引用
除了保存Class文件中描述的符号引用外,还会把翻译出来的直接引用也存储在运行时常量池中。
具备动态性,运行期间也能将新的常量放入池中。
- 出现这样的情况,是因为我们定义的常量的数据不是存放在堆中,而是存放在常量池中的,且常量池是无序不可重复的规则,所以s1就等于s2
- 通过new操作符创建的对象,是存放在堆内存中的,所以会出现s1不等于s3的情况
- s3.intern()的方法是运行期间将s3的值生成常量,这样的情况就会造成数据会被放在常量池中,也就出现s1等于s3.intern()的情况,这种常量称为运行时常量。
直接内存:
- 直接内存由操作系统管理,不是有虚拟机管理。
- 作用:NIO中可以直接使用Native函数库直接分配堆外内存,然后通过一个存储在队中的DirectByteBuffer对象作为内存的引用进行操作。
- OutOfMemoryError:设置虚拟机参数时可能忽略直接内存,使得各个内存区域总和大于物理内存限制,动态扩展内存时就会出现这个异常。