Java内存结构图

由图所示,Java运行时内存区域分为:虚拟机栈方法区本地方法栈程序计数器

程序计数器

程序计数器可以看作是当前线程所执行的字节码的行号指示器,它只了很小的内存。程序的基础功能(循环、跳转、异常处理、线程恢复等)都需要依赖这个计数器来完成。程序计数器是属于线程私有的。

虚拟机栈

虚拟机栈描述的是Java方法之行的内存模型:每个方法被执行的时候都会同时创建一个栈帧,用于存储局部变量表、操作数栈、动态链表、方法出口等信息。

每一个方法被调用直至执行完成的过程,就是对应一个栈帧在虚拟机从入栈道出栈的过程。

我们平时所关心的栈,可以说是虚拟机栈中的局部变量表,局部变量表存放了编译期可知的基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用returnAddress类型。

JVM规范规定了这个区域的两种异常:

  • StackOverflowError

    如果线程请求的栈深度大于虚拟机所允许的深度时会抛出;

  • OutOfMemoryError

    如果虚拟机栈可以动态扩展,当扩展时无法申请到足够的内存时会抛出。

本地方法栈

本地方法栈与虚拟机栈所发挥的作用是非常相似的,虚拟机栈执行Java方法(字节码)服务,而本地方法栈则是Native方法服务

本区域会抛出 StackOverflowErrorOutOfMemoryError异常。

方法区

方法区存储已经被虚拟机加载的类信息常量静态变量即时编译器编译后
的代码

有些资料说方法区为“永久代(Permanent Generation)”(其实只有HotSpot虚拟机)。

在垃圾回收方面,这个区域很少出现垃圾回收行为,但是不是说没有垃圾回收。这个区域的垃圾回收主要目标是常量池的回收和对类型的卸载。

JVM规范规定,这个区域会抛出 OutOfMemoryError 异常:当方法区无法满足内存分配需求时抛出。

运行时常量池

运行时常量池 是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。

运行时常量池的另一个重要特征是具备动态性,除了Class文件中常量池的内容进入方法区常量池外,运行期间也可能将新的常量放入池中,比如String类的intern()。

这部分会抛出 OutOfMemoryError 异常:当常量池无法再申请到内存时会抛出。

Java堆是被所有线程共享的一块内存区域,在虚拟机启动的时候创建。大多数应用中,堆事JVM内存中最大的一块。按照JVM规范,所有的对象实例以及数组都要在堆上分配,但是随着技术的发展,这已经不是绝对的了。

Java堆是垃圾收集器管理的主要区域,被称为GC堆,按照分带收集算法(现在基本是),堆分为新生代老年代。从内存非配的角度,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区。

这里存放的都是对象实例

堆的大小可通过 -Xmx-Xms 控制。

这里会抛出 OutOfMemoryError:堆中没有内存完成实例分配,并且堆也无法再扩展时。

总结

简单的归类,Java运行时内存区域中:

属于线程私有的区域是:程序计数器、虚拟机栈、本地方法栈;

属于线程共享的区域是:堆、方法区(包括运行时常量池)。