JVM常见面试题(二):JVM是什么、由哪些部分组成、运行流程,JDK、JRE、JVM关系;程序计数器,堆,虚拟机栈,堆栈的区别是什么,方法区,直接内存

简介: JVM常见面试题(二):JVM是什么、由哪些部分组成、运行流程是什么,JDK、JRE、JVM的联系与区别;什么是程序计数器,堆,虚拟机栈,栈内存溢出,堆栈的区别是什么,方法区,直接内存

目录

一、JVM基本介绍——概念、组成、重点

  • 1.1 JVM是什么
  • 1.2 JVM由哪些部分组成,运行流程是什么?
  • 1.3 JDK、JRE、JVM 关系
  • 1.4 学习什么

二、JVM组成

  • 2.1 什么是程序计数器
  • 2.2 你能详细地介绍堆吗
  • 2.3 什么是虚拟机栈
  • 2.4 垃圾回收是否涉及栈内存
  • 2.5 栈内存分配越大越好吗
  • 2.6 方法内的局部变量是否线程安全
  • 2.7 什么情况下会导致栈内存溢出
  • 2.8 堆栈的区别是什么
  • 2.9 介绍下方法区
  • 2.10 直接内存
  • 2.11 总结
javap -v  xx.class    #打印堆栈大小,局部变量的数量和方法的参数

一、JVM基本介绍——概念、组成、重点

1.1 JVM是什么

JVM(Java Virtual Machine,即java虚拟机),java程序的运行环境(java二进制字节码的运行环境)。

JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。针对java用户,也就是拥有可运行的.class文件包(jar或者war)的用户。里面主要包含了jvm和java运行时基本类库(rt.jar)。rt.jar可以简单粗暴地理解为:它就是java源码编译成的jar包。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java能够“一次编译,到处运行”的原因

JVM是Java跨平台的关键,因为它屏蔽了不同操作系统之间的差异,可以让相同的Java程序在不同的操作系统上运行出相同的结果。

好处:

  • 一次编写,到处运行
  • 自动内存管理,垃圾回收机制

1-1.png

1-2.png

1.2 JVM由哪些部分组成,运行流程是什么?

1-3.png

  • JVM由哪些部分组成:类加载子系统,运行数据区(方法区、堆、程序计数器、虚拟机栈、本地方法栈),执行引擎(解释器、即使编辑器、垃圾回收)、本地库接口

  • JVM包含两个子系统和两个组件,两个子系统为Class loader(类装载器)、Execution engine(执行引擎);两个组件为Runtime data area(运行时数据区)、Native Interface(本地库接口)

    • Class loader(类加载器):根据给定的全限定名类名(如:java.lang.Object)来装载class文件到运行时数据区中的方法区;

    • Execution engine(执行引擎):执行引擎也叫解释器,负责解释命令,交由操作系统执行;

    • Native Interface(本地接口):与native libraries交互,是其它编程语言交互的接口。

    • Runtime data area(运行时数据区域):这就是我们常说的JVM的内存,我们所有写的程序都被加载到这里,之后才开始运行。由五部分组成,Method Area/MateSpace 方法区/元空间、Heap 堆、PC Register 程序计数器、JVM Stacks 虚拟机栈、Nativa Method Stacks 本地方法栈

  • 运行流程、作用 :首先通过编译器把 Java 代码转换成字节码,类加载器(ClassLoader)再把字节码加载到内存中,将其放在运行时数据区(Runtime data area)的方法区内;而字节码文件只是 JVM 的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。

  • GC垃圾回收主要针对 运行数据区中的堆空间。

1.3 JDK、JRE、JVM 关系

我们在 JVM常见面试题(一):JVM是什么、由哪些部分组成、运行流程是什么,JDK、JRE、JVM的联系与区别 中已详细介绍过JDK、JRE、JVM的联系与区别,此处简单讲解下:

JDK(Java Development Kit,Java开发工具包)、JRE(Java Runtime Environment,Java运行时环境)、JVM(Java Virtual Machine,即java虚拟机)。

JDK是 Java 语言的软件开发工具包(SDK)。在JDK的安装目录下有一个jre目录,里面有两个文件夹bin和lib,在这里可以认为bin里的就是jvm,lib中则是jvm工作所需要的类库,而jvm和 lib合起来就称为jre。

1-4.png

  • JDK包含了JRE,JRE包含了JVM。
  • 如果只想运行Java程序,只需安装JRE即可(少数情况例外);如果想要开发Java程序,则需要安装JDK。
  • JDK是用于java程序的开发,而jre则是只能运行class而没有编译的功能;Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够“一次编译,到处运行”的原因。

1-5.png

1-6.png

1-7.png

1.4 学习什么

