字节后端凉了,实在太菜......
字节后端面经,严刑烤打3小时。
字节后端面经
1. 问实习经历,但是拷打的是自己的项目,正规的规则引擎了解过吗,没有。
2. 浏览器键入网址全过程
3. http为什么是无状态
4. 死锁条件
5. 子网掩码的作用
6. mysql b+树和b树
7. redis数据结构,用什么结构实现延迟消息队列
8. redis分片集群,如何分片的,有什么好处
9. jvm内存分布,有垃圾回收的是哪些地方
10. spring的aop是如何实现的
11. 算法: 数组最大子串和 经过提示才写对,太菜了
面完一个小时约二面。
【二面】
1. 判断字符串四种括号是否合法-有一点问题,经过提醒调试后解决了。
2. 写两道sql题,第一道写出来了,第二道还在想,没给时间了。
3. Linux命令熟吗,在log文件中查找所有IP,不会正则,没写出来。。。
4. 线程安全单例模式
5. 合并k个有序数组
中间加了道八股,redis过期删除和内存淘汰策略
全程60min,一直在做题,后面时间刚好到了就马上下线了,甚至没给反问环节
体验不是很好,当然自己确实太菜了,这辈子与字节无缘了。
八股文答案如下:
4. 死锁条件
死锁是指在并发系统中,两个或多个进程因为互相等待对方释放资源而无法继续执行的状态。
死锁发生的条件通常包括以下四个条件:
- 互斥条件(Mutual Exclusion):至少有一个资源被标记为只能被一个进程占用,即一次只能有一个进程使用该资源。
- 请求与保持条件(Hold and Wait):一个进程在持有至少一个资源的同时,又请求其他进程占用的资源。
- 不可剥夺条件(No Preemption):已经分配给一个进程的资源不能被强制性地剥夺,只能由持有该资源的进程主动释放
- 循环等待条件(Circular Wait):存在一个进程资源的循环链,每个进程都在等待下一个进程所占用的资源。
当这四个条件同时满足时,就可能发生死锁。为了避免死锁的发生,可以采取一些策略,如资源预分配、避免循环等待、引入资源剥夺等。
5. 子网掩码的作用
子网掩码是用于划分网络地址和主机地址的一个32位二进制数。
它与IP地址结合使用,用于确定一个IP地址中哪些位表示网络地址,哪些位表示主机地址。
子网掩码的作用主要有两个方面:
- 确定网络地址:子网掩码通过将IP地址中的网络部分与主机部分进行分隔,将网络地址和主机地址进行划分。子网掩码中的1表示网络部分,0表示主机部分。通过与IP地址进行逻辑与运算,可以得到网络地址。
- 确定主机地址范围:子网掩码中的0表示主机部分,确定了主机地址的范围。主机地址范围是指在同一个网络中可以分配给主机的不同IP地址。子网掩码中主机部分的位数决定了主机地址的数量,可以根据主机地址范围来分配IP地址给不同的主机。
子网掩码的正确配置对于网络通信的正常运行非常重要。它可以帮助路由器和交换机等网络设备正确地识别网络地址和主机地址,实现数据的正确传输和路由。
6. mysql b+树和b树
B+树和B树都是常用的索引结构,用于在数据库中进行高效的数据检索。
它们的主要区别在于叶子节点的结构和使用方式。
B树(B-Tree)是一种平衡多路搜索树,它的特点是每个节点可以存储多个关键字和对应的数据指针。
B树的节点分为内部节点和叶子节点,内部节点存储关键字和指向子节点的指针,叶子节点存储关键字和对应的数据指针。
B树的特点是所有叶子节点都在同一层级上,通过内部节点的指针可以快速定位到叶子节点。
B树适用于磁盘存储等场景,可以减少磁盘I/O次数,提高检索效率。
B+树(B+ Tree)是在B树的基础上进行了改进,它的特点是所有关键字都存储在叶子节点上,而内部节点只存储关键字和指向子节点的指针。
叶子节点之间通过指针连接形成一个有序链表,可以支持范围查询和顺序遍历。
B+树的内部节点相对较小,可以存储更多的关键字,减少了树的高度,提高了检索效率。B+树常用于数据库索引的实现,可以支持高效的范围查询和排序操作。
总结来说,B树适用于磁盘存储等场景,可以减少磁盘I/O次数;而B+树适用于数据库索引等场景,可以支持范围查询和顺序遍历。两者都是常用的索引结构,根据具体的应用场景选择适合的索引结构可以提高数据库的性能。
7. redis数据结构,用什么结构实现延迟消息队列
对于实现延迟消息队列,可以使用有序集合(Sorted Set)结构来存储消息。将消息的执行时间作为分数,消息内容作为成员,按照分数进行排序。通过定时任务或者轮询方式,检查有序集合中的消息,当消息的执行时间到达时,取出消息进行处理。
具体实现时,可以使用Redis的ZADD命令将消息添加到有序集合中,使用ZRANGEBYSCORE命令按照分数范围获取需要执行的消息,使用ZREM命令从有序集合中删除已经执行的消息。
添加消息:使用有序集合的ZADD命令,将消息作为成员添加到有序集合中,同时指定一个分数(score)作为消息的优先级或执行时间。分数可以是一个时间戳或其他有序的值,用于排序消息。
取出消息:使用有序集合的ZRANGE命令,按照分数范围获取需要执行的消息。可以设置获取的消息数量,也可以设置获取的分数范围。获取到的消息是按照分数从小到大排序的。
执行消息:获取到消息后,进行相应的处理操作。可以是执行具体的业务逻辑,发送消息给其他系统,或者进行其他操作。
删除消息:使用有序集合的ZREM命令,从有序集合中删除已经执行的消息。删除消息可以避免重复处理。
定时任务或轮询:为了实现消息的自动执行,可以使用定时任务或者轮询方式,定期检查有序集合中的消息。根据消息的分数判断是否到达执行时间,如果是则取出消息进行处理。
有序集合实现消息队列的优势在于:
- 消息有序:有序集合会根据分数对消息进行排序,可以按照优先级或执行时间顺序处理消息。
- 支持延迟消息:通过设置不同的分数,可以实现延迟消息的处理,即在指定的时间后才会被取出执行。
- 支持优先级:可以根据分数设置消息的优先级,高优先级的消息会被优先处理。
- 支持批量获取:可以一次性获取多个消息,提高处理效率。
- 支持范围查询:可以按照分数范围获取消息,实现范围查询的功能。
需要注意的是,使用有序集合实现消息队列时,需要根据实际需求合理设置消息的分数和处理逻辑,以及定时任务或轮询的频率,以达到预期的消息处理效果。
使用Redis作为延迟消息队列的好处是,它具有高性能、持久化、可靠性等特点,并且提供了丰富的命令和数据结构,方便进行消息的添加、获取和删除操作。
8. redis分片集群,如何分片的,有什么好处
Redis分片集群是通过将数据分散存储在多个Redis节点上来实现数据的分片和负载均衡。
具体来说,Redis分片集群将数据按照一定的规则(如哈希算法)分配到不同的节点上,每个节点负责存储和处理一部分数据。
分片集群的好处如下:
- 扩展性:通过增加节点,可以线性扩展集群的存储和处理能力,提高系统的吞吐量和并发性能。
- 负载均衡:将数据分散存储在多个节点上,可以均衡每个节点的负载,避免单个节点成为瓶颈,提高系统的稳定性和可用性。
- 容错性:当某个节点发生故障或宕机时,其他节点仍然可以继续提供服务,保证系统的可靠性和持久性。
- 高可用性:通过复制机制,每个分片可以有多个副本,当主节点发生故障时,可以自动切换到备用节点,实现高可用性。
需要注意的是,Redis分片集群也存在一些挑战和限制,如数据一致性、跨节点事务等问题,需要根据具体的业务需求和场景来选择合适的分片策略和解决方案。
9. jvm内存分布,有垃圾回收的是哪些地方
VM(Java虚拟机)内存分布主要包括以下几个区域:
- 方法区(Method Area):用于存储类的结构信息,如类的字段、方法、常量池等。在JDK 8及之前,方法区被称为"永久代"(Permanent Generation),而在JDK 8及之后,被移除了永久代,改为使用元空间(Metaspace)来存储。
- 堆(Heap):用于存储对象实例。堆是Java程序中最大的一块内存区域,被所有线程共享。堆被划分为新生代(Young Generation)和老年代(Old Generation)两部分。新生代又分为Eden空间和两个Survivor空间。
- 虚拟机栈(VM Stack):每个线程在运行时都会创建一个栈,用于存储局部变量、方法参数、返回值等。栈中的每个栈帧对应一个方法的调用,包括方法的局部变量表、操作数栈、动态链接、方法返回地址等。
- 本地方法栈(Native Method Stack):与虚拟机栈类似,但是用于执行本地方法(Native Method)。
- 程序计数器(Program Counter):用于记录当前线程执行的字节码指令的地址。
垃圾回收主要发生在堆和方法区(或元空间)中。
在堆中,垃圾回收器会自动回收不再被引用的对象,释放内存空间。在方法区(或元空间)中,垃圾回收主要针对无用的类和常量进行回收。
需要注意的是,不同的垃圾回收器有不同的工作方式和策略,如串行回收器、并行回收器、并发回收器等。它们会根据具体的配置和场景来决定何时进行垃圾回收以及如何回收。
10. spring的aop是如何实现的
Spring的AOP(面向切面编程)是通过动态代理实现的。
在Spring中,AOP通过在运行时动态地将切面织入到目标对象的方法中,从而实现横切关注点的模块化。
Spring AOP的实现方式有两种:基于代理的经典AOP和基于字节码的AspectJ AOP。
- 代理的经典AOP:JDK动态代理:基于接口的代理,通过java.lang.reflect.Proxy类和InvocationHandler接口实现。Spring使用JDK动态代理来代理实现了接口的目标对象。CGLIB动态代理:基于继承的代理,通过CGLIB库生成目标对象的子类来实现代理。Spring使用CGLIB动态代理来代理没有实现接口的目标对象。
- 基于字节码的AspectJ AOP:AspectJ是一个独立的AOP框架,它提供了更强大和灵活的AOP功能。Spring可以集成AspectJ,使用AspectJ注解或XML配置来定义切面和切点,然后通过编译时或运行时的织入方式将切面织入到目标对象中。
在Spring中,通过配置文件或注解来定义切面和切点,然后使用AOP代理将切面织入到目标对象的方法中。
当目标对象的方法被调用时,AOP代理会在方法执行前、执行后或抛出异常时执行切面的相关逻辑,实现横切关注点的功能,如日志记录、事务管理等。
需要注意的是,Spring AOP是基于代理的AOP,只能代理方法调用,而不能拦截对象的内部方法调用。如果需要更细粒度的控制和更强大的AOP功能,可以考虑使用AspectJ。
3. Linux命令熟吗,在log文件中查找所有IP,不会正则,没写出来。
要在日志文件中查找所有IP,可以使用以下命令组合:
grep -oE "([0-9]{1,3}.){3}[0-9]{1,3}" logfile
这个命令使用grep
命令来搜索文件中的文本,并使用正则表达式来匹配IP地址。具体解释如下:
- -o选项表示只输出匹配到的内容,而不是整行;
- -E选项表示使用扩展的正则表达式;
- 表示单词边界,用于确保匹配的是完整的IP地址;
- [0-9]{1,3}表示匹配1到3位数字;
- .表示匹配点号。
将logfile
替换为你要搜索的日志文件的路径和文件名。执行命令后,它将输出日志文件中所有匹配到的IP地址。
4. 线程安全单例模式
public class Singleton { private static volatile Singleton instance; private Singleton() { // 私有构造函数 } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
在上述代码中,使用了双重检查锁定(Double-Checked Locking)来确保线程安全。关键点如下:
- volatile 关键字用于保证可见性,确保多线程环境下对 instance 的正确访问。
- 在 getInstance() 方法中,首先检查 instance 是否已经被实例化,如果没有,则进入同步块。
- 在同步块内部,再次检查 instance 是否为 null,如果是,则创建一个新的实例。
- 使用双重检查的方式可以减少锁的竞争,提高性能。
这样,无论多少个线程同时调用 getInstance()
方法,都只会返回同一个实例,确保了线程安全的单例模式。
中间加了道八股,redis过期删除和内存淘汰策略
Redis中有两种方式来处理过期键(expired keys)和内存淘汰(eviction):过期删除和内存淘汰策略。
- 过期删除(Expired Keys Deletion): Redis使用过期删除策略来处理过期键。当一个键的过期时间到达时,Redis会在键被访问时检查它是否过期,并在访问时删除过期的键。这种方式确保了过期键在被访问时被删除,但可能会导致一些性能开销。
- 内存淘汰策略(Eviction Policy): 当Redis的内存使用达到上限时,需要淘汰一些键来释放内存空间。Redis提供了多种内存淘汰策略,用于确定要淘汰的键。
常见的内存淘汰策略有:
- LRU(Least Recently Used):淘汰最近最少使用的键。
- LFU(Least Frequently Used):淘汰最不经常使用的键。
- Random(随机):随机选择要淘汰的键。
- TTL(Time To Live):淘汰具有最早过期时间的键。
可以通过配置Redis的maxmemory-policy参数来选择所需的内存淘汰策略。默认情况下,Redis使用的是noeviction策略,即不淘汰任何键,当内存使用达到上限时,写操作会报错。需要注意的是,过期删除和内存淘汰策略是不同的概念。过期删除是处理过期键的机制,而内存淘汰策略是处理内存空间不足时的策略。它们可以同时使用,以确保Redis的内存使用和键的过期管理。
以上问题以收录在专栏。************************。
收录各个网友分享的各个公司的面经,并给出答案。