前言
栈溢出(StackOverFlowError):
若Java虚拟机栈的内存大小不允许动态扩展,
当线程请求栈的深度超过当前Java虚拟机栈的最大深度时抛出
内存溢出(OutOfMemoryError):
若Java虚拟机堆中没有空闲内存,并且垃圾回收器也无法提供更多内存的话就会抛出
一、内存模型
1.线程私有的
程序计数器(Program Counter Register):
也被称为PC寄存器,
一块较小的内存空间,
当前线程所执行的字节码的行号指示器.
生命周期同线程,
唯一不会出现OutOfMemoryError的内存区域
Java虚拟机栈(Java Virtual Machine Stack):
Java方法执行的内存模型,
每次方法调用的数据都是通过栈传递的.
由一个个栈帧组成,
而每个栈帧都拥有:局部变量表、操作数栈、动态链接、方法出口信息.
生命周期同线程,
可能会出现StackOverFlowError和OutOfMemoryError
本地方法栈(Native Method Stacks):
为虚拟机使用的本地方法服务.
方法执行的时候也会创建一个栈帧,
用于存放本地方法的局部变量表、操作数栈、动态链接、出口信息.
可能会出现StackOverFlowError和OutOfMemoryError
2.线程共享的
Java堆(Java Heap):
虚拟机所管理的内存中最大的一块.
所有线程共享的一块内存区域,
在虚拟机启动时创建.
存放对象实例,
几乎所有的对象实例都在这里分配内存.
也是垃圾收集器管理的主要区域,
也被称为GC(Garbage Collected Heap)堆.
容易出现的就是OutOfMemoryError 错误
方法区(Method Area):
存储已被虚拟机加载的类信息、常量、静态变量、即编译器编译后的代码等数据.
别名Non-Heap(非堆),
也称为永久代(方法区是标准,永久代是实现).
JDK1.8被移除,用直接内存里的元空间取而代之.
可能会出现OutOfMemoryError
运行时常量池(Runtime Constant Pool):
class文件中除了有类的版本、字段、方法、接口等描述信息,
还有常量池表(用于存放编译器生成的各种字面量和符号引用).
属于方法区,
可能会出现OutOfMemoryError
直接内存(Direct Memory):
并不是虚拟机运行时数据区的一部分,
也不是虚拟机规范中定义的内存区域,
但是这部分也被频繁的使用,
可能会出现OutOfMemoryError
元空间(Metaspace):
方法区的实现由永久代变成了元空间,
属于直接内存,
可能会出现OutOfMemoryError
为什么替换掉方法区:
(1)客观上使用永久代来实现方法区的决定的设计导致了Java应用更容易遇到内存溢出的问题,而且有极少数方法 (例如String::intern())会因永久代的原因而导致不同虚拟机下有不同的表现
(2)主观上当Oracle收购BEA获得了JRockit的所有权后,准备把JRockit中的优秀功能,譬如Java Mission Control管理工具,移植到HotSpot 虚拟机时,但因为两者对方法区实现的差异而面临诸多困难。考虑到HotSpot未来的发展,在JDK 6的时候HotSpot开发团队就有放弃永久代,逐步改为采用本地内存(Native Memory)来实现方法区的计划了,到了JDK 7的HotSpot,已经把原本放在永久代的字符串常量池、静态变量等移出,而到了 JDK 8,终于完全废弃了永久代的概念,改用与JRockit、J9一样在本地内存中实现的元空间(Meta-space)来代替,把JDK 7中永久代还剩余的内容(主要是类型信息)全部移到元空间中