1-8.png

1-9.png

二、JVM组成

2.1 什么是程序计数器

程序计数器:线程私有的,内部保存的字节码的行号。用于记录正在执行的字节码指令的地址。

2-1.png

javap -v xx.class    #打印堆栈大小,局部变量的数量和方法的参数

2-2.png

2.2 你能详细地介绍堆吗

线程共享的区域:主要用来保存对象实例,数组等,当堆中没有内存空间可分配给实例,也无法再扩展时,则抛出OutOfMemoryError异常。

2-3.png

Java堆主要组成部分:

  • 元空间保存的类信息、静态变量、常量、编译后的代码(Jdk1.8引入)
  • 年轻代被划分为三部分,Eden区和两个大小严格相同的Survivor区,根据JVM的策略,在经过几次垃圾收集后,任然存活于Survivor的对象将被移动到老年代区间。
  • 老年代主要保存生命周期长的对象,一般是一些老的对象

2-4.png

总结:你能详细地介绍Java堆吗

  • 线程共享的区域:主要用来保存对象实例,数组等,内存不够则抛出OutOfMemoryError异常
  • 组成:年轻代+老年代
    • 年轻代被划分为三部分,Eden区和两个大小严格相同的Survivor区
    • 老年代主要保存生命周期长的对象,一般是一些老的对象
  • Jdk1.7和1.8的区别
    • 1.7中有有一个永久代,存储的是类信息、静态变量、常量、编译后的代码
    • 1.8移除了永久代,把数据存储到了本地内存的元空间中,防止内存溢出

2.3 什么是虚拟机栈

Java Virtual machine Stacks (java 虚拟机栈),早期也叫Java栈。每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个栈帧(stack Frame) ,对应着一次次的Java方法调用。一次方法的调用,就是栈帧入栈到出栈的过程;

虚拟机栈是线程私有的,生命周期和线程一致,其作用为 主管Java程序的运行,它保存方法的局部变量(8大基本数据类型、对象的引用地址)、部分结果,并参与方法的调用和返回

每个线程都有自己的栈,栈中的数据都是以栈帧(stack Frame)的格式存在。栈帧是一个内存区块,是一个数据集,维系着方法执行过程中的各种数据信息,存储:局部变量表(Local variables)、操作数栈(operand stack) (或表达式栈)、动态链接(Dynamic Linking) (或指向运行时常量池的方法引用)、方法返回地址(Return Address) (或方法正常退出或者异常退出的定义)、一些附加信息

  • 每个线程运行时所需要的内存,称为虚拟机栈,先进后出
  • 每个栈由多个栈帧(frame)组成,对应着每次方法调用时所占用的内存。栈帧对应的方法执行完后,栈会将该方法对应的栈帧弹出栈,释放内存
  • 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

2-5.png

2-6.png

多线程下

2-7.png

public class StackTest {
   
    public static void main(String[] args) {
   
        StackTest stackTest = new StackTest();
        stackTest.methodA();
    }

    public void methodA() {
   
        int i = 10;
        int j = 20;
        methodB();
    }

    private void methodB() {
   
        int k = 30;
        int m = 40;
    }
}

2-8.png

补充:本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的。其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。

2.4 垃圾回收是否涉及栈内存

垃圾回收主要指就是堆内存,不涉及栈内存,当栈帧弹栈以后,内存就会释放。

1)什么是垃圾回收?

垃圾回收(Garbage Collection,GC),顾名思义就是释放垃圾占用的空间,防止内存泄露。有效地使用可以使用的内存,对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收。

2)结合2.5,因为栈帧对应的方法执行完后,栈会将该方法对应的栈帧弹出栈,释放内存,因此垃圾回收不涉及栈内存。

2.5 栈内存分配越大越好吗

未必,默认的栈内存通常为1024k

栈帧过大会导致线程数变少,例如,机器总内存为512m,目前能活动的线程数则为512个(512m/1024k=512);如果把栈内存改为2048k,那么能活动的栈帧就会减半(512m/2048k=256)

(栈内存的大小不会影响方法执行的速度,而且由于计算机硬件的储存大小是有限的,栈空间内存设置过大,创建线程数量较多时会出现栈内存溢出OutofMemoryError,导致最大线程数减少,得不偿失。同时,栈内存也决定方法调用的深度,栈内存过小则会导致方法调用的深度较小,如递归调用的次数较少)

2.6 方法内的局部变量是否线程安全

  • 如果方法内局部变量没有逃离方法的作用范围,它是线程安全的
  • 如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全

2-9.png

2.7 什么情况下会导致栈内存溢出

  • 栈帧过多导致栈内存溢出。典型问题:递归调用,没有结束语句,一直递归调用方法,导致栈帧过多、栈内存溢出
  • 栈帧过大导致栈内存溢出。单个栈帧的所需要的内存超出了栈内存大小
