JVM运行时数据区(详解+面试)

本文详细介绍了JVM的运行时数据区,包括程序计数器、虚拟机栈、本地方法栈、Java堆、方法区等区域的作用、特点和线程共享情况。同时,讨论了内存分配、垃圾回收、栈溢出以及面试中常见的问题,如堆内存分区、对象创建过程等。通过对这些内容的理解,有助于深入掌握JVM的工作原理和性能优化。
摘要由CSDN通过智能技术生成

运行时数据区

1.概述

JVM的运行时数据区,不同虚拟机实现可能略微不同,但都会遵从Java虚拟机规范,Java 8虚拟机规范规定,Java虚拟机所管理的内存将会包括一下几个运行时数据区域:

  1. 程序计数器(Program Counter Register)

    程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。

    java中最小的执行单位是线程,因为虚拟机的是多线程的,每个线程是抢夺cpu时间片,程序计数器就是存储这些指令去做什么,比如循环,跳转,异常处理等等需要依赖它。

    每个线程都有属于自己的程序计数器,而且互不影响,独立存储。

  2. Java虚拟机栈(Java Virtual Machine Stacks)

    描述的是Java方法执行的内存模型,每个方法在执行的同时都会创建一个线帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息,每个方法从调用直至执行完成的过程,都对应这一个线帧在虚拟机栈中入栈到出栈的过程。

    虚拟机栈

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

    与虚拟机的作用是相似的,只不过虚拟机栈是服务Java方法的,而本地方法栈是为虚拟机调用Native方法服务的,与虚拟机栈相同的是栈的深度是固定的,当线程申请的大于虚拟机栈的深度就会抛出StackOverFlowError异常,当然虚拟机栈也可以动态的扩展,如果扩展到无法申请到足够的内存就会抛出outofMemoryError异常。

  4. Java堆(Java Heap)

    是Java虚拟机中内存最大的一块,是被所有线程共享的,在虚拟机启动时候创建,Java堆唯一的目的就是存放对象实例,几乎所有的对象实例都在这里分配内存,随着JIT编译器的发展和逃逸分析技术的逐渐成熟,栈上分配、标量替换优化的技术将会导致一些微妙的变化,所有对象都分配在堆上渐渐变得不那么“绝对”了。

  5. 方法区(Method Area)

    用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。

    内存区域是很重要的系统资源,是硬盘和CPU的中间桥梁,承载着操作系统和应用程序的实时运行。JVM内存布局规定了Java在运行过程中内存申请、分配、管理的策略,保证了JVM的高效稳定运行。不同的JVM对于内存的划分方式和管理机制存在着部分差异,我们现在以使用最为流行的HotSpot虚拟机为例讲解。

Java虚拟机定义了若干中程序运行期间会使用到的运行数据区,其中有一些会随着虚拟机启动而创建,随着虚拟机退出而销毁。另外一些则是与线程一一对应的。这些与线程对应的区域会随着线程开始和结束而创建销毁。

如图:红色的为多个线程共享,灰色的为单个线程私有的,即

线程间共享:堆,对外内存。

每个线程:独立包括程序计数器,栈,本地方法栈。

运行时数据区

2.程序计数器(Program Counter Register)

  1. 概述

    JVM中的程序计数寄存器中的Register命名源于CPU的寄存器,寄存器存储指令相关的现场信息。CPU只有把数据装载到寄存器才能运行。

    这里,并非是广义上所值的物理寄存器,或许将其翻译为PC计数器(或指令计数器)会更加贴切(也称为程序钩子),并且也不容易引起一些不必要的误会。JVM中的PC寄存器是对物理PC寄存器的一种抽象模拟。

  2. 作用

    程序计数器用来存储下一条指令的地址,也即将要执行的指令代码。有执行引擎读取下一条指令。

    程序计数器

    1. 它是一块很小的内存空间几乎可以忽略不计,也是运行速度最快的存储区域。
    2. 在JVM规范中,每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程生命周期保持一致。
    3. 任何时间一个线程都只有一个方法在执行,也就是所谓的当前方法。程序计数器会存储当前线程正在执行的Java方法的JVM指令地址。如果是在执行native方法,则是未指定值(undefined)。
    4. 它是程序控制流的指示器,分支,循环,跳转,异常处理,线程恢复等基础功能都需要依赖这个计数器来完成。
    5. 字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。
    6. 它是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

    如下图所示:程序计数器的作用位置

    作用位置

  3. 面试题

    1.使用程序计数器存储字节码指令地址有什么用?为什么使用程序计数器记录当前线程的执行地址呢?

    因为CPU需要不停的切换各个线程,这时候切换回来以后,就得知道接着从哪儿开始继续执行。
    JVM的字节码解释器就需要通过改变程序计数器的值来明确下一条应该执行什么样的字节码指令。
    

    2.程序计数器为什么被设定为线程私有的

    我们都知道所谓的多线程在一个特定的时间段内只会执行其中某个线程的方法,CPU会不停地做任务切换,这样必然导致经常中断或恢复,如何保证分毫无差呢?
    为了能够准确地记录各个线程正在执行的当前字节码指令地址,最好的办法自然是为每个线程都分配应该程序计数器,这样一来各个线程之间便可以独立计算,从而不互相干扰。
    

3.虚拟机栈(Java Virtual Machine Stack)

  1. 出现背景

    • 由于跨平台性的设计,Java的指令都是根据栈来设计的。不同平台CPU架构不同,所以不能设计为基于寄存器的。

      基于栈的指令设计优点是跨平台,指令集小,编译器容易实现,缺点是性能下降,实现同样功能需要更多的指令集。

  2. 栈和堆区别

    栈是运行时的单位,而堆是存储的单元。

    解决程序的运行问题,即程序如何执行,或者说如何处理数据。

    解决的是数据存储的问题,即数据怎么放,放在哪儿。

  3. Java虚拟机栈是什么?

    • Java虚拟机栈,早期也叫Java栈。每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个栈帧,对应这一次方法的调用。
    • Java虚拟机栈是线程私有的。
    • 生命周期和线程一致。
  4. 作用

    主管Jav

  • 33
    点赞
  • 152
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值