美团Java日常实习一面

也是半年前面的,最近复习贴上来

1.自我介绍

2.说说垃圾回收。

3.可达性分析法怎么实现的,根节点有哪些,让你自己实现你怎么做

4.垃圾回收期间发生的动态变化怎么处理,比如一个对象之前是垃圾,现在不是了,会不会回收。

5.哪种gc会发生stop the world

6.说说aqs原理(简历里写的,不写应该不会直接问)。

7.java有哪些引用类型

8.由4引出了ThreadLocalMap的提问,为什么Key是弱引用。

9.ACID是什么,在Innodb怎么实现的。

10.redolog满了怎么办。——文件组循环清除——追问:什么时候清除清除的是哪些数据?

11.是事务执行过程中边写redolog边写数据,还是commit的时候写redolog,先写数据还是先写redolog?

12.说说索引。

13.实习和项目问答

14.算法题:前序中序生成二叉树。

15.研究方向

16.学习或者研究过程中遇到的困难,怎么解决的

17.反问

下面是我自己整理的对上面问题的回答,可能我理解有误,欢迎指出。

2.垃圾回收首先是判断垃圾对象:引用计数法和可达性分析法。

引用计数法:记录对象的被引用数,没引用了就是垃圾。存在自己引用自己或者几个垃圾对象相互引用的情况

可达性分析法:从GCROOT出发,如果能找到对象,即为有用,到不了即为垃圾。

垃圾回收算法:标记清除,标记复制,标记整理。

GCROOT:虚拟机栈、本地方法栈引用对象,方法区中静态属性引用的常量或对象,持有锁的对象。。

可以用三色标记法实现

三种颜色:白色,灰色,黑色。白色表示还未被访问过,灰色表示访问过,但子对象没访问完,黑色表示访问完毕。

过程:所有对象初始是白色,与GCROOT直接可达的改为灰色,加入队列。然后一种从队列里取,标记为黑色,并将其直接可达的白色对象改为灰色,加入队列,如此循环,直到没有灰色对象。

4.stop the world期间自然是不会有这种问题,但垃圾回收器并不会在所有阶段都stw。

以CMS为例,分为四个阶段,可以参照三色标记的过程来理解

1.初始标记:从GCROOT找到能到达的对象,有STW

2.并发标记:无STW,从初始标记的对象出发,遍历整个对象图,完成标记

3.重新标记:STW,修改并发标记期间的产生的变化

4.并发清除

为了减少垃圾回收造成的stw时间,遍历图的过程自然要并发的进行,这时候会产生问题,而重新标记阶段就是为了解决这些问题。虚拟机会记录下并发过程中产生的修改,比如线程清除了一个引用,GC会通过写屏障保存旧引用,确保老对象不会因为断开被错删。这块涉及记忆集,SATB等一些概念,我也囫囵吞枣不管了,一般也不会一直追问到这

5.所有的GC都会,MinorGC,MajorGC,FullGC。

6.AbstractQueuedSynchronizer是抽象队列同步器,是java并发包中提供的构建锁和同步器的基本框架。

首先是它有一个volatile修饰的int型变量state,在ReentrantLock里它表示锁被获取的次数,在Semaphore里它表示剩余的许可数量。一般我们通过cas操作来修改这个变量,cas成功表示获取锁成功,否则失败。

同时,aqs内部维护了一个同步队列,用来存储那些等待获取锁的线程。当线程获取锁失败时,就会把线程引用包装成一个结点放到队列里去。当持有锁的线程释放资源时,会调用release方法,在释放锁之后会把队列里的第一个结点唤醒,被唤醒的线程会去尝试获取锁。

aqs是基于模板方法设计的,它定义了一系列模板方法,如acquire,acquireShared,release,releaseShared等等,我们可以写一个aqs的子类,然后自己实现tryAcquired,tryReleased等几个抽象方法。然后就能实现自定义的同步器。

7.强引用:显示new一个对象就是强引用,一般我们用的都是强引用

软引用:被扫描到并且内存不足就会回收。用于实现内存敏感的缓存:如图片缓存,数据库连接缓存

弱引用:被扫描到就直接回收。可用于避免内存泄漏,案例是ThreadLocal

虚引用:用于监控对象回收过程

8.LocalMap用的是弱引用。

