Java面试题、八股文学习之JVM篇

ops/2024/11/27 7:02:29/

1、知识点汇总

JVM是Java运行基础,面试时一定会遇到JVM的有关问题,内容相对集中,但对只是深度要求较高。

在这里插入图片描述

重点包括内存模型、类加载机制和垃圾回收(GC)。性能调优侧重应用实践,编译器优化与执行模式侧重理论基础。需掌握内存模型的各部分功能和数据保存;类加载的双亲委派机制及各类加载器的使用;GC的分代回收思想、算法与适用场景;性能调优的JVM参数及工具的应用;执行模式中的解释、编译、混合模式优缺点,以及JIT即时编译、OSR栈替换、C1/C2编译器优化等。新技术包括Java 10的Graal编译器,javac编译过程优化及AST抽象语法树。

2、知识点详解:

1、JVM内存模型:

线程独占:栈,本地方法栈,程序计数器 线程共享:堆,方法区

2、栈:

又称方法栈,线程私有的,线程执行方法是都会创建一个栈阵,用来存储局部变量表,操作栈,动态链接,方 法出口等信息.调用方法时执行入栈,方法返回式执行出栈.

3、本地方法栈

与栈类似,也是用来保存执行方法的信息.执行Java方法是使用栈,执行Native方法时使用本地方法栈.

4、程序计数器

保存着当前线程执行的字节码位置,每个线程工作时都有独立的计数器,只为执行Java方法服务,执行 Native方法时,程序计数器为空.

5、堆

JVM内存管理最大的一块,对被线程共享, 目的是存放对象的实例,几乎所欲的对象实例都会放在这里, 当堆没有可用空间时,会抛出OOM异常.根据对象的存活周期不同,JVM把对象进行分代管理,由垃圾回 收器进行垃圾的回收管理

6、方法区:

又称非堆区,用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器优化后的代码等数据.1.7 的永久代和1.8的元空间都是方法区的一种实现

7、JVM 内存可见性

在这里插入图片描述

JMM是定义程序中变量的访问规则,线程对于变量的操作只能在自己的工作内存中进行,而不能直接对主内存操作.由于指令重排序,读写的顺序会被打乱。因此JMM需要提供原子性,可见性,有序性保证.

在这里插入图片描述

3、说说类加载与卸载

加载过程
在这里插入图片描述

其中验证,准备,解析合称 链接

  • 加载通过类的完全限定名,查找此类字节码文件,利用字节码文件创建Class对象。
  • 验证确保Class文件符合当前虚拟机的要求,不会危害到虚拟机自身安全。
  • 准备进行内存分配,为static修饰的类变量分配内存,并设置初始值(0或null).不包含final修饰的静态变 量,因为final变量在编译时分配.
  • 解析将常量池中的符号引用替换为直接引用的过程.直接引用为直接指向目标的指针或者相对偏移量等。
  • 初始化主要完成静态块执行以及静态变量的赋值.先初始化父类,再初始化当前类.只有对类主动使用 时才会初始化。
  • 触发条件包括,创建类的实例时,访问类的静态方法或静态变量的时候,使用Class.forName反射类的时候,或者某个子类初始化的时候。
  • Java自带的加载器加载的类,在虚拟机的生命周期中是不会被卸载的,只有用户自定义的加载器加载的类才可以被卸。

1、加载机制-双亲委派模式

在这里插入图片描述

双亲委派模式,即加载器加载类时先把请求委托给自己的父类加载器执行,直到顶层的启动类加载器. 父类加载器能够完成加载则成功返回,不能则子类加载器才自己尝试加载。

优点:

  1. 避免类的重复加载
  2. 避免Java的核心API被篡改

2、分代回收
分代回收基于两个事实:大部分对象很快就不使用了,还有一部分不会立即无用,但也不会持续很长时间。
在这里插入图片描述

  • 年轻代->标记-复制
  • 老年代->标记-清除

3、回收算法

a、 G1算法
1.9后默认的垃圾回收算法,特点保持高回收率的同时减少停顿.采用每次只清理一部分,而不是清理全 部的增量式清理,以保证停顿时间不会过长。

其取消了年轻代与老年代的物理划分,但仍属于分代收集器,算法将堆分为若干个逻辑区域(region),一 部分用作年轻代,一部分用作老年代,还有用来存储巨型对象的分区。

同CMS相同,会遍历所有对象,标记引用情况,清除对象后会对区域进行复制移动,以整合碎片空间。

