JVM内存空间

图解结构

  1. JVM基本结构图
    这里写图片描述
  2. 《深入理解Java虚拟机(第二版)》中的描述
    这里写图片描述

结构介绍

Java程序在运行时,需要在内存中的分配空间。为了提高运算效率,就对数据进行了不同空间的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。

具体划分为如下5个内存空间:(非常重要)

  1. 线程隔离
    1. 程序计数器: 存放下一条指令
    2. java虚拟机栈: 存放java函数调用堆栈信息
    3. 本地方法栈: 存放本地函数调用堆栈信息
  2. 线程共享
    1. java堆: 存放java程序运行时所需的对象等数据
    2. 方法区:存放程序的类元数据信息

其中java堆和java栈是大多数程序员最关心也是最容易出现异常的区域,另外还有几个比较特殊的内存空间,运行时常量池和直接内存,其中运行时常量池包含在方法区里,直接内存则已不是虚拟机运行时数据区的一部分了

下面详细介绍各个部分

1.程序计数器(pc寄存器,Program Counter Register)

  • 定义:程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器
    当前线程:java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时间,一个处理器内核都只会执行一条线程中的指令,正在执行的线程即为”当前线程”,正在被线程执行的方法即为该线程的当前方法
  • 线程私有:每个线程拥有一个PC寄存器,在线程创建时创建
    原因:为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储
  • 功能:指向下一条指令的地址
    在虚拟机的概念模型里,字节码解释器的工作就是通过改变程序计算器的值来选择下一条需要执行的字节码指令.
    分支,循环,跳转,异常处理,线程恢复等(流程相关的)基础性功能都要依赖这个计数器来完成.
    • 执行java方法时,保存java虚拟机正在执行的字节码指令的地址
    • 执行native本地方法时,值为空undefined
  • 异常:程序计数器区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域.

2.java虚拟机栈(Java Virtual Machine Stack)

  • 定义:虚拟机栈描述的是java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法返回地址等信息,每一个方法从调用直到执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程.
    栈帧:栈帧是方法运行时的基础数据结构,由局部变量表,操作数栈,动态链接,方法返回地址,附加消息5部分组成.
    在 java虚拟机规范 的第1版中,java虚拟机栈也称为”java栈”,一般说java栈就是指java虚拟机栈
  • 线程私有:每一条java虚拟机线程都有自己私有的java虚拟机栈,其生命周期与线程相同
    栈帧对应的是方法,java虚拟机栈对应的是线程
  • 功能:存储栈帧(栈帧的局部变量表存放了编译期已知的各种基本数据类型(8种基本类型),对象引用(指针或句柄)和returnAddress类型(已弃用))
  • 容量:可固定大小(用户可调节初始容量),也可动态扩展和收缩(用户可调节最大,最小容量)
  • 异常:
    • StackOverflowError
      线程请求分配的栈容量(深度)超过java虚拟机栈允许的最大容量(深度)
    • OutOfMemoryError
      1.动态扩展时无法申请到足够的内容
      2.创建新的线程时没有足够的内容去创建对应的虚拟机栈

3.本地方法栈(Native Method Stack)

  • 作用类似于java虚拟机栈,但是是为native方法服务的.
  • java虚拟机规范对其使用方式和数据结构实现没有具体要求,甚至允许不提供本地方法栈(java虚拟机不支持native方法或者不依赖传统栈的话)
  • 容量和异常同java虚拟机栈

4.java堆(Java Heap)

  • 定义:java堆是可供各个线程共享的运行时内存区域,也是可供所有类实例数组对象分配内存的区域
  • 线程共享:在虚拟机启动的时候就被创建
  • 功能:存放被垃圾收集器(garbage collector)管理的各个对象(类实例和数组对象)
  • 容量:可固定大小(用户可调节初始容量),也可动态扩展和收缩(用户可调节最大,最小容量)
  • 异常:
    OutOfMemoryError:堆中没有内存完成实例分配,并且堆无法扩展(对于按动态扩展实现的虚拟机来说)

5.方法区(Method Area)

  • 定义:与java堆类似,是被JVM中所有的线程共享的区域,主要保存每一个类的结构信息(类的元数据)
    类的元数据:运行时常量池,字段和方法数据,构造方法和普通方法的字节码内容,还包括一些在类,实例,接口初始化时用到的特殊方法
    方法区是堆的逻辑组成部分,但又被称为Non-Heap(非堆),虚拟机规范(SE8版本)指出方法区可不实现垃圾收集和压缩,也不限定方法区的内存位置和编译代码的管理策略.
    在HotSpot虚拟机中,方法区被称之为永久代(Permanent Generation),这只是用永久代来实现方法区以交由垃圾收集器管理的策略而已,这个策略也在被放弃,所以一般不用”永久代”这个说法.JDK7开始,方法区中的一些区域移动到Java堆中
  • 线程共享:在虚拟机启动的时候就被创建
  • 功能:保存个类的结构信息(类的元数据)
  • 容量:可固定大小(用户可调节初始容量),也可动态扩展和收缩(用户可调节最大,最小容量)
  • 异常:
    OutOfMemoryError:方法区的内存空间不能满足内存分配请求

6.运行时常量池(Runtime Constant Pool)

  • 定义:运行时常量池是class文件中每一个类或接口的常量池表(constant_pool table)的运行时表示形式,每一个运行时常量池都在java虚拟机的方法区中分配,在加载类和接口到虚拟机后,就创建对应的运行时常量池.
  • 功能:包含若干种不同的常量(范围:从 编译器可知的数值字面量必须在运行期解析后才能获得的方法或字段引用)(常量是可以在运行时产生的,如String.intern()方法)
  • 注意:字符串常量池从JDK7开始就移到堆之中,导致String.intern()的用法也有所不同,参考https://blog.csdn.net/alinyua/article/details/79796453

7.直接内存(Direct Memery)

直接内存并不是虚拟机内存的一部分,也不是Java虚拟机规范中定义的内存区域。jdk1.4中新加入的NIO,引入了通道与缓冲区的IO方式,它可以调用Native方法直接分配堆外内存,这个堆外内存就是本机内存,不会影响到堆内存的大小。但会受到本机总内存影响,动态扩展失败时会出现OutOfMemoryError异常.

java堆和栈的区别

一般指java堆和java虚拟机栈的区别

区别点 java堆 java虚拟机栈
主要存储对象 类实例和数组对象(通过逃逸分析,有些局部变量的实例可在栈内分配) 栈帧(栈帧的局部变量表存放了基本数据类型,对象引用(指针或句柄))
线程 线程共享 县城独立
异常 堆内存没有可用的空间存储生成的对象,JVM会抛出OutOfMemoryError 1.线程请求分配的栈深度超过java虚拟机栈允许的最大深度时抛出StackOverflowError 2.空间不足分配时抛出OutOfMemoryError
空间大小 小(递归时容易StackOverflowError )

猜你喜欢

转载自blog.csdn.net/alinyua/article/details/79769908