在Thread类里有一个ThreadLocalMap的变量,它是线程私有的空间。它的key为ThreadLocal的弱引用,value为我们自己想存的值。设计成弱引用主要是为了解决内存泄露的问题,尤其是在线程复用的情况下。因为如果是强引用,那么ThreadLocalMap就会一直持有ThreadLocal,导致无法回收。用了弱引用,回收时会将key回收掉,但value不会,但是ThreadLocalMap的get,set,remove方法都会检查key为Null的数据并清除。

9.原子性:uodolog保证

隔离性:MVCC保证

持久性:redolog保证

一致性:以上三个性质都是为了一致性

MVCC是通过ReadView实现的,ReadView是一个逻辑快照,表示某一时刻可见的事务集合。包含了四个重要字段:

m_ids创建时的活跃事务列表;

min_trx_ix_id活跃事务列表里的最小事务id;

max_trx_id创建时应该给下一个事务的id值;

creator_trx_id创建该ReadView的事务的事务id;

然后就能判断某条记录该事务是否可见了。

如果记录的事务id比ReadView中的min_trx_ix_id小,可见;

大于等于max_trx_id则不可见;

在min和max之间且在m_ids里,不可见;

在min和max之间且不在m_ids里,可见;

RW的创建时机:读已提交是每次读都会创建,可重复读是第一次快照读时创建。

10.redolog一般有两个,并且组成1个重做日志文件组。两个文件已循环写的方式来工作。要写满时会阻塞mysql,停下来将buffer pool中的脏页刷新到磁盘。因为redolog的目的是防止数据丢失,写到磁盘就不怕丢了,这些数据在redolog里就可以擦除,就有空间了。

11.这个问题其实还挺难的,面试的时候没想清楚。

事务执行过程中边修改内存数据页(Buffer pool),边写redolog(在redolog buffer);事务提交的时候才进行对redolog持久化。,然后任务事务提交成功。这是默认策略,可以通过参数控制刷盘时机,可以控制提交事务不刷盘,刷盘,以及写到redolog文件(内核空间,还没有没有真正写到磁盘,这时除非操作系统出错或者断电会丢失数据,不会因为Mysql的问题丢失数据了)。

回顾下事务开始到提交过程过三个日志是怎么样的。

BEGIN:开启事务,会分配唯一事务id

执行DML:update ... 修改buffer pool里数据页;生成undolog;写redolog

此时undolog和redolog还在内存里,未持久化;binlog还没写

COMMIT:进入两阶段提交

prepare阶段:redolog打上prepare标记,开始刷盘;

如果此时崩溃,通过prepare标记就知道,丢弃或回滚;

commit阶段:写binlog;binlog刷盘;将redolog状态改为commit;客户端返回‘Commit OK’

为啥先写redolog而不是binlog,因为redolog可以用来恢复,如果写好了redolog没写binlog还能再补上;但如果binlog落盘了,redolog还没有,此时又崩溃了,不但数据丢失,写入磁盘的binlog可能又同步给了从服务器,搞得主从不一致。

且注意,此时只剩Undolog还没落盘。其实已经无所谓了,undolog也是受redolog保护的,只要redolog写到了磁盘,一切都不是问题。undolog也是放在buffer pool中,有个undolog页,什么时候落盘不要紧,由后台线程去弄就行了。

先写redolog保证持久化,而不将数据持久化,这也是常说的WAL(Write-Ahead Logging)。

如果对您有帮助的话欢迎一键三连

