知识记录【自用】

juc

synchronized

synchronized 底层使用的JVM级别中的Monitor 来决定当前线程是否获得了锁,如果某一个线程获得了锁,在没有释放锁之前,其他线程是不能或得到锁的。synchronized 属于悲观锁。

每个 Java 对象都可以关联一个 Monitor 对象,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的Mark Word 中就被设置指向 Monitor 对象的指针。

monitor内部维护了三个变量

  • WaitSet:保存处于Waiting状态的线程
  • EntryList:保存处于Blocked状态的线程
  • Owner:持有锁的线程

Java并发编程三大特性【一句话说明 线程安全】

  • 原子性
  • 可见性
  • 有序性

GC

G1 回收过程

Mixed GC:

  1. 初始标记 - STW,标记 GC-Roots 直接关联的对象
  2. 并行标记,标记所有可达的对象
  3. 重新标记 - STW,标记前两步中变化的对象
  4. 估计 Region 价值
  5. 清除 - STW

对于 Young GC 来说,回收全部新生代 Region 即可,不需要对 Region 进行价值估计。

G1 在 CMS 的基础上做的优化

CMS 有哪些缺点:

  1. 基于连续内存布局,只能回收整个老年代,无法预测停顿时间
  2. 标记-清除算法,会留下内存碎片

G1 做的改进:

基于 Region,弱化分代,会跟踪 Region 回收价值从而优先选择垃圾最多、收益最大的 Region,从而预测回收时间。

局部标记 - 复制 + 宏观的标记 - 整理,达到减少内存碎片的效果。

mysql

UPDATE记录锁的工作机制

  1. 锁的类型:当UPDATE语句通过索引条件定位到需要更新的行时,InnoDB会为这些行加上排他锁(X锁)。排他锁会阻止其他事务修改这些行,也会阻止其他事务在这些行上加共享锁或排他锁。
  2. 锁的范围:如果UPDATE语句使用了唯一索引(包括主键)且条件精确匹配一行,则只锁定这一行。如果UPDATE语句使用了非唯一索引,则可能锁定多行,并且可能还会锁定间隙(使用间隙锁)以防止幻读。如果UPDATE语句没有使用索引,则可能会锁住整个表(在InnoDB中,如果没有使用索引,MySQL会进行全表扫描,并锁住所有扫描到的行,同时还会在间隙上加锁)。
  3. InnoDB的行锁是基于索引实现的,所以如果更新语句使用了索引(包括主键索引和非主键索引),那么它通常会锁定符合条件的索引记录

Bean生命周期:

1. 实例化 (创建对象) → 2. 依赖注入 (@Autowired) → 3. 初始化 → 4. 使用 → 5. 销毁

反射的实现原理

反射机制主要涉及以下类:

  • java.lang.Class:代表一个类或接口。
  • java.lang.reflect.Field:代表类的字段。
  • java.lang.reflect.Method:代表类的方法。

当使用Class.forName()或类加载器加载一个类时,JVM会执行以下步骤:

  1. 加载:通过类的全限定名获取类的二进制字节流,将类的静态存储结构转化为方法区的运行时数据结构,并生成一个代表该类的Class对象。
  2. 链接:将类的二进制数据合并到JVM运行状态中,包括验证、准备和解析(可选)。
  3. 初始化:执行类构造器<clinit>()方法,初始化静态变量和静态代码块。

反射API通过访问这个Class对象来获取类的元数据,并可以动态创建对象、调用方法、访问字段等。

反射的优缺点

优点

  1. 动态性:可以在运行时动态获取类的信息,动态创建对象和调用方法,使得程序更加灵活。
  2. 通用性:可以编写通用的代码,例如框架中经常使用反射来加载不同的类,实现解耦。

缺点

  1. 性能开销:反射操作比直接调用慢,因为需要解析类信息,以及方法调用时需要检查访问权限等。虽然现代JVM对反射进行了优化,但仍然有一定的性能损失。
  2. 安全性限制:反射可以突破访问权限的限制,例如可以调用私有方法和访问私有字段,这可能会破坏封装性,导致安全问题。
  3. 内部暴露:反射可以访问类的私有成员,这可能会破坏类的封装,并且可能随着版本更新,内部实现改变而导致反射代码失效

Spring事件驱动:发布、传输、监听都是怎么实现的?

Spring框架中的事件驱动模型基于观察者模式,主要包含以下几个角色:

  • 事件(ApplicationEvent):所有事件都必须继承自ApplicationEvent类,通常用于封装事件相关的数据。
  • 事件发布者(ApplicationEventPublisher):通过ApplicationEventPublisher的publishEvent方法来发布事件。
  • 事件监听者(ApplicationListener):实现ApplicationListener接口,或者使用@EventListener注解来标记事件处理方法。

实现过程:

定义事件:class OrderCreateEvent extends ApplicationEvent {发布事件的步骤:1.定义事件类:创建一个事件类,继承自 ApplicationEvent,该类封装了你要传递的事件数据

  • 发布事件:通过applicationContext.publishEvent(new OrderCreateEvent(tradeOrder));//发布事件。当事件发布后,Spring会将事件传递给所有匹配的监听器。
  • 传输:Spring的事件传输是在同一个JVM内进行的,默认是同步的。也就是说,事件发布后,会直接调用监听器的处理方法。注意,如果有一个监听器处理事件时抛出异常,可能会影响其他监听器的执行以及事件发布者的后续执行。
  • 监听:监听器可以通过实现ApplicationListener接口,并指定事件类型,或者使用@EventListener注解来监听特定的事件。当事件发布时,Spring会通过反射调用匹配的监听器。

此外,Spring还支持异步事件处理,可以通过在监听器方法上使用@Async注解,并配置Spring的异步任务执行器来实现

oom

os

线程切换和进程切换的上下文都存什么?

线程切换上下文:线程是CPU调度的基本单位。线程切换时,需要保存当前线程的上下文,以便之后能恢复执行。上下文主要包括:程序计数器(PC):当前线程执行到的指令地址。

寄存器状态:包括通用寄存器、栈指针等。

线程状态:如运行状态、阻塞状态等。

栈信息:每个线程有自己的栈,用于存储局部变量、方法调用等。

注意:线程切换发生在同一进程内,因此虚拟内存空间不变,所以不需要切换页表等内存管理相关的信息。

进程切换上下文:进程是资源分配的基本单位。进程切换时,除了需要保存线程切换所需的上下文外,还需要切换内存空间、文件描述符、信号处理等进程相关的资源。具体包括:

内存管理信息:页表、内存映射等(因为不同进程有独立的虚拟地址空间)。

文件描述符表:进程打开的文件和I/O状态。

信号处理设置:信号掩码、信号处理函数等。

进程控制块(PCB)中的其他信息:如进程ID、优先级、资源使用情况等。

进程切换比线程切换开销大,因为进程切换需要切换内存地址空间,导致TLB失效,从而可能增加内存访问的开销。

Redis

  1. 为了解决redis节点变化导致的⼤规模数据迁移问题,⼀致性哈希分区出现了:它将整个哈希值空间想象成⼀个环,节点

和数据都映射到这个环上。数据被分配到顺时针⽅向上遇到的第⼀个节点。因为Redis集群槽的个数刚好是 2 14 次⽅,和 HashMap 中数组的⻓度必须是 2 的幂次⽅有着异曲同⼯之妙。它能保证扩容后,⼤部分数据停留在扩容前的位置,只有少部分数据需要迁移到新的槽上。如果下线的是主节点,它的从节点之⼀将被选举为新的主节点,接管原主节点负责的哈希槽。

2. Redis字典,更底层是⽤数组+链表实现的哈希表。它的设计很巧妙,⽤了两个哈希表,平时⽤第⼀个,rehash 的时候⽤第⼆个,这样可以渐进式地进⾏扩容,不会阻塞太久。【当负载因⼦触发 rehash 条件时,Redis 会为哈希表1 分配新的空间,通常是哈希表 0 的两倍⼤⼩,然后将rehashidx 设置为 0。接下来的关键是,Redis 不会⼀次性把所有数据从哈希表0 迁移到哈希表1,⽽是每次操作字典时,顺便迁移哈希表0 rehashidx 位置上的所有键值对。迁移完⼀个槽位后,rehashidx 递增,直到整个哈希表0 迁移完毕。在 rehash 期间,查找操作会先查 哈希表 0,没找到再查哈希表 1;但是新插⼊的数据只会放到哈希表 1 中。这样既可以保证数据的完整性,⼜能避免数据的重复。这种设计的巧妙之处在于把 rehash 的开销分摊到了每次操作中。假设有⼀个⼏百万键的哈希表,如果⼀次性rehash 可能需要⼏百毫秒,这对单线程的 Redis 来说是灾难性的。但通过渐进式 rehash,每次操作只增加很少的额外开销,⽤户基本感觉不到延迟。】

5.压缩列表是 Redis 为了节省内存⽽设计的⼀种紧凑型数据结构,它会把所有数据连续存储在⼀块内存当中。 整个结构包含头部信息,如总的字节数、尾部偏移量、节点数量,以及连续的节点数据。当 listhash set 的数据量较⼩且值都不⼤时,底层会使⽤压缩列表来实现。

3.压缩列表 ziplist,这个设计很有意思。Redis 为了节省内存,设计了这种紧凑型的数据结构,把所有元素连续存储在⼀块内存⾥。但是它有个致命问题叫"连锁更新",就是当我们修改⼀个元素的时候,可能会导致后⾯所有的元素都要重新编码,性能会急剧下降。

你知道为什么Redis 7.0要⽤listpack来替代ziplist吗?listpack紧凑列表 它让每个节点只记录⾃⼰的⻓度信息,不再依赖前⼀个节点的⻓度。这样就从根本上避免了连锁更新的问题。

4. zset当保存的元素数量少于 128 个,且保存的所有元素⼤⼩都⼩于 64 字节时,Redis 会采⽤压缩列表的编码⽅式;否则就⽤跳表

分布式

针对你关心的Kafka消息路由、Rebalance机制以及Redis动态字符串扩容问题,我梳理了它们的核心原理,具体如下:

🚀 Kafka的消息路由与分区算法

当Producer发送消息时,确定消息分配到哪个Partition(分区)的规则(也称为分区器)主要有以下三种方式:

指定目标分区

在代码中直接指定消息的目标分区。

Key哈希取模

若消息设置了Key,则对Key计算哈希值,然后与分区总数取模,得出目标分区。这可保证同一Key的消息总进入同一分区。

轮询调度

若未指定分区也未设Key,默认采用轮询

方式依次将消息发送到各个分区,以实现均匀分布。

你可以通过实现 Partitioner 接口来自定义分区策略。

🔄 Kafka的Rebalance机制

Rebalance(再平衡)是指当消费者组内的消费者成员发生增减(如新consumer加入、旧consumer掉线)时,为了保持负载均衡,整个消费者组会重新分配其订阅Topic的所有分区的过程。

Rebalance的核心过程主要包含以下步骤:

  1. 消费者注册:新加入或发生变化的消费者会向Kafka集群注册,汇报自身情况。
  2. 分区重分配:Kafka集群根据新的消费者组成员列表,按照组内配置的分区分配策略,计算出新的分区分配方案。
  3. 分区转移与消费恢复:新的消费者开始消费分配到的分区,而不再拥有分区所有权的旧消费者则停止消费。

常见的分区分配策略包括:

  • RangeAssignor:按分区范围顺序分配给消费者。简单但可能导致分区数量多的Topic在消费者间分配不均。
  • RoundRobinAssignor:使用轮询方式分配所有分区,追求绝对均匀。
  • StickyAssignor:在均衡分配的前提下,尽可能保留上一次分配中已有的分区所有权,以减少因分区转移带来的资源开销。
  • CooperativeStickyAssignor:StickyAssignor的增强版,通过多次小规模重平衡来实现更平滑的调整。

💎 总结

  • Kafka消息路由:让你能通过Key或轮询等方式控制消息流向特定分区。
  • Kafka Rebalance:是Kafka保证消费者组高可用和伸缩性的核心机制,在消费者变动时自动重新分配分区。
全部评论
RPC协议通用架构和流程 序列化/反序列化模块、网络传输模块和服务注册中心;服务注册、发现 -> 序列化组包传输 -> 解包反序列化 -> 调用真实服务获得结果然后一样序列化返回 Redis的哈希表的扩容和rehash机制,以及在get和set指令模式下的具体表现 ht[0]和ht[1]两个哈希表轮流切换,惰性迁移,指数翻倍扩容;get: 先rehash,然后通过在ht[0]和ht[1]进行查找。set: 先rehash,然后新插入或修改的键值对只保存到ht[1]中
点赞 回复 分享
发布于 10-29 14:37 上海

相关推荐

📍面试公司:合肥大智慧🕐面试时间:下午三点到五点半💻面试岗位:java开发❓面试问题:首先我是进去报道,对接的人让我先写笔试,我就说等会学校里还有面试,然后笔试就免了接下来是一面:就是对着简历写的点一条条问,问到redismysql就给一个开放性的问题然后就是差不多半小时?对着两个项目一点点问,还有一些基础的八股,比如你写了a,市面上还有b,面试官会问为什么用a,有什么不同?然后我就说了几点,然后面试官会不断问,还有吗?但是你只要说完了就别硬耗时间就好了,直接说答完了。然后就是经典反问,问公司情况,业务情况等。二面的话来了两个面试官,就差不多是更深得的底层原理吧,比如你写jwt,就问你jwt和普通token有什么区别?其实我没准备的,就说了一下jwt的特点,然后又把rsa加密介绍一下。差不多就是横向对比加底层理解吧,问你mongodb和mysql的区别?为什么用?差不多就这些吧,半小时其实问不了太多。还有就是项目的流程,那些方面会有问题,怎么解决的,无非就是缓存一致性问题,mq丢失消息重复消费这些问题。然后还是反问,直接和一面一样。三面是hr面,本来都要走了,结果给我加了个hr面。然后就是我在boss上联系的hr,问题包括家庭情况,对象情况,为什么选合肥?有没有offer?有没有论文?还有实验室的情况,能不能提前实习,我就说如果寒假开学后可能比较忙。还有就是薪资问题,给了我好几个选项,月薪年薪最低月薪最低年薪啥的,我就填了一个月薪20k,面试官说有点高,我说boss上不是写的这么多吗,她说那是一个很大的区间14_22k,然后其实这是在问offer的时候,我说有一个14k的我给拒了。反问环节问了一下她大概能给多少,她说她绝对不聊,还有一些标准的反问环节吧。最后说一周或两周给消息。🙌面试感想:简历写了什么就问什么,好评。如果对于底层的细节理解到位的话,面试官应该挺欣赏这一点的。还有就是关于八股这方面多准备准备,以备不时之需。整体流程偏快,线下面试的话其实少了很多等待面试的焦虑。
查看9道真题和解析
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

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