【JVM从小白学成大佬】2.Java虚拟机运行时数据区

  • 时间:
  • 浏览:0

这里有些人儿先说句题外话,相信有些人儿在面试中总是被问到介绍Java内存模型,我在面试别人时也会总是问类似难题。因此,往往时会 令我比较尴尬,我还话音未落,面试者就会“背诵”一段(Java虚拟机时有堆、措施去、虚拟机栈,吧啦吧啦。。。),估计心里还一脸自豪的想幸好哥提前在网上搜过,早有准备。每每类似前一天,我时会 忍心打断,肯能“背诵”的真的太顺畅了!

这也怪不得面试者,首先Java虚拟机方面的知识,对中高级线程池池猿来说,工作中正面接触Java虚拟机的东西如此来太多。其次,类似其次咱得好好唠唠,网上搜个Java内存模型,度娘推的第一页大时会 介绍Java运行时数据区的,起到了一定的误导作用,大写的尴尬。

本篇将给各位小伙伴先删改介绍Java运行时数据区的组成,Java内存模型也是虚拟机上边的重点,上边会单独抽出一篇来进行介绍。

1.运行时数据区介绍

线程池池所需的内存空间,有些是不到在编译期就能选者,得要在运行期根据实际运行清况 动态地在系统中创建。Java虚拟机在执行Java线程池池的过程中会把它所管理的内存划分为若干个不同的数据区域。类似区域时会 本人 的用途,以及创建和销毁的时间,有的区域随着虚拟机线程池池的启动而趋于稳定,有些区域则依赖用户线程池池的启动和开始 而建立和销毁。

如图所示,堆和措施区是所有线程池池共享的公共区域,堆和措施区所占的内存空间是由JVM负责管理的,在该区域内的内存分配是由HotSpot的内存管理模块维护的,而内存的释放工作则由垃圾分派器自动完成。虚拟机栈、本地措施栈、线程池池计数器是线程池池的私有区域,每个线程池池都关联着唯一的栈和线程池池计数器,并仅能使用属于买车人的那份栈空间和线程池池计算器来执行线程池池。

2.堆(Heap)

对于大多数应用来说,Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。堆是可供各个线程池池共享的运行时内存区域,在虚拟机启动的前一天就被创建。此内存区域的唯一目的就是 存放对象实例,几乎所有的对象实例时会 这里分配内存。类似点在Java虚拟机规范中的描述就是 :所有的对象实例以及数组对象时会 在堆上分配。因此随着JIT编译器的发展与逃逸分析技术逐渐成长期图片 图片 期期的句子的句子的句子是什么,栈上分配、标量替换优化技术肯能愿因有些微妙的变化趋于稳定,所有的对象都分配在堆上也渐渐变得时会 如此“绝对”了。

Java堆的容量都不能 是固定的,也都不能 随着线程池池执行的需求动态扩展,并在不到如此来太多空间时自动收缩。Java堆都不能 趋于稳定物理上不连续的内存空间中,就是 逻辑上是连续的即可。肯能在堆中如此内存完成实例分配,因此堆也无法再扩展时,肯能抛出OutOfMemoryError异常。

Java堆是垃圾分派器管理的主要区域,因此就是 前一天也被称做“GC堆”(Garbage Collected Heap)。从内存回收的宽度来看,肯能现在分派器基本都采用分代分派算法,Java虚拟机将堆划分为新生代和老年代。其中,新生代又被分为Eden区,以及另另俩个 大小相同的Survivor区(From Survivor,To Survivor)。默认清况 下,Java虚拟机采取的是两种动态分配的策略(JVM参数-XX:+UsePSAdaptiveSurvivorSizePolicy),根据生成对象的带宽,以及Survivor区的使用清况 ,动态调整Eden区和Survivor区的比例。也都不能 通过参数(SurvivorRatio)来调整类似比例,SurvivorRatio类似参数就是 新生代中Eden区与Survivor区的容量比值,默认是8,代表Eden:Survivor=8:1。

算是肯能有另另俩个 对象共用一段内存的事故?

当调用new指令时,会在Eden区划出一块作为存储对象的内存。肯能堆空间是线程池池共享的,因此直接在这上边划空间是都不能进行同步的。因此,将有肯能出現另另俩个 对象共用一段内存的事故。避免措施就是 ,Java堆中肯能划出多个线程池池私有的分配缓冲区TLAB(Thread Local Allocation Buffer,对应的虚拟机参数-XX:+UseTLAB,默认开启)。

具体来说,每个线程池池都不能 向Java虚拟机申请一段连续内存,比如2048字节,作为线程池池私有的TLAB。类似操作都不能加锁,线程池池都不能维护另另俩个 指针(实际上肯能更多,但重要也就另另俩个 ),另另俩个 指向TLAB中空余内存的起始位置,另另俩个 则指向TLAB末尾。接下来的new指令,便都不能 直接通过指针加法(bump the pointer),时会 人叫做指针碰撞来实现,即把指向空余内存位置的指针上加所请求的字节数。肯能加法后空余内存指针的值仍小于或等于指向末尾的指针,则代表分配成功。因此,TLAB肯能如此足够的空间来满足本次新建操作。类似前一天,便都不能当前线程池池重新申请新的TLAB。