全部评论
都是一些有点深入的八股了,感觉讲的很清楚,适合拿来复习
4 回复 分享
发布于 2025-05-16 11:41 北京
mark
1 回复 分享
发布于 2025-05-29 03:51 江苏
mark
点赞 回复 分享
发布于 2025-08-21 17:44 湖南
mark收藏
点赞 回复 分享
发布于 2025-07-01 18:46 辽宁
mark收藏
点赞 回复 分享
发布于 2025-06-12 12:24 四川
mark
点赞 回复 分享
发布于 2025-06-11 16:43 辽宁
mark收藏
点赞 回复 分享
发布于 2025-06-06 18:23 上海
mark
点赞 回复 分享
发布于 2025-06-06 13:08 辽宁
mark收藏
点赞 回复 分享
发布于 2025-05-31 18:03 上海
mark收藏
点赞 回复 分享
发布于 2025-05-31 00:57 四川
mark收藏
点赞 回复 分享
发布于 2025-05-28 18:41 湖南
mark
点赞 回复 分享
发布于 2025-05-27 23:15 广东
mark
点赞 回复 分享
发布于 2025-05-26 23:44 天津
佬,约二面了吗
点赞 回复 分享
发布于 2025-05-25 20:16 四川
mark收藏
点赞 回复 分享
发布于 2025-05-23 23:00 天津
m
点赞 回复 分享
发布于 2025-05-23 22:29 浙江
mark收藏
点赞 回复 分享
发布于 2025-05-22 18:25 北京
mark收藏
点赞 回复 分享
发布于 2025-05-22 15:08 上海
mark收藏
点赞 回复 分享
发布于 2025-05-22 01:06 安徽
mark
点赞 回复 分享
发布于 2025-05-22 00:33 湖南

相关推荐

