首页 > 其他分享 >上周面了百度,问的很细~

上周面了百度,问的很细~

时间:2024-06-17 15:42:56浏览次数:12  
标签:volatile 对象 线程 内存 JVM 上周 CPU 百度

上周刚刚面了百度,问的问题不算很难,但却很细,我把这些面试题和答案都整理出来了,一起来看吧。

重点介绍一个你觉得有意义的项目?

回答技巧和思路:

  1. 介绍的项目业务难度和技术难点要高一些,最好是微服务项目。
  2. 简明扼要的讲清楚项目核心板块的业务场景即可,切忌不要讲的太细和太久,这只是面试官要考察你技术问题的一个触手。
  3. 讲清楚自己在项目中负责的模块。
  4. 讲清楚项目的亮点是啥。
  5. 讲清楚遇到了哪些棘手的问题?以及最终的解决方案。

synchronize锁升级过程?

synchronized 锁升级有两个版本:

  1. JDK 1.6 synchronized 锁升级:无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁。
  2. JDK 15 synchronized 锁升级:无锁 -> 轻量级锁 -> 重量级锁。

注意:回答的过程中,最好能讲明白每种锁的概念和特征,以及为什么 JDK 15 要废除偏向锁?

synchronize作用在普通方法和静态方法上有什么区别?

synchronized 作用在静态方法是类级别的,而作用在普通方法是对象实例级别,因此作用在静态方法时,锁范围更大、性能也更低。

synchronized实现原理?

synchronized 同步锁是通过 JVM 内置的 Monitor 监视器实现的,而监视器又是依赖操作系统的互斥锁 Mutex 实现的。

例如,以下代码中使用了 synchronized 修饰:

public class SynchronizedToMonitorExample {
    public static void main(String[] args) {
        int count = 0;
        synchronized (SynchronizedToMonitorExample.class) {
            for (int i = 0; i < 10; i++) {
                count++;
            }
        }
        System.out.println(count);
    }
}

当我们将上述代码编译成字节码之后,得到的结果是这样的:

从上述结果我们可以看出,在 main 方法中多了一对 monitorenter 和 monitorexit 的指令,它们的含义是:

  • monitorenter:表示进入监视器。
  • monitorexit:表示退出监视器。

由此可知 synchronized 是依赖 Monitor 监视器实现的。

volatile作用和实现原理?

volatile 作用有以下两个:

  1. 保证内存可见性;
  2. 保证有序性(禁止指令重排序)。

① 内存可见性实现原理

volatile 内存可见性主要通过 lock 前缀指令实现的,它会锁定当前内存区域的缓存(缓存行),并且立即将当前缓存行数据写入主内存(耗时非常短),回写主内存的时候会通过 MESI 协议使其他线程缓存了该变量的地址失效,从而导致其他线程需要重新去主内存中重新读取数据到其工作线程中。

什么 MESI 协议?

MESI 协议,全称为 Modified, Exclusive, Shared, Invalid,是一种高速缓存一致性协议。它是为了解决多处理器(CPU)在并发环境下,多个 CPU 缓存不一致问题而提出的。
MESI 协议定义了高速缓存中数据的四种状态:

  1. Modified(M):表示缓存行已经被修改,但还没有被写回主存储器。在这种状态下,只有一个 CPU 能独占这个修改状态。
  2. Exclusive(E):表示缓存行与主存储器相同,并且是主存储器的唯一拷贝。这种状态下,只有一个 CPU 能独占这个状态。
  3. Shared(S):表示此高速缓存行可能存储在计算机的其他高速缓存中,并且与主存储器匹配。在这种状态下,各个 CPU 可以并发的对这个数据进行读取,但都不能进行写操作。
  4. Invalid(I):表示此缓存行无效或已过期,不能使用。

MESI 协议的主要用途是确保在多个 CPU 共享内存时,各个 CPU 的缓存数据能够保持一致性。当某个 CPU 对共享数据进行修改时,它会将这个数据的状态从 S(共享)或 E(独占)状态转变为 M(修改)状态,并等待适当的时机将这个修改写回主存储器。同时,它会向其他 CPU 广播一个“无效消息”,使得其他 CPU 将自己缓存中对应的数据状态转变为I(无效)状态,从而在下次访问这个数据时能够从主存储器或其他 CPU 的缓存中重新获取正确的数据。

