文章目录
- 1. Java 虚拟机内存模型
- 2. Java 虚拟机内存分布
- 2.1 程序计数器
- 2.2 Java 虚拟机栈
- 2.3 本地方法栈
- 2.4 Java 堆
- 2.5 方法区
1. Java 虚拟机内存模型
- 程序计数器, 用于存放下一条运行的指令。
线程私有
- Java 虚拟机栈, 用于存放 Java 函数调用堆栈信息。
线程私有
- 本地方法栈, 用于存放 Native 函数调用堆栈信息。
线程私有
- Java 堆, 用于存放 Java 程序运行时所需要的对象等数据。
线程共享
- 方法区, 用于存放程序的类元数据信息。
线程共享
2. Java 虚拟机内存分布
2.1 程序计数器
程序计数器是一块很小的内存空间, 由于 Java 是支持线程的语言, 当线程数量超出 CPU 数量时, 线程之间根据时间片轮询抢夺 CPU 资源。 对于单核 CPU 而已, 每一时刻, 只能有一个线程在运行, 而其他线程必须被切换出去。 为此,
每一个线程都必须用一个独立的程序计数器, 用于记录下一条要运行的指令
。各个线程之间的计数器互不影响, 独立工作, 是一块线程私有
的内存空间。
如果当前线程正在执行一个 Java 方法, 则程序计数器记录正在执行的 Java 字节码地址
; 如果当前线程正在执行一个 Native 方法, 则程序计数器为空
。
2.2 Java 虚拟机栈
Java 虚拟机栈也是
线程私有
的内存空间, 它和 Java 线程在同一时间创建, 它保存方法的局部变量、 部分结果, 并参与方法的调用和返回。
如果线程在计算过程中, 请求的栈深度大于最大可用的栈深度, 则抛出 StackOverflowError; 如果 Java 栈可以动态扩展, 而在扩展栈的过程中没有足够的内存空间来支持栈的扩展, 则抛出 OutOfMemoryError。
在 Hot Spot 虚拟机中, 可以使用 -Xss 参数来设置栈的大小。 栈的大小直接决定了函数调用的可达深度。
虚拟机栈在运行时使用一种叫做栈帧
的数据结构来保存上下文数据。在栈帧中存放了方法的局部变量表(存放方法的参数和方法内部的局部变量)、操作数栈、动态连接方法和返回地址等
信息。每一个方法调用都伴随着栈帧的入栈操作
。 相应的, 方法的返回则表示栈帧的出栈操作
。 如果方法调用时, 方法的参数和局部变量相对较多, 那么栈帧中的局部变量表就会比较大, 栈帧会膨胀以满足方法调用所需传递的信息。 因此, 单个方法调用所需的栈空间大小也会比较多。注意: 函数嵌套调用的次数由栈的大小决定。 栈越大, 函数嵌套调用次数越多。
对一个函数而言, 它的参数越多, 内部局部变量越多, 它的栈帧就越大
, 其嵌套调用次数就会减少。
2.3 本地方法栈
本地方法栈用于管理本地方法的调用。
本地方法不是用 Java 实现的, 而是用 C 实现的。在 SUN 的 Hot Spot 虚拟机中不区分本地方法栈和虚拟机栈。 因此, 和 Java 虚拟机栈一样, 也会抛出 StackOverflowError 和 OutOfMemoryError。
2.4 Java 堆
Java 堆是 Java 程序运行时内存中最重要的部分, 几乎所有的对象和数组都是在堆中分配空间的。 Java 堆分为
新生代
和老年代
两个部分, 新生代用于存放刚刚产生的对象和年轻的对象
。 如果对象一直没有被回收, 生存的足够长, 新生代对象就会被移入老年代。
新生代可细分为: Eden、 Survivor Space0(s0 或者 from space)、 Survivor Space1(s1 或者 to space)
。 Eden意义为伊甸园, 即对象的出生地, 大部分(存在部分较大对象会在创建时就移入老年代, 由参数 PretenureSizeThreshold 决定)对象刚刚建立时, 通常会存放在这里。 s0 和 s1 为 Survivor 空间, 直译为幸存者, 也就是说存放其中的对象至少经历过一个垃圾回收, 并得以幸存。 如果幸存区的对象到了指定年龄仍未被回收, 则有机会进入老年代(tenured)。
2.5 方法区
方法区也是 JVM 内存中非常重要的一块内存区域, 与堆空间类似, 它也是被 JVM 中所有
线程共享
的。 方法区主要保存的信息是类的元数据
。
方法区中最为重要的是类的类型信息、常量池、域信息、方法信息
。类型信息包括类的完整名称、 父类的完整名称、 类型修饰符和类型的直接接口类表; 常量池包括这个类方法、域等信息所引用的常量信息; 域信息包括域名称、域类型和域修饰符; 方法信息包括方法名称、 返回类型、 方法参数、 方法修饰符、 方法字节码、 操作数栈和方法栈帧的局部变量区大小以及异常表。 总之, 方法区内保存的信息大部分来自于 class 文件, 是 Java 应用程序运行必不可少的重要数据。注意:
方法区也可称为永久区, 主要存放常量和类的定义信息。
Hot Spot 虚拟机回收类的基本条件:
所有该类的实例被回收且装载该类的 ClassLoader 被回收。