2025-12-08 12:43
已编辑
门头沟学院 Java
那些杀不死我的 反复追着我杀!!1.jvm包含哪几部分classloader:将编译(.java->.class)好的java字节码文件(.class文件) 加载到内存中(也就是运行时数据区Runtime data area 存放运行时数据(线程私有:栈 本地方法栈 pc ,进程级(也就是所有线程共享):堆 本地方法区)Execution engine 执行引擎,class文件被加载后 将指令和数据放在内存中 执行引擎负责将命令解释给os 也就是将JVM指令翻译为os指令集Native interface:本地库接口2.jvm内存结构/运行时数据区线程私有的:栈(每个方法执行时创建栈帧 存局部变量表等) 本地方法栈(为Native方法提供服务) 程序计数器(存当前线程执行的字节码指令地址)线程共享:堆(最大的内存区 存对象实例、类对象) 方法区 (存类有关的信息 静态变量等)3.说一下永久代和元空间都是对 方法区 的实现永久代:JDK7 及之前的实现,属于堆内存的一部分,有固定大小限制(通过-XX:PermSize/-XX:MaxPermSize设置),存储类元信息、常量池等,易触发永久代 OOM元空间:JDK8 及之后的实现,使用本地内存(不在堆中),大小受物理内存限制(默认无上限),仅需通过-XX:MetaspaceSize设置初始触发 GC 的阈值4.为什么jdk8要将永久代替换为元空间解决永久代OOM问题 简化类元数据管理 兼容JVM规范5.类加载过程加载:通过类全限定名获取字节码流,将字节码转化为方法区的类元数据,同时在堆中生成Class对象验证:确保字节码符合 JVM 规范(如文件格式验证、语义验证),防止恶意字节码破坏 JVM 安全准备:为类的静态变量分配内存并赋默认值(如 int 赋 0、对象赋 null),不执行赋值语句(赋值在初始化阶段)解析:将常量池中的符号引用替换为直接引用(如类、方法的内存地址),该阶段可延迟到初始化后执行。初始化:执行<clinit>方法(静态变量赋值 + 静态代码块),触发时机是 “主动使用”(new 对象、访问静态成员、反射、初始化子类、主类),且每个类仅初始化一次6.类加载机制(双亲委派制)1类加载器收到加载请求  -> 2将请求委托给父加载器,一直向上委托,直到启动类加载器 -> 3启动加载器检查是否能加载这个类,能则使用当前加载器,否则向下传递 ->4重复步骤3 如果找不到 class not foundJVM 默认类加载器层级:启动类加载器(Bootstrap)→扩展类加载器(Ext)→应用程序类加载器(App)→自定义类加载器7.why双亲委派保护核心类库(如String由启动类加载器加载,避免自定义类篡改替换)防止类重复加载8.自定义类加载器需要注意什么重写findClass方法(not loadClass,避免破坏双亲委派)9.双亲委派模型有哪些破坏场景 为什么要破坏Tomcat的WebAppClassLoader:Tomcat 为每个 Web 应用创建独立类加载器,优先加载应用内的类(而非委托父类),目的是隔离不同 Web 应用的类(防止不同应用的同包同类冲突)JDBC 的 SPI 加载:JDBC 的核心接口java.sql.Driver由启动类加载器加载,但具体实现(如 MySQL 驱动)在第三方包,启动类加载器无法加载,因此 DriverManager 通过线程上下文类加载器(破坏双亲委派)加载实现类(可能会让详细说一下SPI机制一些热部署实现:自定义类加载器加载更新后的类10.类卸载条件类所有实例被完全回收 类加载器被完全回收 Class对象无任何引用(FullGC时发生11.介绍一下gc机制涉及对象可回收判定、垃圾收集算法、分代收集模型、垃圾收集器、GC 触发时机等维度(问这么大 吟唱启动12.常见gc算法标记-清除,标记-复制,标记-整理,分代收集13.介绍一下CMS和G1(1)CMS 收集器(老年代专用)核心流程:初始标记(STW,标记 GC Roots 直接关联对象)→并发标记(无 STW,遍历引用链)→重新标记(STW,修正并发标记的遗漏)→并发清除(无 STW,清理可回收对象)优点:并发收集,STW 时间短,适合低延迟场景缺点:产生内存碎片、CPU 消耗高(并发阶段占用 CPU)、依赖老年代空间预留(易触发 Full GC)(2)G1 收集器(全堆收集)核心流程:将堆划分为多个大小相等的 Region,包含新生代 Region 和老年代 Region;流程为初始标记→并发标记→最终标记→筛选回收(STW,优先回收垃圾多的 Region)优点:兼顾吞吐量和延迟、可预测 STW 时间(通过-XX:MaxGCPauseMillis设置)、无内存碎片(回收时整理 Region)缺点:内存占用高(维护 Remembered Set)、小内存场景下性能不如 CMS14.jvm如何判断对象是可回收的早期方案引用计数法(无法解决循环引用问题)可达性分析算法判定对象是否不可达,结合引用类型细分可回收时机,finalize 机制完成最终判定15.能否将对象分配在栈上jvm针对“逃逸”做了优化分析对象的作用域是否逃出方法 / 线程,若逃逸则可直接分配在栈上 无需gc16.jvm调优核心思路是什么监控 分析 参数调整 验证17.一般会调整哪些jvm参数堆大小:-Xms2g -Xmx2g(初始堆 = 最大堆,避免堆动态扩展的开销)新生代大小:-Xmn512m(新生代占堆的 1/4~1/3,过小会导致对象提前进入老年代)Survivor 比例:-XX:SurvivorRatio=8(Eden:S0:S1=8:1:1,保证新生代对象有足够空间存活)晋升老年代年龄:-XX:MaxTenuringThreshold -XX:InitialTenuringThresholdGC 收集器:-XX:+UseG1GC(默认推荐,兼顾吞吐量和延迟)、-XX:+UseZGC(低延迟场景)GC 日志:-XX:+PrintGCDetails -Xloggc:gc.log(输出 GC 日志用于分析)元空间:-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m(限制元空间大小,避免元空间 OOM)18.Jvm调优经验《没有经验!!》(回去等通知吧19.内存泄漏和OOM的区别?如何排查(1)内存泄漏:对象已无业务意义,但仍被引用(如ThreadLocal......),导致无法被 GC 回收,长期积累会引发 OOM① jmap -dump:format=b,file=heap.hprof导出堆快照;② MAT分析快照,定位 “无法回收的大对象” 和 “持有其引用的 GC Roots ③结合 Arthas 的jvm/heapdump命令定位泄漏代码(如静态 Map 未清理)(2)OOM:JVM 内存不足(如堆 / 元空间 / 栈超出上限),无法为新对象分配内存,直接抛出 OOM 异常① 看 OOM 类型(堆 OOM / 元空间 OOM / 栈 OOM)② 堆 OOM 分析对象分布(是否大对象过多) 元空间 OOM 检查类加载数量(是否频繁动态生成类) 栈 OOM 检查递归深度或线程数20.线上OOM问题如何排查21.如何分析gc日志22.线上频繁FullGC如何排查23.讲一下对jvm的理解(太大了 上文内容挑一些吟唱24.jmmJava Memory Model Java 内存模型,Java 虚拟机规范中定义的一种抽象内存模型,并非物理内存的实际布局,其核心目标是解决多线程环境下的原子性、可见性、有序性问题,为多线程编程提供内存访问的一致性规则欢迎佬赐教上辈子🔪👨放🔥 这辈子十月(底)秋招!!
查看23道真题和解析
点赞 评论 收藏
分享
评论
91
508
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务