这种协议可以确保在多处理器环境中,各个 CPU 的缓存数据能够正确、一致地反映主存储器中的数据状态,从而避免由于缓存不一致导致的数据错误或程序异常。

② 有序性实现原理

volatile 的有序性是通过插入内存屏障(Memory Barrier),在内存屏障前后禁止重排序优化,以此实现有序性的。

什么是内存屏障?

内存屏障(Memory Barrier 或 Memory Fence)是一种硬件级别的同步操作,它强制处理器按照特定顺序执行内存访问操作,确保内存操作的顺序性,阻止编译器和 CPU 对内存操作进行不必要的重排序。内存屏障可以确保跨越屏障的读写操作不会交叉进行,以此维持程序的内存一致性模型。

在 Java 内存模型(JMM)中,volatile 关键字用于修饰变量时,能够保证该变量的可见性和有序性。关于有序性,volatile 通过内存屏障的插入来实现:

  • 写内存屏障(Store Barrier / Write Barrier): 当线程写入 volatile 变量时,JMM 会在写操作前插入 StoreStore 屏障,确保在这次写操作之前的所有普通写操作都已完成。接着在写操作后插入 StoreLoad 屏障,强制所有后来的读写操作都在此次写操作完成之后执行,这就确保了其他线程能立即看到 volatile 变量的最新值。
  • 读内存屏障(Load Barrier / Read Barrier): 当线程读取 volatile 变量时,JMM 会在读操作前插入 LoadLoad 屏障,确保在此次读操作之前的所有读操作都已完成。而在读操作后插入 LoadStore 屏障,防止在此次读操作之后的写操作被重排序到读操作之前,这样就确保了对 volatile 变量的读取总是能看到之前对同一变量或其他相关变量的写入结果。

通过这种方式,volatile 关键字有效地实现了内存操作的顺序性,从而保证了多线程环境下对 volatile 变量的操作遵循 happens-before 原则,确保了并发编程的正确性。

JVM内存布局?

《Java虚拟机规范》中将 JVM 运行时数据区域划分为以下 5 部分:

  1. 程序计数器(Program Counter Register):用于存储当前线程执行的字节码指令的地址,在多线程环境中,程序计数器用于实现线程切换,保证线程恢复执行时能够继续从正确的位置执行代码。
  2. Java 虚拟机栈(Java Virtual Machine Stacks):用于存储方法调用和局部变量(方法内部定义的变量),在方法调用和返回时,虚拟机栈用于保存方法的调用帧,包括方法的局部变量、操作数栈、方法返回地址等。
  3. 本地方法栈(Native Method Stack):与虚拟机栈类似,本地方法栈用于执行本地(Native)方法。
  4. Java 堆(Java Heap):JVM 中最大的一块内存区域,用于存储对象实例。所有的对象都在堆中分配内存。
  5. 方法区(Methed Area):用于存储类的元数据信息,包括类的结构、字段、方法、静态变量、常量池等。

如下图所示:
image.png

栈和方法区会有OOM吗?

栈和方法区都会出现 OOM,它们的 OOM 发生场景如下:

  • 栈内存主要用于存储方法的栈帧,包括局部变量、操作数栈、方法返回地址等。当一个线程请求的栈深度超过 JVM 允许的最大深度时(默认情况下这个值是比较大的,但可以通过-Xss参数调整),会抛出 StackOverflowError 异常。此外,如果 JVM 尝试动态扩展栈空间大小但无法获得足够的内存,也可能抛出 OutOfMemoryError 异常。不过,这种情况相对较少见,因为栈空间一般在启动时就已经固定或者有比较确定的上限。
  • 当方法区(或元空间)中存放的类、常量、静态变量等信息超过了 JVM 分配给它的内存大小时(可通过 -XX:MaxMetaspaceSize 等参数进行设置),就会抛出 OutOfMemoryError。特别是当应用动态加载大量的类或类加载器无法被垃圾回收时,容易出现方法区的内存溢出。

