深入理解Java虚拟机读书笔记 - Java运行时数据区域

运行时数据区域


Java虚拟机在执行Java程序的过程中会把它所管理的内存区域划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁。

程序计数器


是一块较小的内存空间。它可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。

由于Java多线程是通过线程轮流切换并分配处理器执行时间来实现的。所以每条线程都有自己独立的程序计数器,这样多线程程序运行时才不会错乱。所以也称其为线程“私有”内存。

需要注意的是;

  • Java方法:如果当前线程正在执行的是Java方法。这个计数器记录的是正在执行的虚拟机字节码指令的地址。
  • Native方法:如果执行Native方法,则计数器记录为空。所以是唯一一个不存在OutOfMemoryError情况的内存区域。

虚拟机栈(重要)


  • 线程私有,生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型,每个方法执行都会创建一个栈帧。
  • 栈帧(Stack Frame):用于存储局部变量表、操作数栈、动态链接、方法出口等信息。局部变量表存放编译时的8中基本数据类型、引用类型和returnAddress类型(指向一条字节码指令地址)。
  • 该区域有两种异常:
    • 线程请求栈深度大于虚拟机栈深度,抛出StackOverflowError异常。
    • 动态拓展时无法申请到足够内存,抛出OutOfMemoryError异常。

如果对栈帧所包含的概念不太清楚可以参考:

栈帧、局部变量表、操作数栈

JAVA内存结构之运行时栈帧结构

本地方法栈


与虚拟机栈功能类似,但虚拟机栈为Java方法服务,而本地方法栈为Native方法服务。也有StackOverflowError和 OutOfMemoryError异常。

Java堆(重要)


  • Java堆(Java Heap)是Java虚拟机管理的最大内存区域,虚拟机启动时创建,所有线程共享该内存。该内存唯一目的就是存放对象实例,几乎所有的对象实例都在此分配内存。
  • Java堆是垃圾收集器管理的主要区域,也被成为“GC堆”。Java堆可被细分为:新生代和老年代。
    • 新生代: 用来存放生命周期较短的对象,而新生代又使用复制算法进行GC ,又将其按照8:1:1的比例分为一块较大的Eden空间和2个较小的From Survivor和To Survivor空间。
    • 老年代:用来存放生命周期较长的对象。
  • Java堆可以处于物理上不连续内存区域,但需要逻辑上连续。
    • 如果堆中没有内存进行实例分配,并且堆也无法再拓展时(通过-Xmx和-Xms控制),将会抛出OutOfMemoryError异常。
    • Xmx Java堆最大内存,默认值为物理内存的1/4,当可用的Java堆内存大于70%时,JVM会将内存调整至-Xms所指定的初始值
    • Xms Java堆初始内存,默认值为物理内存的1/64,当可用的Java堆内存小于40%时,JVM会将内存调整至-Xmx所允许的最大值
      深入理解Java虚拟机读书笔记 - Java运行时数据区域_第1张图片

方法区(重要)


  • 方法区和Java堆一样,是各个线程的共享的区域,是系统分配的一个内存逻辑区域,它用于存放已被虚拟机加载的类信息(类的描述信息)、常量、静态变量、即时编译器编译后的代码等数据。在HotSpot里也叫“永生代”。但两者不能等同。如上图,字符串池在永生代中却在方法区外。
  • 可以处于物理上不连续内存区域,但需要逻辑上连续。
  • 又叫静态区,方法区包含所有的class和static变量以及常量。
  • 该区域的垃圾收集比较少见,主要针对常量池的回收和类型的卸载。
  • 方法区无法满足内存分配需求时,会抛出OutOfMemoryError异常。

运行时常量池

  • 属于方法区的一部分。每个类私有
  • 存放 Class 文件中的常量池(存放编译期生成的各种字面量和符号引用);翻译出来的直接引用;运行期间产生的新的常量(譬如 String 类的 intern() 方法)。
  • 常量池无法申请到内存, 会抛出OutOfMemoryError异常。

直接内存


深入理解Java虚拟机读书笔记 - Java运行时数据区域_第2张图片
  • 直接内存并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范定义中的内存区域。但这部分区域被频繁使用并可能引起OutOfMemoryError异常。
  • NIO(New Input/Output)类中 ,可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 java 堆里面的 DirectByteBuffer 对象作为这块内存的引用进行操作,避免了在 Java 堆和 Native 堆中来回复制数据。
  • 不受 Java 堆大小的限制,但受本机总内存的大小及处理器寻址空间的限制,会抛出 OutOfMemoryError异常。

参考:

汉森X:JVM学习01——内存区域及内存溢出

付小德:JVM学习心得

胖胖:https://www.zhihu.com/question/22739143

Emanue:Java中几种常量池的区分

你可能感兴趣的:(深入理解Java虚拟机读书笔记 - Java运行时数据区域)