Java 虚拟机将管理的内存分为多个不同的区域,这些区域有着各自的用途和生命周期。在 Java 7 中,虚拟机将其划分为一下几个区域:
![](https://img.haomeiwen.com/i4894808/ae47aaaa6ba2a057.png)
程序计数器 (Program Counter Register)
程序计数器可以看做当前线程所执行的字节码的行号指示器。
简单的来讲,我们根据修改计数器的值,然后实现分支、循环、跳转、异常处理、线程恢复等实现。
-
为什么时线程私有的?
为了保证线程切换后恢复到正确的执行位置,所以每个线程都需要有一个队里的程序计数器,各条线程计数器互不影响,独立储存,称为这类内存区域“线程私有”的内存。 -
Native 方法和Java方法的区别
如果执行的时Java方法,计数器指示的是执行的字节码的地址。如果执行的是 Native 方法,则计数器值为空。 -
特点
- 线程私有的,生命周期和线程相同。
- 是唯一一块不会出现 OutOfMemory 的内存区域。
Java 虚拟机栈 (Java Virtual Machine Stacks)
传统意义上的方法栈区,简单(并不准确)讲就是,在开始执行方法时,方法入栈,然后在方法执行结束后出栈。
每个方法执行时会创建一个栈帧(stack frame),用于储存局部变量表、操作数栈、动态链接、方法出口等信息。直到方法 return 或 抛出异常时出栈。
-
局部变量表时什么?
局部变量表是一个方法在编译期间就确定空间大小的,存放了方法涉及到的局部变量。存放了编译期可知的基本数据类型,包括(boolean, byte, char, short, int, float, long, double) 对象的 reference 和 returnAddress 类型(指向一条字节码指令的地址)。 -
特点
- 线程私有的,生命周期和线程相同。
- 线程请求的栈深度大于虚拟机允许的深度,会抛出 StackOverFlow 异常。
- 如果虚拟机是可以动态扩展,且扩展时无法申请到足够的内存,会抛出 OutOfMemory 异常。
本地方法栈(Native Method Stack)
- 本地方法栈与虚拟机栈的区别在于 虚拟机栈为虚拟机执行的 Java 方法服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。
- 本地方法堆也同样会抛出 StackOverFlow 和 OutOfMemory。
Java 堆(Java Heap)
Java 堆的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
- 特点
- 所有线程共享
- 虚拟机启动时创建
- 垃圾回收器管理的主要区域
- 在实现时可以是固定大小的,也可以是可扩展的,堆中内存不足以完成实例的内存分配,并且堆无法再扩展时,会抛出 OutOfMemory 异常
方法区(Method Area)
用于存储被虚拟机加载的类信息、常量、静态变量、编译器编译后的代码等数据。
-
类信息是什么?
类型的权限定符,类的方法信息,类的字段信息等。 -
特点
- 所有线程共享
- 虚拟机启动时创建
- 无法请求到内存时会抛出 OutOfMemory 异常。
运行时常量池(Runtime Constant Pool)
运行时常量池是方法区的一部分。
Class 文件除了有类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后在运行时常量池存放。具体信息入下图:
![](https://img.haomeiwen.com/i4894808/c665a730f263f40b.png)
- 其他
- 无法请求到内存时会抛出 OutOfMemory 异常。
- 字符串常量在 JDK 1.7后移入堆中。
直接内存(Direct Memory)
直接内存并不是虚拟机运行时数据区的一部分。也不是 JVM 规范中定义的内存区域。
NIO 类中引入了一种基于通道和缓冲区的 I/O 方式,可以使用 Native 函数库直接分配堆外内存,然后通过一个储存在 Java 堆中的 DirectByteBuffer 对象对这块内存引用操作。
- 直接内存不会受 Java 堆大小的限制。但还是会受本机的总内存限制。导致 OutOfMemory。
网友评论