父线程创建多个子线程可能会导致哪块内存溢出?

可能会导致内存溢出的区域有以下几个:

  1. Java 栈内存溢出:每个线程都有自己的栈,用于存储方法调用时的方法信息、局部变量等数据。如果线程的栈设置过大,或者线程递归深度过深,可能会导致栈内存溢出(StackOverflowError)。若栈大小动态扩展受限于系统可用内存,则可能抛出 OutOfMemoryError。
  2. 堆内存溢出:当创建大量线程时,每个线程可能会创建和管理多个对象,这些对象都存储在堆中,当对象超过 JVM 配置的最大堆内存时(通过 -Xmx 参数设置),可能会导致 java.lang.OutOfMemoryError: Java heap space 错误。
  3. 方法区溢出: 在多线程应用中,当线程中的代码涉及到动态类加载(例如使用线程上下文类加载器加载不同的类)时,可能会导致方法区(或其替代品 Metaspace)内存的快速增长。方法区存储了类的元数据,如果这部分内存达到 JVM 配置的最大值(通过 -XX:MaxMetaspaceSize 设置),也会引发 java.lang.OutOfMemoryError,错误信息可能涉及“Metaspace”。

CMS和G1收集器的区别?

CMS 和 G1 区别主要有以下几个:

  1. 目标和定位不同:CMS 目标和定位是最短停顿时间;G1 除了追求低停顿外,还允许用户设定期望的最大停顿时间(Pause Time Target),更加灵活地平衡吞吐量和响应时间,适合大规模数据的应用。
  2. 作用区域不同:CMS 是老年代垃圾回收器;G1 是全代(新生代+老年代)垃圾回收器。
  3. 使用算法不同:CMS 使用的是“标记-清除”算法,所以会产生内存碎片;而 G1 使用的是“复制”算法所以不会有内存碎片。
  4. 适用场景不同:小内存和 JDK 8 之前通常会使用 CMS 垃圾回收器;而大内存管理和 JDK 9+ 以上环境通常会使用 G1 垃圾回收器。

说说创建一个对象的过程?

创建一个对象的过程如下:

  1. 类加载检查:当通过 new 关键字创建一个对象时,JVM 首先会检查该对象的类是否已经被加载并初始化了。如果尚未加载,JVM 将先进行类的加载过程,然后调用该类的构造器以完成初始化。
  2. 分配内存空间:类加载完成后,JVM 会在 Java 堆中为新创建的对象分配足够的内存空间。对象所需的内存大小在类加载过程中就已经确定。内存的分配方式取决于 Java 堆内存是否规整,可以选择“指针碰撞”或“空闲列表”两种不同的分配方式。
  3. 初始化零值:内存分配之后,JVM 会对对象的普通成员变量进行初始化为零值,比如 int 类型初始化为 0,Integer 类型初始化为 null。这一步骤确保了对象的成员字段在不经过显式初始化的情况下也能被直接使用。
  4. 设置对象头:然后 JVM 需要对对象的对象头进行设置,这包括对象的元数据信息、GC 分代年龄、 hashCode 以及锁标记等。对象头的设置对于对象的后续使用至关重要。
  5. 执行构造方法:最后,JVM 将执行对象的构造方法。这一步是开发者可以控制的,可以在构造方法中添加自定义的初始化代码,以实现特定的业务逻辑。构造方法执行完成后,一个完全初始化且可直接使用的对象才算创建成功。

注意:对象创建过程和类加载机制(加载、验证、准备、解析、初始化)略有不同。

本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、消息队列等模块。

标签:volatile,对象,线程,内存,JVM,上周,CPU,百度
From: https://www.cnblogs.com/vipstone/p/18252550

