jvm内存区

来源:互联网 发布:java 计算时间的差 编辑:程序博客网 时间:2024/06/05 09:40

运行时的数据区域

java 虚拟机在执行 java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建销毁时间。下面概括这些区域。

程序计数器

程序计数器是一片比较小的内存区域,里面记录着当前线程代码的执行位置。代码的分支、循环、异常处理等都是通过程序计数器来实现。
由于java的多线程是通过线程的轮流切换并分配处理器的执行时间来实现的,因此为了线程切换后能回到正确的执行位置每个线程都需要一个独立的程序计数器。
如果一个java线程正在执行一个本地方法,则这个值为空。

java 虚拟机栈

与程序计数器一样,java 虚拟机栈的生命周期与线程的生命周期相同。虚拟机栈由一个个栈帧组成,栈帧是 java 方法执行的主要场所。每个方法在执行的同时会创建一个栈帧。用于存储局部变量表,动态链接,操作数栈,方法返回地址等信息。每个 java 方法开始执行和执行结束就对应着一个栈帧的创建与销毁。
局部变量表存储着方法中所有的局部变量,这里可以存放原始类型、对象引用等。操作数栈是指令码执行的场所。
在 java 虚拟机规范中,对这个区域规定了两种异常:
1. StackOverflowError: 如果线程所请求的栈深度超过虚拟机所允许的深度,则该异常会抛出。
2. OutOfMemoryError: 如果虚拟机栈可以动态扩展,而栈在扩展时无法申请到足够的内存,则该异常会抛出。

本地方法栈

本地方法栈是在调用 native 方法时使用的,这里不做过多讲解。

java 堆

java 堆是被线程共享的一块区域。这块区域存在的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
java 堆是垃圾收集管理的主要区域,因此也被叫为 “GC 堆”。从垃圾搜集的角度讲,现代的垃圾搜集器都是分带搜集,所以 java 堆还可以细分为新生代和老年代。新生代又可再细分为 Eden 区、 From Survivor、 To Survivor 等。
java 堆的大小可以通过 vm 参数 -Xmx 与 -Xms 来控制。当 java 堆中没有内存能完成实例的分配并且堆也无法扩展的时候,将会抛出 OutOfMemoryError 异常。

方法区

方法区也是被所有线程共享的区域。它用于存储被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
对于 jdk 1.7 及以前的 HotSpot VM 来说,由于该 VM 的设计团队把 GC 分代搜集扩展至方法区,因此这里也叫永久代。但是这并不是一个很好的叫法,只有 HotSpot VM 这么做了,而且在 JDK 1.8 中 HotSpot VM 也已经放弃永久代,改用本地内存来管理方法区了。

运行时常量池

运行时常量池(Runtime Constant Pool)是方法区的一部分。 运行时常量池存放着 Class 文件中的常量池的拷贝。
运行时常量池相对于 CLass 文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入CLass 文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用比较多的就是 String 类的 intern() 方法。

直接内存

直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是 Java 虚拟机规范中定义的内存区域,它直接从操作系统中分配,因此不受 Java 堆大小的限制,但是会受到本机总内存的大小及处理器寻址空间的限制,因此它也可能导致 OutOfMemoryError 异常出现。在 JDK1.4 中新引入了 NIO 机制,它是一种基于通道与缓冲区的新 I/O 方式,可以直接从操作系统中分配直接内存,即在堆外分配内存,这样能在一些场景中提高性能,因为避免了在 Java 堆和 Native 堆中来回复制数据。

内存区简图

找不到图片

0 0
原创粉丝点击