百度日常实习凉经

手撕:n个数范围是[1,n]怎么找出重复数字,并且不改变原来的数组,O(1)的空间复杂度

类似环形链表,先定位重逢的点,然后从头开始定位重复元素,一开始没写出来面试官的提醒下最后还是oc了

场景题:应该是挂在这了

(1)怎么设计一个在线共同编辑的word结合场景等分析

(2)如何处理sql慢查询大量请求响应缓慢

核心目标​​:快速定位慢查询根源,针对性优化。

​步骤​​:

  1. ​监控与告警​​:使用工具(如 Prometheus + Grafana)监控数据库 QPS、慢查询比例、CPU/内存/磁盘 IO。设置慢查询阈值(如 MySQL 的 long_query_time=1s),触发告警。
  2. ​日志分析​​:收集慢查询日志(MySQL 的 slow_query_log),通过工具(如 pt-query-digest)分析高频慢 SQL。检查执行计划:EXPLAIN SELECT ...,关注全表扫描(type=ALL)、未用索引(key=NULL)。
  3. ​上下文关联​​:结合业务日志,确认慢查询是否由突发流量(如促销)、数据量增长或低效代码(如循环调用 SQL)引起。

1.ConcurrentHashMap 的扩容机制

1. 扩容触发条件

  • 元素数量超过阈值:当 ConcurrentHashMap 中的元素数量超过 容量 * 负载因子 时,触发扩容。默认负载因子为 0.75。
  • 并发插入时检测到需要扩容:在插入元素时,如果发现当前数组已满(即元素数量达到阈值),会触发扩容。

2. 扩容过程

ConcurrentHashMap 的扩容过程是渐进式的,允许多个线程并发参与扩容,具体步骤如下:

(1)初始化新数组

  • 当需要扩容时,ConcurrentHashMap 会创建一个新的数组,其容量是原数组的两倍。
  • 新数组的初始化是懒加载的,只有在真正需要迁移数据时才会进行。

(2)并发迁移数据

  • 迁移任务分片:将整个数组的桶分为多个任务分片(通常是 16 个),每个线程负责迁移一部分桶。
  • 迁移单个桶:对于每个桶,线程会将其链表或红黑树拆分为两部分:一部分留在原数组的位置。另一部分迁移到新数组的对应位置(通过重新哈希计算新的索引)。迁移过程中,原桶会被标记为 ForwardingNode,表示该桶正在迁移。
  • 协助扩容:在扩容过程中,如果有新的线程插入数据,发现某个桶正在迁移(即遇到 ForwardingNode),该线程会协助完成迁移任务,而不是等待。

(3)替换数组

  • 当所有桶都迁移完成后,ConcurrentHashMap 会将内部数组引用指向新数组。
  • 旧数组会被垃圾回收。

(4)其他:插入节点

  • 桶为空:如果目标桶为空,使用 CAS 操作尝试直接插入新节点。如果 CAS 成功,插入完成;否则,重试整个流程。
  • 桶不为空:如果目标桶已有节点,锁定该桶(使用 synchronized (f)),然后进行以下操作:​链表:遍历链表,查找是否存在相同的键:如果存在,更新值(除非 onlyIfAbsent 为 true)。如果不存在,插入新节点到链表末尾。如果链表长度超过阈值(默认 8),调用 treeifyBin 将链表转换为红黑树。​红黑树:如果桶中存储的是红黑树,调用红黑树的插入方法 putTreeVal。

3.HashMap扩容机制

  • 触发条件:元素数量 > 容量 × 负载因子(默认16 × 0.75=12)。
  • 扩容步骤:新容量 = 旧容量 × 2(如16→32)。重建哈希表,重新计算每个元素的位置((hash & (newCap-1)))。旧链表拆分为高位链和低位链(利用hash & oldCap是否为0)。

4.动态代理实现