年轻代回收: 并行复制采用复制算法,并行收集,会StopTheWorld。

老年代回收: 会对年轻代一并回收。

初始标记完成堆root对象的标记,会StopTheWorld.并发标记 GC线程和应用线程并发执行. 最终标记 完成三色标记周期,会StopTheWorld.复制/清楚会优先对可回收空间加大的区域进行回收。

b、ZGC算法
前面提供的高效垃圾回收算法,针对大堆内存设计,可以处理TB级别的堆,可以做到10ms以下的回收停顿时间。

在这里插入图片描述

  • 着色指针
  • 读屏障
  • 并发处理
  • 基于region
  • 内存压缩(整理)

roots标记:标记root对象,会StopTheWorld。
并发标记:利用读屏障与应用线程一起运行标记,可能 会发生StopTheWorld.清除会清理标记为不可用的对象。
roots重定位是对存活的对象进行移动,以腾出大块内存空间,减少碎片产生。重定位最开始会StopTheWorld,却决于重定位集与对象总活动集的比例。 并发重定位与并发标记类似。

4.简述一下JVM的内存模型

JVM(Java虚拟机)的内存模型定义了Java程序在运行时,JVM如何管理和使用内存。它包括程序运行时所需的各个内存区域,并规定了这些区域如何分配、使用和回收。JVM内存模型的设计非常关键,因为它直接影响程序的性能、稳定性及并发执行的行为。

JVM内存模型的主要组成部分:

1.方法区(Method Area)

功能:方法区用于存储类的相关信息(如类的结构、字段、方法、常量池、静态变量等),是所有线程共享的内存区域。

特点

  • 存储类信息、常量池、静态变量和即时编译(JIT)编译后的代码。
  • 由于类的加载、卸载和回收,方法区可能会经历频繁的垃圾回收。
  • 在早期的JVM中,方法区和永久代(PermGen)是同一块内存区域,但在Java 8及以后,永久代被**元空间(Metaspace)**替代,元空间不再在堆内存中,而是存储在本地内存中。

2.堆(Heap)

功能:堆是JVM中最大的内存区域,用于存储所有的对象实例。它是垃圾回收器(GC)管理的主要区域,所有的对象实例和数组都在堆中创建。

特点

  • 堆是所有线程共享的内存区域。
  • 通过垃圾回收器来管理内存,避免内存泄漏。
  • 堆通常分为年轻代(Young Generation)和老年代(Old Generation),其中年轻代存放新创建的对象,而老年代用于存放生命周期较长的对象。

3.栈(Stack)

功能:每个线程都有自己的栈,用于存储局部变量、方法调用的栈帧、返回地址等。栈内存也用于方法的调用和返回。

特点

  • 每个线程有独立的栈。
  • 局部变量、方法调用、返回地址等信息都保存在栈中。
  • 栈是基于先进后出(LIFO)方式存储的,因此每次方法调用时都会创建一个新的栈帧,方法执行完后,栈帧就会被销毁。

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

功能:程序计数器是一个较小的内存区域,用于记录当前线程所执行的字节码的行号指示器。它能够跟踪正在执行的代码位置,以便程序能够顺利地进行线程切换。

特点

  • 每个线程都有一个独立的程序计数器。
  • 线程上下文切换时,程序计数器保存当前线程的执行位置。

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

功能:本地方法栈是用于支持JVM与本地(非Java)代码交互的栈。它与JVM的栈类似,但用于执行本地方法(如C、C++等语言编写的代码)。

特点

本地方法栈主要用于执行本地方法,调用本地代码时会创建本地方法的栈帧。

6.直接内存(Direct Memory)

功能:直接内存并非JVM内存的一部分,但它是通过nio(New Input/Output)类库对本地内存的直接访问。这允许JVM绕过堆内存,通过直接与操作系统内存交互来提高性能。

特点

通过DirectByteBuffer直接访问本地内存,可以减少内存复制,提高I/O性能。

JVM内存模型的内存区域划分

  • 堆内存:存储对象实例和数组,是垃圾回收的主要区域。堆被划分为年轻代(Young Generation)和老年代(Old Generation):

  • 年轻代:存储大部分新创建的对象,垃圾回收较频繁。年轻代又分为三个区域:

    • Eden区:新创建的对象通常会被分配到这里。
    • Survivor区:存放经过一次或多次垃圾回收的对象。
  • 老年代:存储生命周期较长的对象,垃圾回收相对较少。长期存活的对象会被转移到老年代。

  • 方法区:存储类的元数据(如类的结构、方法、字段、常量等),它是所有线程共享的区域。

  • 栈内存:每个线程都有自己的栈空间,用于存放局部变量和方法的调用。

  • 程序计数器:每个线程都有一个程序计数器,用于记录程序当前的执行位置。

  • 本地方法栈:与栈类似,但用于执行本地方法。

