JVM基础知识-内存结构

1.前言
本章开始学习JVM的基础知识,对于大部分开发人员来说,可能并没有花时间研究JVM的相关知识,但是他们也能完成一般程序的编写.如果我们了解JVM的相关知识,那么在代码编写,BUG调试,性能调优方面才能得心应手,不再是盲人摸象
2.目录
JVM基础知识-内存结构
文章图片
目录 3.JVM基础
3.1.内存结构

  • JVM分为5个区:虚拟机栈(VM Stack),本地方法栈(Native Method Stack),方法区(Method Area),堆(Heap),程序计数器(Program Counter Register)
  • 虚拟机栈,本地方法栈,线程计数器为线程私有,方法区和堆为线程共享区,并发访问时会阻塞
  • JVM不同区域占用的内存大小不同,一般情况下堆占用最大,存放的是对象,程序计数器最小
JVM基础知识-内存结构
文章图片
JVM内存结构 3.1.1.堆
堆里面存放是对象,几乎所有的对象实例都在此分配,随着优化技术的更新,某些数据也会被放在栈上.因为堆占用内存空间最大,堆也是Java垃圾回收的主要区域,因此也常称作"GC堆"(Garbage Collected Heap).有了GC就是相适应的策略,现代收集器基本都采用分代收集算法,堆被细化如下:

JVM基础知识-内存结构
文章图片

  • 堆的GC操作采用分带收集算法
  • 堆区分新生代和老年代
  • 新生代又分为:Eden空间,From Survivor(S0)空间,To Survivor(S1)空间
Java虚拟机规范规定,Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可.要增加堆空间时,只需完成逻辑上的扩,即可,但当堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常
3.1.2.方法区
方法区与堆都有线程共享,内存不连续可扩展,可垃圾回收,当无法再扩展时会抛出OutOfMemoryError异常等特性,Java虚拟机规范也把方法区描述为堆的一个逻辑部分,但目前实际上是与Java堆分开的(Non-Heap)
方法区存放的是已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的数据.方法区的内存回收主要目标是常量池和类型的卸载

JVM基础知识-内存结构
文章图片
方法区 3.1.3.程序计数器
程序计数器占用内存小,线程私有,唯一没有OutOfMemoryError的区域.其作用是当前程序所执行字节码的行号指示器,字节码解释器工作时通过计数器的值来选取下一条字节码指令,操作程序执行顺序,跳转,异常处理,线程恢复等基础功能
JVM基础知识-内存结构
文章图片
程序计数器 java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间来实现的,同一时刻,一个处理器(对于多核处理器来说是一个内核)只会执行一条线程中的指令.
程序计数器是私有的,这样才能恢复到正确的执行位置,各条线程之间的计数器互不影响,独立存储.执行Java方法是记录的是正在执行的虚拟机字节码指令的地址,执行Native方法计算器的值为空(Undefined)
3.1.4.虚拟机栈
虚拟机栈线程私有,生命周期与线程相同,每个方法执行都会创建一个栈帧(Stack Frame),栈帧用于虚拟机进行方法调用的数据结构,栈帧包含局部变量表,操作数栈,动态链接和方法返回地址等信息.每一个方法从调用至执行的过程,对应着一个栈帧在虚拟机从入栈到出栈的过程

JVM基础知识-内存结构
文章图片
虚拟机栈
  • 局部变量表(Local Variable Table):变量值的存储空间,存放方法参数和方法内定义的局部变量.包括8种基本数据类型,引用类型(reference类型)和returnAddress类型(指向一条字节码指令的地址)
  • 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError,
    如果虚拟机动态扩展无法申请到足够的内存时会抛出OutOfMemoryError
  • 操作数栈(Operand Stack):也称操作栈,随着方法的执行,会从局部变量表或对象实例的字段中复制常量或变量写入到操作数栈(入栈),再随着计算的进行,将栈中的元素出栈到局部变量表或者返回给方法的调用者()出栈
  • 动态链接(Dynamic Linking):每个栈帧都包含一个指向运行时常量池中该栈所属方法的符号引用,class文件的常量池中有大量的符号引用,字节码中方法的调用指令就以常量池中方法的符号引用为参数,这些符号引用一部分会在类加载阶段或者第一次使用的时候转化为直接引用(静态方法,私有方法),这种转化称为静态解析; 另外一部分将在每一次运行期间转化为直接引用,这部分称为动态链接
  • 方法返回:无论方法是否正常完成,都需要返回到方法被调用的位置,程序才能继续进行
3.1.5.本地方法栈
与虚拟机栈作用相似,也会抛出StackOverflowError和OutOfMemoryError,区别在于虚拟机栈为虚拟机执行java方法服务,而本地方法栈视为Native方法服务
3.2.JDK8 JVM内存结构
JVM基础知识-内存结构
文章图片
堆和方法区的物理存储(方法区位于永久代中)
  • 堆和方法区是在同一块连续的内存,但是在逻辑上它们是分开的
  • HotSport将GC分代扩展至方法区,即用永久代(Permanent Generation)来实现方法区,其它虚拟机没有永久代的概念.方法区是规范,永久代是hotSport针对该规范的实现
  • 永久代和老年代是捆绑在一起的,无论谁满了,都将触发永久代和老年代的垃圾收集
JVM基础知识-内存结构
文章图片
JDK8内存结构
  • JDK8中,HotSport取消了永久代,取而代之的是元空间(MetaSpace),方法区存在于元空间,元空间不再与堆连续,而是存在于本地内存
  • 本地内存(Native Memory),也称C-Heap,供JVM自身进程使用,当Java Heap空间不足不足时会触发GC,C-Heap空间不足时却不会触发GC.默认情况下可以无限使用本地内存,可设置参数限制其使用
  • 使用元空间可由系统的实际可用空间来控制,不再由参数配置上限,减少了OOM的产生,且java8的HotSport就是以前的HotSport和JRockit的合并版,JRockit没有所谓的永久代
4.总结
本章讲解JVM的内存结构,对于数据的存储和运行有了简单的了解,知道了当虚拟机无法在扩展更多的内存,且不足以存储时会抛出OutOfMemoryError异常,当栈深度超过虚拟机分配给线程的栈的大小时会抛出StackOverflowError异常
【JVM基础知识-内存结构】参考博客:
https://www.zhihu.com/question/29265430
https://juejin.im/post/5da9446751882508866e960b
https://juejin.im/post/5dae7e5de51d45249850cf9f#heading-0

    推荐阅读