原创

Java内存区域

一、JVM 框架

Java 虚拟机 HotSpot 的框架: HotSpot的框架 JVM 主要组成部分:

Class Loader(类加载器)Runtime Data Area(运行时数据区)执行引擎(Execution Engine)

二、JDK1.6 内存区域

Jdk1.6 内存区域 Jdk1.6内存区域

Jdk1.6内存区域
Jdk1.6内存区域

1. 程序计数器

程序计数器(Program CounterRegister) 是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器.

在虚拟机的概念模型里, 字节码解释器工作时就是通过改变这个计数器的值来选去吓一跳需要执行的字节码指令, 分支, 循环, 跳转, 异常处理, 线程恢复等基础功能都需要依赖这个计数器来完成.

2. Java 虚拟机栈

与程序计数器一样, Java 虚拟机栈(Java Virtual Machine Stacks) 也是线程私有的, 它的生命周期与线程相同.

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

每一个方法被调用直至执行完成的过程, 就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程.会抛两种异常:

  • 如果线程请求的栈深度大于虚拟机所允许的深度, 将抛出 StackOverflowError 异常;
  • 如果虚拟机栈可以动态扩展(当前大部分 Java 虚拟机都可动态拓展, 只不过 Java 虚拟机规范中也允许固定长度的虚拟机栈),当拓展时无法申请到足够的内存时会抛出 OutOfMemoryEoor 异常.

3.本地方法栈

本地方法栈(Native Method Stacks) 与虚拟机栈所发挥的作用是非常相似的, 其区别不过是虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到 Native 方法服务. 虚拟机规范中对本地方法栈中的方法使用的语言, 使用方式与数据结构并没有强制规定, 因此具体的虚拟机可以自由实现它. 甚至有的虚拟机(譬如 Sun HotSpot 虚拟机)直接就把本地方法栈和虚拟机栈合二为一. 与虚拟机栈一样, 本地方法栈区域也会抛出 StackOverflowError 和 OutOfMemoryError 异常.

4.Java 堆

对于大多数应用来说, Java 堆(Java Heap) 是 Java 虚拟机所管理的内存中最大的一块. Java 堆是被所有线程共享的一块内存区域, 在虚拟机启动时创建. 此内存区域的唯一目的就是存放对象实例, 几乎所有的对象实例都在这里分配内存. 当前主流的虚拟机都是按照可拓展来实现的( 通过-Xms 初始化堆, -Xmx 最大堆空间), 如果在堆中没有内存完成实例分配, 并且堆也无法在拓展时, 将会抛出 OutOfMemoryError 异常.

5. 方法区

方法区(Method Area) 与 Java 堆一样, 是各个线程共享的内存区域, 它用于存储已被虚拟机加载的类信息, 常量, 静态变量, 即时编译器编译后的代码等数据. 虽然 Java 虚拟机规范把方法区描述为堆的一个逻辑部分, 但是它却有一个别名叫做 Non-Heap 非堆, 目的应该是与 Java Heap 区分开来.

6.运行时常量池

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

7. 直接内存

直接内存(Direct Memory) 并不是虚拟机运行时数据区的一部分, 也不是 Java 虚拟机规范中定义的内存区域, 但是这部分内存也被频繁地使用,而且也可能导致 OutOfMemoryError 异常出现. 显然, 本机直接内存的分配不会受到 Java 堆大小的限制, 但是, 既然是内存, 则肯定还是会受到本机总内存的大小及处理器寻址空间的限制. 服务器管理员配置虚拟机参数时, 一般会根据实际内存-Xmx 等参数信息, 但经常会忽略到直接内存, 使得各个内存区域的总和大于物理内存限制(包括物理上的和操作系统级的限制), 从而导致动态扩展时出现 OutOfMemoryError 异常.

三、JDK8 内存区域-永久代的废弃

JDK8 永久代变化如下图:

JDK8 永久代 永久代(方法区的实现) : PermGen----->替换为 Metaspace(本地内存中)

更新动机:

  • 移除永久代是为融合 HotSpot JVM 与 JRockit VM 而做出的努力 因为 JRockit 没有永久代,需要配置永久代。
  • 现实使用中易出问题
    • 字符串存在永久代中,容易出现性能问题和内存溢出。
    • 类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。
    • 永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。

元空间概念

元空间是方法区的在 HotSpot jvm 中的实现,方法区主要用于存储类的信息、常量池、方法数据、方法代码等。方法区逻辑上属于堆的一部分,但是为了与堆进行区分,通常又叫“非堆”。 元空间的本质和永久代类似,都是对 JVM 规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。,理论上取决于 32 位/64 位系统可虚拟的内存大小。 

正文到此结束