© 2020 GitHub, Inc.

Jvm内存说明

in Note with 0 comments

内存区域

运行时数据区域

Java虚拟机管理的内存区域具体如图所示

程序计数器

线程私有。当前执行的字节码的行号指示器。每个线程都有着自己的计数器,如果线程正在执行一个Java方法,这个计数器记录的是在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,这个计数器是空(undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OOM情况的区域。

Java虚拟机栈

线程私有,生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法运行时都会创建一个栈帧,用于存储局部变量表(基本数据类型、对象引用和returnAddress类型)、操作数栈、动态链接、方法出口等信息。方法的执行其实就是压栈的过程。

​ 虚拟机规范中,该区域有两种异常情况:

  1. 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowException;
  2. 如果虚拟机栈可动态扩展,如果扩展时无法申请到足够的内存,就会抛出OOM异常。
本地方法栈

​ 与虚拟机栈作用非常相似,区别是虚拟机栈服务于Java方法,本地方法栈服务于Native方法。但是虚拟机规范中并没有对本地方法栈中方法的语言、使用方法与数据结构做强制规定,虚拟机可自由的实现。栈深度溢出或栈扩展失败时会分别抛出StackOverFlow和OOM。

Java堆

线程共享的一块区域。唯一目的:存放对象实例,几乎所有的对象都在这里分配内存。

​ 从内存回收角度来看,由于现在收集器基本都采用分代收集算法,所以Java堆可细分至新生代和老年代(Eden、From Survivor、To Survivor)。从内存分配角度来看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB),用于提升对象分配效率。

​ Java堆可以处于物理上不连续的内存空间,只要逻辑上是连续的即可。可以通过-Xmx,-Xms控制内存大小。

​ 如果在堆中没有内存完成实例分配,并且堆再也无法扩展时,将抛出OOM异常。

方法区

​ 线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。JDK8之后,就放弃了永久代,改用元空间(区别就是元空间使用本地内存,主要存放类型信息)。

​ 当方法区无法满足内存分配需求时,将抛出OOM。

运行时常量池

​ 是方法区的一部分,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池。还具备动态性,可在运行时将新的常量放入池中,如String的intern()方法(返回字符串对象的规范化表示形式)。当常量池无法再申请到内存时,会抛出OOM。

直接内存

​ 不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。受本机内存大小和处理器寻址空间限制,

但是这部分内存会频繁被使用,也会有OOM。

​ JDK1.4的NIO类,引入了一种基于通道(Channel)和缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过Java堆中的DirectByteBuffer对象作为这块对内存的引用进行操作。避免了在Java堆和Native堆中来回的复制数据,提升性能。

回复