public static void m4(){
   
    m4();
}

java.lang.StackOverflowError

2.8 堆栈的区别是什么

  • 栈内存一般会用来存储局部变量和方法调用,但堆内存是用来存储Java对象和数组的的。堆会GC垃圾回收,而栈不会。
  • 栈内存是线程私有的,而堆内存是线程共有的。
  • 两者异常错误不同,但如果栈内存或者堆内存不足都会抛出异常。
    • 栈空间不足:java.lang.StackOverFlowError。
    • 堆空间不足:java.lang.OutOfMemoryError。

2.9 介绍下方法区

  • 方法区(Method Area)是各个线程共享的内存区域
  • 主要存储类的信息、运行时常量池
  • 虚拟机启动的时候创建,关闭虚拟机时释放
  • 如果方法区域中的内存无法满足分配请求,则会抛出OutOfMemoryError: Metaspace

2-10.png

常量池

可以看作是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息

javap -v Application.class    #查看字节码结构(类的基本信息、常量池、方法定义)

2-11.png

运行时常量池

常量池是 *.class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址

2-12.png

2.10 直接内存

直接内存:并不属于JVM中的内存结构,不由JVM进行管理。是虚拟机的系统内存,常见于 NIO 操作时,用于数据缓冲区,它分配回收成本较高,但读写性能高。

举例:Java代码完成文件拷贝

2-13.png

常规IO的数据拷贝流程

2-14.png

NIO数据拷贝流程

2-15.png

2.11 总结

1)什么是程序计数器

线程私有的,每个线程一份,内部保存的字节码的行号。用于记录正在执行的字节码指令的地址。

2)你能详细地介绍Java堆吗

  • 线程共享的区域:主要用来保存对象实例,数组等,内存不够则抛出OutOfMemoryError异常
  • 组成:年轻代+老年代
    • 年轻代被划分为三部分,Eden区和两个大小严格相同的Survivor区
    • 老年代主要保存生命周期长的对象,一般是一些老的对象
  • Jdk1.7和1.8的区别
    • 1.7中有有一个永久代,存储的是类信息、静态变量、常量、编译后的代码
    • 1.8移除了永久代,把数据存储到了本地内存的元空间中,防止内存溢出

3)什么是虚拟机栈

每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个栈帧(stack Frame) ,对应着一次次的Java方法调用。一次方法的调用,就是栈帧入栈到出栈的过程。

  • 每个线程运行时所需要的内存,称为虚拟机栈,先进后出
  • 每个栈由多个栈帧(frame)组成,对应着每次方法调用时所占用的内存。栈帧对应的方法执行完后,栈会将该方法对应的栈帧弹出栈,释放内存
  • 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

4)垃圾回收是否涉及栈内存

垃圾回收主要指就是堆内存,不涉及栈内存,当栈帧弹栈以后,内存就会释放。

5)栈内存分配越大越好吗

未必,默认的栈内存通常为1024k,栈帧过大会导致线程数变少。

6)方法内的局部变量是否线程安全

  • 如果方法内局部变量没有逃离方法的作用范围,它是线程安全的
  • 如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全

7)什么情况下会导致栈内存溢出

  • 栈帧过多导致栈内存溢出。典型问题:递归调用,没有结束语句,一直递归调用方法,导致栈帧过多、栈内存溢出
  • 栈帧过大导致栈内存溢出。单个栈帧的所需要的内存超出了栈内存大小

8)堆栈的区别是什么

  • 栈内存一般会用来存储局部变量和方法调用,但堆内存是用来存储Java对象和数组的的。堆会GC垃圾回收,而栈不会。
  • 栈内存是线程私有的,而堆内存是线程共有的。
  • 两者异常错误不同,但如果栈内存或者堆内存不足都会抛出异常。
    • 栈空间不足:java.lang.StackOverFlowError。
    • 堆空间不足:java.lang.OutOfMemoryError。

9)解释一下方法区

  • 方法区(Method Area)是各个线程共享的内存区域
  • 主要存储类的信息、运行时常量池
  • 虚拟机启动的时候创建,关闭虚拟机时释放
  • 如果方法区域中的内存无法满足分配请求,则会抛出OutOfMemoryError: Metaspace

10)介绍一下运行时常量池

  • 常量池:可以看作是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息
  • 当类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址

11)直接内存

  • 并不属于JVM中的内存结构,不由JVM进行管理。是虚拟机的系统内存
  • 常见于 NIO 操作时,用于数据缓冲区,分配回收成本较高,但读写性能高,不受 JVM 内存回收管理