相关文章

  • 证照之星XE个人破解版2024最新证照之星百度网盘免费下载
    随着社会的进步,证件照的用处越加宽广,例如:、结婚证件照、驾驶证照片、毕业证照片等等;当工作人员每天面对如此庞大的照片数量时,都尝试着去网上下载证照之星破解版,企图能够快速且专业的处理掉这些照片,这边是不建议这样做的。对于一些不会用PS,又想自己做证件照的时候,证照之星是一......
  • 百度翻译逆向Acs-Token逆向百度翻译爬虫(调试篇)
    文章声明本文章中所有内容仅供学习交流,严禁用于商业用途和非法用途,否则由此产生的一切后果均与文章作者无关,若有侵权,请联系我立即删除!概要-该篇文章是基于translate接口的分析-用到的工具Edge浏览器逆向目标求参数Acs-Token上一篇百度翻译是技巧篇,这篇来带着......
  • 百度202425
    #include<bits/stdc++.h>usingnamespacestd;#definelsu<<1#definersu<<1|1constintN=3e5+7;inta[N];structnode{intl,r,tag,odd[2],even[2];}tr[N<<3];intok;voidpushup(intu){tr[u].odd[0]=tr[ls].odd[0]+......
  • 百度云CDN怎么样?对新站有什么好处?
    百度云CDN是百度智能云提供的内容分发网络服务•优势:•收录快:小编在2024/6月份一共注册3个域名全部已备案,通过百度云CDN内容分发,大部分3天内收录仅限于WWW与根域名。•安全稳定:创新智能的安全防护思路,拒绝一刀切式的安全防护,引入深度学习引擎技术,守护财富安全。•......
  • python爬虫获取百度热搜
    注:本篇学习需要python基础前言:在上篇中,我们学习了怎么用python发送网页请求来获取网站的源代码,在这篇中,我们将进一步学习本篇目标:利用python爬虫获取百度热搜第一步,用浏览器打开百度热搜网站百度热搜网址https://top.baidu.com/board?tab=realtime页面如下:第二步,按下F12键......
  • 新网站如何把链接提交给百度搜索引擎
    新网站如何把链接提交给百度搜索引擎 很多新手或者刚接触的人,搭建好了网站,并没有百度蜘蛛或者其他的搜索引擎蜘蛛来抓取爬行,所以对于这样的新站,我们首先的是要提交给百度,不能坐等百度来找我们。工具/原料爱站工具网站链接数据百度站长工具方法......
  • 利用某些平台(聚合API、百度AI、科大讯飞API)的API接口,利用HTTP协议向服务器发送请求,并
    要使用C语言通过HTTP协议向服务器发送请求并接收响应,你可以使用如libcurl这样的库来发送HTTP请求。libcurl是一个免费且易于使用的客户端URL传输库,支持多种协议,包括HTTP。同时,为了解析服务器响应中的JSON数据,你可以使用cJSON库,这是一个轻量级的JSON解析库。以下是一个简单的示例......
  • easyrecovery专业版破解无需注册绿色版免费下载 easyrecovery16数据恢复软件永久激活
    EasyRecovery(易恢复中国)是由全球著名数据厂商Ontrack®出品的一款数据文件恢复软件。支持恢复不同存储介质数据:硬盘、光盘、U盘/移动硬盘、数码相机、Raid文件恢复等,能恢复包括文档、表格、图片、音视频等各种文件。开发背景:全球领先的数据恢复解决方案提供商Ontrack近......
  • 我才不要和你做朋友呢百度云/迅雷BT下载[BD/HD-MKV1.77G/8.52G][高清版]
    电影《我才不要和你做朋友呢》是一部极具深度和情感的作品,通过展现人际关系的复杂性和友谊的真实性,为观众带来了深刻的思考。影片以两位主角小明和小红的故事展开。小明是一个内向而害羞的男孩,小红则是一个外向而热情的女孩。他们在同一个学校里上学,但却几乎没有交集......
  • 百度面试:如何用Redis实现限流?
    高并发系统有三大特征:限流、缓存和熔断,所以限流已经成为当下系统开发中必备的功能了。那么,什么是限流?如何实现限流?使用Redis能不能实现限流?接下来我们一起来看。1.什么是限流?限流是指在各种应用场景中,通过技术和策略手段对数据流量、请求频率或资源消耗进行有计划的限制,以避......