代理模式​(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式常用于延迟加载、访问控制、日志记录、事务管理等场景。根据代理对象的创建时机和使用方式,代理模式主要分为静态代理动态代理

静态代理定义:静态代理是在编译期就已经确定代理类和被代理类的关系。代理类通常需要手动编写,并持有被代理对象的引用,通过代理类来间接访问被代理对象。

动态代理定义:在运行时动态生成代理类,无需在编译期手动编写代理类。Java 提供了两种动态代理机制:基于接口的 JDK 动态代理和基于类的 CGLIB 动态代理。

  • JDK动态代理:基于接口,通过Proxy.newProxyInstance()创建代理对象,实现InvocationHandler接口的invoke()方法添加逻辑。
  • CGLIB动态代理:通过继承目标类生成子类,拦截方法调用,无需接口。使用MethodInterceptor接口的intercept()方法增强逻辑。

5.AOP的原理怎么实现

6.32位系统和64位的系统实现JVM虚拟机和java程序有什么区别

1. ​内存寻址能力

  • ​32位系统:​地址空间:每个进程的虚拟地址空间为4GB(2³²字节)。其中,一部分地址空间被操作系统保留,通常Java应用程序可用的堆内存最大约为2GB到3GB(具体取决于操作系统配置)。​限制:由于地址空间的限制,32位JVM无法分配超过约3GB的堆内存,这在处理大型应用或大数据集时可能成为瓶颈。
  • ​64位系统:​地址空间:每个进程的虚拟地址空间理论上可达16EB(2⁶⁴字节),但实际受限于操作系统和硬件,通常可用的虚拟地址空间为128TB或更多。​优势:64位JVM可以分配更大的堆内存(理论上无上限,但实际受限于物理内存和操作系统限制),适合需要大量内存的应用场景,如大数据处理、高性能计算等。

2. ​JVM实现与配置

  • ​32位JVM:​堆内存限制:默认堆内存较小,通常需要手动配置以充分利用可用内存,但受限于32位系统的地址空间。​兼容性:适用于大多数现有的Java应用程序,尤其是那些对内存需求不高的应用。
  • ​64位JVM:​堆内存配置:可以配置更大的堆内存(通过-Xmx和-Xms参数),适合需要大量内存的应用。​优化选项:支持更多的JVM优化选项和特性,如G1垃圾收集器、ZGC等,适应不同的性能需求。​兼容性:大多数Java应用程序可以在64位JVM上无缝运行,但需要确保所有依赖库和组件也兼容64位环境
java -Xms256m -Xmx1024m -Xss512k MyJavaApp

这个命令将为MyJavaApp分配初始堆内存256MB,最大堆内存1024MB,每个线程的栈内存大小为512KB。

7.k8s和docker的理解

8.声明式事务失效的原理

为什么声明式事务必须使用public,还有对啥类型的误差会存在异常

  1. ​非公共方法上的注解:在Spring AOP中,只有public方法上的@Transactional注解才会被代理,protected、private方法上的注解不会生效。
  2. 异常处理不当:默认情况下,@Transactional注解只对未捕获的RuntimeException和Error进行回滚。如果抛出的异常被捕获并且没有重新抛出,或者抛出的异常不是RuntimeException或其子类,事务将不会回滚。可以通过设置rollbackFor属性来指定哪些异常需要回滚。

Java 中的异常类型

Java 使用 Throwable 类作为所有错误和异常的超类。Throwable 有两个主要的子类:

  1. ​**Error(错误)​**
  2. ​**Exception(异常)​**

1. Error(错误)

Error 表示严重的问题,通常是虚拟机相关的问题,程序通常无法处理这些错误。常见的 Error 包括:

  • ​**OutOfMemoryError**:内存不足错误,表示 JVM 无法分配更多内存。
  • ​**StackOverflowError**:栈溢出错误,通常由于递归调用过深导致。
  • ​**NoClassDefFoundError**:类定义未找到错误,通常在编译后类文件丢失时发生。

特点

  • 通常由 JVM 或底层资源问题引起。
  • 程序一般无法恢复,建议记录日志并终止程序。

2. Exception(异常)

Exception 表示程序可以处理的异常情况。它又分为两类:

a. 受检异常(Checked Exception)

受检异常是在编译时强制要求处理的异常,开发者必须显式地捕获或声明抛出这些异常。常见的受检异常包括:

  • ​**IOException**:输入输出操作失败时抛出,如文件读写错误。
  • ​**SQLException**:数据库操作失败时抛出。
  • ​**ClassNotFoundException**:找不到指定的类时抛出。

特点

  • 编译器会检查是否处理这些异常(捕获或声明抛出)。
  • 通常表示可以预见的、可恢复的错误情况。

b. 非受检异常(Unchecked Exception)

非受检异常是在编译时不强制要求处理的异常,通常是程序逻辑错误导致的。非受检异常继承自 RuntimeException。常见的非受检异常包括:

  • ​**NullPointerException**:试图访问 null 对象的成员时抛出。
  • ​**ArrayIndexOutOfBoundsException**:数组索引越界时抛出。
  • ​**ArithmeticException**:算术运算错误时抛出,如除以零。
  • ​**IllegalArgumentException**:方法接收到不合法的参数时抛出。

特点

  • 编译器不会强制要求处理这些异常。
  • 通常表示程序逻辑错误,应通过修改代码避免。

9. 消费延迟一天怎么处理

其他:分布式锁怎么加库存,使用incr的操作加锁,保证目标最终和,降低竞争

10. G1和CMS

G1回收器

  • 内存划分:将堆划分为多个大小相等的Region(1~32MB),分为Eden、Survivor、Old、Humongous(大对象)区。
  • 工作流程:初始标记(STW):标记GC Roots直接关联的对象。并发标记:遍历堆标记存活对象。最终标记(STW):处理并发标记期间的引用变化。筛选回收(STW):选择回收价值高的Region进行清理。

​CMS:

  • ​初始标记(Initial Mark)​:短暂停顿,标记 GC Roots 直接引用的对象。
  • ​并发标记(Concurrent Mark)​:与应用线程并发执行,遍历整个对象图,标记所有可达对象。
  • ​预清理(Preclean)​:并发执行,处理在并发标记阶段发生变化的对象引用。​
  • 重新标记(Remark)​:短暂停顿,修正并发标记期间因应用线程运行而产生的标记变动。
  • ​并发清除(Concurrent Sweep)​:与应用线程并发执行,清除不可达对象,回收空间。​
  • 并发重置(Concurrent Reset)​:准备下一次垃圾回收。

​G1:​

  • 初始标记(Initial Mark)​:短暂停顿,标记 GC Roots 直接引用的对象,并记录下需要清理的 Region。
  • ​并发标记(Concurrent Mark)​:与应用线程并发执行,遍历整个对象图,标记所有可达对象。​
  • 最终标记(Final Mark)​:短暂停顿,处理引用变化,并完成标记工作。
  • ​清理(Cleanup)​:部分并发,部分停顿,统计每个 Region 的存活对象,准备进行空间回收。​
  • 内存整理(Compaction)​:在必要时,对 Region 进行内存整理,消除碎片。

设计目标

低延迟,适用于对响应时间要求高的应用

平衡吞吐量和延迟,适用于大堆内存和多核 CPU 的应用

堆内存划分

连续的堆内存空间

将堆内存划分为多个固定大小的区域(Region),每个区域可以是 Eden、Survivor 或 Old

垃圾回收阶段

初始标记、并发标记、预清理、重新标记、并发清除、并发重置

初始标记、并发标记、最终标记、清理(可并行或并发执行)

停顿时间控制

尽量减少停顿时间,但无法精确控制

提供可预测的停顿时间,通过设置

 

-XX:MaxGCPauseMillis

 

参数

碎片化处理

容易产生内存碎片,可能导致 Full GC

通过区域化管理,减少内存碎片,避免 Full GC 的发生

适用堆大小

适用于中小型堆(通常小于 4GB)

适用于大型堆(通常大于 4GB),可以高效管理大内存

全部评论
百度在线吗
点赞 回复 分享
发布于 04-11 18:07 陕西

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务