3.措施区(Method Area)

措施区与堆一样是线程池池共享的,在虚拟机启动的前一天创建,措施区可视为堆的另另俩个 逻辑偏离 ,因此它却另另俩个 多别又名做Non-Heap(非堆),目的应该是与Java堆区分开来。

措施区类似传统语言编译后的代码存储区域,它存储每个类的特征信息,如:

  • 常量池
  • 措施数据
  • 措施和构造函数的字节码
  • 类、实例、接口初始化时用到的特殊措施

备注:《深入理解Java虚拟机》里将措施区归纳为用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

Java虚拟机规范对措施区的限制非常宽松,除了和Java堆一样不到连续的内存和都不能 选者固定大小肯能可扩展外,还都不能 选者不实现垃圾分派。这区域的内存回收目标主就是 针对常量池的回收和对类型的卸载。

4.线程池池计数器(Program Counter Register)

Java虚拟机都不能 支持多条线程池池一起去执行,一条小Java虚拟机线程池池时会 买车人的线程池池计数器。在任意时刻,一条Java虚拟机线程池池只会执行另另俩个 措施的代码,类似正在被线程池池执行的措施称为该线程池池的当前措施(current methon)。肯能类似措施时会 native的,那线程池池计数器保存的就是 Java虚拟机正在执行的字节码指令的地址。肯能该措施是native措施,那线程池池计数器的值为空(undefined)。线程池池计数器的容量大约应当保存另另俩个 returnAddress类型的数据肯能另另俩个 与平台相关的本地指针的值。

线程池池计数器是一块较小的内存空间,它都不能 看作是当前线程池池所执行的字节码的行号指示器。此内存区域是唯一另另俩个 在Java虚拟机规范中如此规定任何OutOfMemoryError清况 的区域。

5.虚拟机栈(VM Stack)

一条小Java虚拟机线程池池时会 买车人私有的Java虚拟机栈,它的生命周期与线程池池相同。虚拟机栈描述的是Java措施执行的内存模型:每个措施在执行的一起去时会 创建另另俩个 栈帧(stack frame)用于存储局部变量表、操作数栈、动态链接、措施出口等信息。每另另俩个 措施从调用直至执行完成的过程,就对应着另另俩个 栈帧在虚拟机栈中入栈到出栈的过程。

Java虚拟机栈肯能趋于稳定如下异常清况 :

  • 肯能线程池池请求分配的栈容量超过Java虚拟机栈允许的最大容量,Java虚拟机肯能抛出另另俩个 StackOverflowError异常。
  • 肯能Java虚拟机栈都不能 动态扩展,因此在尝试扩展的前一天无法申请到足够的内存,肯能在创建新的线程池池时如此足够的内存区创建对应的虚拟机栈,那Java虚拟机肯能抛出另另俩个 OutOfMemoryError异常

6.本地措施栈(Native Method Stack)

本地措施栈与虚拟机栈所发挥的作用是非常类似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java措施(也就是 字节码)服务,而本地措施栈则为虚拟机使用到的native措施服务。

Java虚拟机规范允许本地措施栈实现成固定大小肯能根据计算来动态扩展和收缩。肯能采用固定大小的本地措施栈,如此每另另俩个 线程池池的本地措施栈容量都不能 在创建栈的前一天独立选定。

与虚拟机栈一样,本地措施栈区域也会抛出StackOverflowError和OutOfMemoryError异常。

7.扩展知识点

7.1 栈上分配和逃逸分析

在栈中分配的基本思路是那我的:分析局部变量的作用域仅限于措施内部内部结构,则JVM直接在栈帧内分配对象空间,避免在堆中分配。类似分析过程称为逃逸分析(时会 叫逸出分析),而栈帧内分配对象的措施称为栈上分配

那我做的目的是减少新生代的分派次数,间接提高JVM性能。虚拟机是允许堆逃逸分析开关进行配置的,从Sun Java 6u23前一天,HotSpot默认开启逃逸分析。

7.2 栈帧

栈帧是用于支持虚拟机进行措施调用和措施执行的数据特征,它是虚拟机运行时数据区中的虚拟机栈的栈元素。栈帧存储了措施的局部变量表、操作数栈、动态连接和措施返回地址等信息每另另俩个 措施从调用开始 至执行完成的过程,都对应着另另俩个 栈帧在虚拟机栈上边从入栈到出栈的过程。

在编译线程池池代码的前一天,栈帧中都不能多大的局部变量表,多深的操作数栈都肯能删改选者了,因此写入到措施表的Code属性之中。因此另另俩个 栈帧都不能分配几个内存,太多收到线程池池期变量数据的影响,而仅仅取决于具体的虚拟机实现。

另另俩个 线程池池中的措施调用链肯能会很长,就是 措施都一起去趋于稳定执行清况 。对于执行引擎来说,在活动线程池池中,不到趋于稳定栈顶的栈帧才是有效的,称为当前栈帧(Current Stack Frame),与类似栈帧相关联的措施称为当前措施(Current Method)。执行引擎运行的所有字节码指令都只针对当前栈帧进行操作。栈帧的概念特征如下:

8.运行时数据区脑图