参考 黑马程序员相关视频与笔记

相关文章
|
28天前
|
Java
程序计数器和虚拟机栈
这篇文章介绍了Java虚拟机(JVM)的内存结构,特别解释了程序计数器(Program Counter Register)的作用,即用来记录下一条JVM指令的执行地址和行号。
程序计数器和虚拟机栈
|
19天前
|
消息中间件 设计模式 安全
多线程魔法:揭秘一个JVM中如何同时运行多个消费者
【8月更文挑战第22天】在Java虚拟机(JVM)中探索多消费者模式,此模式解耦生产与消费过程,提升系统性能。通过`ExecutorService`和`BlockingQueue`构建含2个生产者及4个消费者的系统,实现实时消息处理。多消费者模式虽增强处理能力,但也引入线程安全与资源竞争等挑战,需谨慎设计以确保高效稳定运行。
48 2
|
28天前
|
存储 设计模式 Java
Unity精华☀️ 面试“堆、栈”误区!这样做可能反而会降低吸引力
Unity精华☀️ 面试“堆、栈”误区!这样做可能反而会降低吸引力
|
10天前
|
C# Windows 开发者
当WPF遇见OpenGL:一场关于如何在Windows Presentation Foundation中融入高性能跨平台图形处理技术的精彩碰撞——详解集成步骤与实战代码示例
【8月更文挑战第31天】本文详细介绍了如何在Windows Presentation Foundation (WPF) 中集成OpenGL,以实现高性能的跨平台图形处理。通过具体示例代码,展示了使用SharpGL库在WPF应用中创建并渲染OpenGL图形的过程,包括开发环境搭建、OpenGL渲染窗口创建及控件集成等关键步骤,帮助开发者更好地理解和应用OpenGL技术。
46 0
|
17天前
|
Java 编译器 开发工具
JDK vs JRE:面试大揭秘,一文让你彻底解锁Java开发和运行的秘密!
【8月更文挑战第24天】JDK(Java Development Kit)与JRE(Java Runtime Environment)是Java环境中两个核心概念。JDK作为开发工具包,不仅包含JRE,还提供编译器等开发工具,支持Java程序的开发与编译;而JRE仅包含运行Java程序所需的组件如JVM和核心类库。一个简单的"Hello, World!"示例展示了两者用途:需借助JDK编译程序,再利用JRE或JDK中的运行环境执行。因此,开发者应基于实际需求选择安装JDK或JRE。
37 0
|
26天前
|
Oracle Java 关系型数据库
简单记录在Linux上安装JDK环境的步骤,以及解决运行Java程序时出现Error Could not find or load main class XXX问题
本文记录了在Linux系统上安装JDK环境的步骤,并提供了解决运行Java程序时出现的"Error Could not find or load main class XXX"问题的方案,主要是通过重新配置和刷新JDK环境变量来解决。
58 0
|
28天前
|
安全 Java
虚拟机栈的五道面试题
这篇文章提供了关于Java虚拟机栈的五个面试问题,涉及栈溢出的情况、栈大小调整、栈内存的分配、垃圾回收与虚拟机栈的关系以及局部变量的线程安全性。
|
23天前
|
Java Docker 索引
记录一次索引未建立、继而引发一系列的问题、包含索引创建失败、虚拟机中JVM虚拟机内存满的情况
这篇文章记录了作者在分布式微服务项目中遇到的一系列问题,起因是商品服务检索接口测试失败,原因是Elasticsearch索引未找到。文章详细描述了解决过程中遇到的几个关键问题:分词器的安装、Elasticsearch内存溢出的处理,以及最终成功创建`gulimall_product`索引的步骤。作者还分享了使用Postman测试接口的经历,并强调了问题解决过程中遇到的挑战和所花费的时间。
|
21天前
|
存储 算法 Oracle
不好意思!耽误你的十分钟,JVM内存布局还给你
先赞后看,南哥助你Java进阶一大半在2006年加州旧金山的JavaOne大会上,一个由顶级Java开发者组成的周年性研讨会,公司突然宣布将开放Java的源代码。于是,下一年顶级项目OpenJDK诞生。Java生态发展被打开了新的大门,Java 7的G1垃圾回收器、Java 8的Lambda表达式和流API…大家好,我是南哥。一个Java学习与进阶的领路人,相信对你通关面试、拿下Offer进入心心念念的公司有所帮助。
不好意思!耽误你的十分钟,JVM内存布局还给你
|
29天前
|
存储 算法 Java
JVM自动内存管理之垃圾收集算法
文章概述了JVM内存管理和垃圾收集的基本概念,提供一个关于JVM内存管理和垃圾收集的基础理解框架。
JVM自动内存管理之垃圾收集算法