JVM内存模型与并发

JVM的内存模型在多线程环境中至关重要,尤其是关于内存可见性和有序性的问题。Java的内存模型(JMM)规定了多线程并发执行时,如何保证各个线程之间对共享变量的访问是一致的。它主要涉及以下几个方面:

  • 可见性:一个线程对共享变量的修改,其他线程能够及时看到。
  • 原子性:操作的不可分割性,保证某个操作要么完全执行,要么完全不执行。
  • 有序性:程序指令的执行顺序,不会随意重排。

通过 synchronized 关键字和 volatile 关键字,Java提供了对并发访问的控制,确保了线程安全。

总结:

JVM内存模型包括了多个重要的内存区域,每个区域负责特定的任务。堆用于存储对象,栈用于存储局部变量和方法信息,方法区存储类信息,程序计数器帮助管理线程执行,直接内存则提供了与操作系统内存的直接交互。通过合理管理这些内存区域,JVM能够提供高效的内存管理和优化,支持多线程和并发执行。

5.说说堆和栈的区别

堆(Heap)和栈(Stack)是计算机内存管理中的两个重要区域,它们在用途、分配方式、生命周期等方面有许多区别。以下是堆和栈的主要区别:

1. 内存分配方式

栈:

  • 栈内存由操作系统自动管理,分配和回收的速度非常快。栈内存采用先进后出(LIFO)原则进行分配和回收。
  • 每当一个方法被调用时,系统会为该方法分配一个栈帧,存储方法的局部变量、参数和返回地址。当方法调用结束时,栈帧会被销毁,内存空间被释放。
    堆:
  • 堆内存由JVM或操作系统管理,分配和回收相对较慢。堆内存是动态分配的,可以在程序运行时灵活地分配空间。
  • 堆内存用于存储对象实例和数组。对象在堆中分配内存,而这些对象的生命周期由垃圾回收器管理。

2. 内存空间的大小

栈:

  • 栈的大小通常较小,受到操作系统或JVM的限制。每个线程都有自己的栈空间,因此栈的内存大小通常是有限的。
  • 栈的空间比较紧张,适合存储局部变量和函数调用等小规模的数据。

堆:

  • 堆的空间通常比较大,且由JVM动态管理,可以根据需要进行扩展。堆的大小可以通过JVM启动参数来配置。
  • 堆内存主要用于存储程序中创建的对象和数组,适合存储较大的数据结构。

3. 存储内容

栈:

  • 存储局部变量、方法调用信息(如返回地址、函数参数等)以及每个方法的栈帧。
  • 每个线程都有一个独立的栈,栈内存只存储当前线程的相关数据。

堆:

  • 存储对象实例和数组。这些对象由JVM的垃圾回收器进行管理。
  • 堆内存中的对象可以被多个线程共享访问,因此需要注意线程安全。

4. 生命周期

栈:

栈的生命周期由方法调用决定。当一个方法被调用时,栈为该方法分配栈帧,并在方法调用结束时释放栈帧。因此栈中的数据(如局部变量和方法调用信息)具有自动管理的生命周期

堆:

堆中的对象生命周期由垃圾回收器(GC)管理。当一个对象不再被引用时,垃圾回收器会回收该对象占用的堆内存空间。堆内存的管理更加灵活,但也更复杂。

5. 访问速度

栈:

栈的访问速度非常快,因为栈的内存分配和回收遵循严格的顺序(LIFO),每次分配和回收都是在栈的顶端进行。栈是连续的内存空间,操作系统为其分配的内存通常是高效的。

堆:

堆的访问速度相对较慢,因为堆内存的分配和回收是动态的。堆内存可能会因为频繁的分配和垃圾回收而产生碎片化,影响性能。

6. 线程共享

栈:

每个线程都有自己的栈空间,栈内存是线程私有的,其他线程不能访问当前线程的栈。

堆:

堆内存是所有线程共享的,多个线程可以同时访问堆中的对象。因此,在多线程环境下,堆中的数据需要特别小心,避免数据竞争和同步问题。

7. 垃圾回收

栈:

栈中的内存是自动回收的,每次方法结束时,栈帧会被销毁,栈内存也会被释放。

堆中的内存则由垃圾回收器(GC)负责回收。垃圾回收器会定期扫描堆内存,找出不再被引用的对象,并释放它们占用的内存。

8. 适用场景

栈:

栈适用于存储局部变量和方法调用信息,它非常适合用于函数调用、递归等操作。

堆:

堆适用于存储对象、数组等动态数据结构。它适合用于存储生命周期较长的对象,比如在Java中通过new关键字创建的对象都会被分配到堆上。

特性堆(Heap)栈(Stack)
分配方式动态分配,通过JVM管理静态分配,由操作系统自动管理
存储内容对象实例、数组局部变量、方法调用信息
生命周期由垃圾回收器管理,直到没有引用为止随着方法调用结束自动销毁
线程共享所有线程共享每个线程有独立的栈
访问速度较慢,因涉及动态分配和垃圾回收快,栈内存是连续的、按顺序分配的
内存大小较大,可以根据需要调整较小,受操作系统限制
管理方式由垃圾回收器管理由操作系统自动管理

总的来说,栈和堆各自有不同的特点和适用场景。栈适合存储局部变量和执行上下文,速度快且自动回收;堆适合存储对象和数组,适用于动态数据分配,但需要垃圾回收器管理内存,速度较慢。


http://www.ppmy.cn/ops/137018.html

相关文章

【强化学习的数学原理】第02课-贝尔曼公式-笔记

学习资料:bilibili 西湖大学赵世钰老师的【强化学习的数学原理】课程。链接:强化学习的数学原理 西湖大学 赵世钰 文章目录 一、为什么return重要?如何计算return?二、state value的定义三、Bellman公式的详细推导四、公式向量形式…

【纪念365天】我的创作纪念日

过去的一年 没有注意加入csdn已经有一年了。 这几天翻看小猴儿的通知才发现时间来到了一年的纪念日。稍稍思索想要将这一段时间的学习到的知识以及偶然遇到的机遇做一下总结。 上一次写纪念日是来到csdn128天的时候, 200天前我的学习状态是非常疯狂的。 只记得我当时…

Python人工智能项目报告

一、实践概述 1、实践计划和目的 在现代社会,计算机技术已成为支撑社会发展的核心力量,渗透到生活的各个领域,应关注人类福祉,确保自己的工作成果能够造福社会,同时维护安全、健康的自然环境,设计出具有包…

SpringBoot(三十九)SpringBoot集成RabbitMQ实现流量削峰添谷

前边我们有具体的学习过RabbitMQ的安装和基本使用的情况。 但是呢&#xff0c;没有演示具体应用到项目中的实例。 这里使用RabbitMQ来实现流量的削峰添谷。 一&#xff1a;添加pom依赖 <!--rabbitmq-需要的 AMQP 依赖--> <dependency><groupId>org.springfr…

卷积神经网络学习记录

目录 神经网络基础定义&#xff1a; 基本组成部分 工作流程 卷积层&#xff08;卷积定义&#xff09;【CONV】&#xff1a; 卷积层&#xff08;Convolutional Layer&#xff09; 特征提取&#xff1a;卷积层的主要作用是通过卷积核&#xff08;或滤波器&#xff09;运算提…

esp32触发相机

esp32触发相机&#xff0c;测试成功上升沿触发 串口发送命令 up 20000 1 20000 触发 #include <Arduino.h>const int outputPin 12; // 输出引脚 String inputCommand ""; // 串口输入缓冲区// 解析命令参数&#xff0c;例如 "up 10 5" 解析为…

基于nxp LS1046+fpga的嵌入式系统中虚拟化设备的设计与实现

3 虚拟化设备仿真平台设计 本文需要设计和实现的虚拟化设备需要搭建一个仿真平台&#xff0c;一个完善的仿真平台才 是一种虚拟化设备能搭建起来的关键&#xff0c;仿真平台的搭建需要一定条件的硬件环境&#xff0c;更为 主要的是软件环境&#xff0c;下文就要详细介绍此虚…

three.js 修改模型的材质属性 并结合gsap材质颜色修改动画

three.js 修改模型的材质属性 并结合gsap材质颜色修改动画 import * as THREE from three import { OrbitControls } from three/examples/jsm/controls/OrbitControls.js import { GLTFLoader } from three/examples/jsm/loaders/GLTFLoader.js import { DRACOLoader } from…