文章目录
- 引言
- 以往面经整理
- 八股
- 1、TCP拥塞控制机制
- 2、讲讲TCP中的Time-wait状态
- 3、Java怎么管理内存
- 4、Java的泛型怎么理解*
- 5、java为什么要有包装类
- 6、java并发编程的了解
- 7、缓存雪崩、缓存击穿、缓存穿透的区别的
- 8、read系统调用返回0标识什么?
- 9、linux怎么查看网络状态
- 10、输入url到显示网页的过程
- 11、RPC协议和HTTP协议有什么区别
- 12、Spring单体服务能够支持的QPS大概是多少,可以从哪些方面进行提升?
- 13、写一个后端接口,出现延迟瓶颈的时候,大部分卡在那里?
- 14、正常情况下,MySQL单表存储多少条数据比较合适?
- 15、MySQL查询时间大概是多少比较合适?
- 16、redis查询时间大概是多少比较合适?
- 17、Java的进程的内存大概分哪几个部分?
- 19、JVM管理的内存会包括直接内存吗?
- 20、Spring框架的优缺点
- 21、Spring中的IoC和DI是什么?作用是什么?
- 22、SpringMVC包含哪些组件?当收到请求的处理流程是什么?
- 23、介绍一下Bean的生命周期?BeanDefinition的作用?
- 24、Bean的作用域有哪些?默认的是哪种?多线程并发环境下,Singleton作用域可能会有什么问题?为什么会出现Bean之间的循环依赖问题?Spring中三级缓存是如何解决Bean循环依赖问题的?
- 25、redis支持事物吗?不支持的话,采用什么方式实现?
- 26、redis哨兵模式是用来解决什么问题的?他是如何保证高可用性的?
- 27、redis主从复制是如何工作的?说明其原理和过程?
- 28、协程是什么?
- 29、linux进程中,空间是如何分配的?
- 30、介绍一下Socket通信
- 31、HashMap扩容过程
- 32、堆排序原理
- 33、红黑树的原理
- 34、TCP和UDP的区别
- 35、调试过程使用的 Linux 命令
- 36、undolog和redolog
- 算法
- 场景题
- 1. 如何编写程序让一个CPU的占用率是50%
- 2 、1000万个数字找到前十个最大的数?(最小堆,总是将数值最大的十个元素保持在堆顶)
- 3、一等奖一个1%,二等奖2个2%,三等奖3个3%,如何设计这个抽奖系统?(长度100的数组,放1个1,2个2,3个3,其余位置放垃圾值,每次随机生成索引去数组里取值即可)
- 4、敏感词检测,给定一个敏感词库,然后让你判断一个文件(认为是一个string)中是否有敏感词
- 5、内存池伪代码手撕.让你实现一个分配和释放固定字节(32)的内存池
- 6、 十万个文件重命名,根据每个文件的前32字节进行排序,最大的文件重命名为为十万,最小的文件重命名为一
- 7、秒杀场景设计(前端限流、Nginx限流、队列限流、加锁防止超卖、延迟队列兜底)
- 8、一个web网站如何保证高性能
- 9、一个成熟的商业的web网站应该有哪些安全保证
- 智力题
- 部门信息搜集
- 正式面试
- 第一面
- 第二面
- 简单介绍一下 以前做的项目,每一个项目中最有成就的部分?
- 你认为项目中的技术难点是什么?
- Java中classloader的工作原理吗?
- 类是怎么找到?
- Java中有哪些classLoader?
- Java的解释语言再做一些系统优化的时候,会做哪些优化,提高他的编译性能?
- 即时编译可以展开说一下吗?Java的JIT机制知道吗?(这道题就是在胡扯了)
- TCP和UDP的区别?(整理到了)
- 你提到了拥塞控制,你能说一下TCP中的拥塞控制是怎么实现的吗?具体讲讲慢启动是怎么样的过程?(这个也被准备到了)
- 一个网站冲输入URL到显示在网站经历了哪些过程?(背过了,但是背的不好)
- 浏览器获取内容到页面展示,这个页面展示的过程和展示的内容可以讲讲吗?(这个涉及到前端了,不是很懂了,又在胡扯了)
- HTTP和HTTPS有什么样的区别?HTTPS的过程你知道吗?(这里不清楚这个HTTPS的RSA四次握手怎么实现了,简单说了一下,不行!)
- HTTPS具体的协议有哪些你知道吗?(这里不会了,不过已经准备完毕了!)
- 算法题
- 之前参过军,自己的想法还是怎么样?给我讲讲?
- 对未来职业发展有什么规划吗?
- 技术方向有什么选择吗?
- 反问1:当前部门是做什么的,可以介绍一下吗?
- 反问2:工作之后如何做技术积累?
- 第三面
- 总结
引言
- 腾讯面试应该是我面的最快的一个大厂,也是面的最突然的一个大厂,先记录着!许愿,来一个大厂的Offer!
- 过了二面,进了三面,没约面就给我挂了!莫名其妙!
以往面经整理
八股
1、TCP拥塞控制机制
- 网络层面的控制机制,防止过多的数据包同时在网络中传输,导致网路拥塞
- 作用
- 拥塞控制机制,通过慢启动、拥塞避免、快速重传和快速恢复等算法他调整发送速率避免网络拥塞。
- 出现拥塞时,TCP降低发送速率,减少网络负载,保证数据传输的可靠性。
- 拥塞控制机制,通过慢启动、拥塞避免、快速重传和快速恢复等算法他调整发送速率避免网络拥塞。
- 具体实现
- 主要分为五个步骤:慢启动、拥塞避免、超时重传、快速重传、快速恢复
- 慢启动
- 小窗口,确认就翻倍,指数增长,直到达到慢启动门限
- 拥塞避免
- 超过慢启动阈值,进入拥塞避免阶段,确认加一,线性增长
- 拥塞发生的处理阶段:发送速率不断增大,会出现数据都包,需要重传数据,两种机制
- 超时重传
- 一个ACK都收不到,直接慢启动阈值减半,恢复为慢启动窗口值,从头再来
- 快速重传和快速恢复
- 连续三个重复的ACK(接收是乱序的,ACK包含期望的数据包,1要2,13要2,134还是要2,会期望接收第一个空出来的包的序号),认为发生了丢包,拥塞窗口减少为原来的一半,慢启动门限设置为减少后的拥塞窗口大小,
- 进入快速恢复阶段,拥塞控制窗口加3,确认有三个包能够被收到,重传丢失报文
- 收到丢失报文的ACK后,将拥塞窗口设置为慢启动门限,直接进入拥塞避免,继续增大发送速率。
- 超时重传
2、讲讲TCP中的Time-wait状态
-
TimeWait产生在哪里
- 四次挥手过程中,主动关闭放发送完第四次挥手,进入TimeWait状态,持续2MSL,确保对方收到最后一个ACK报文
-
设置为2MSL的原因
- 避免本次连接的历史报文影响后续新创建的连接
- MSL报文最大生存时间,等待两个MSL能够保证当前链接所有报文都能够顺利消失
- 确保最后一次的ACK报文能够顺利到达,帮助被动关闭方正常关闭连接
- 被动关闭方超时没收到ACK,会重传FIN报文
- 避免本次连接的历史报文影响后续新创建的连接
-
TImeWait过多的危害
- 占用系统资源,包括文件描述符、内存资源、CPU资源、线程资源等
- 占用端口资源
-
怎么解决TimeWait过多的状态
- 客户端使用TCP_TW_REUSE参数,发起连接,复用旧的连接
- 服务端尽量让客户端主动断开连接,如果服务端要断开连接,发个信号给客户端,客户端来断
-
TimeWait过多的原因
- 没有使用长链接,客户端和服务端同时开始keepalive机制
- HTTP长链接设置过小,提高nginx中的keepalive_request参数
3、Java怎么管理内存
- Java中的所有内存管理都是通过虚拟机实现,采用分区的方式进行管理,不同区域的特性,存储的数据是不同的。主要包括以下几个运行时数据区域
-
程序计数器
- 当前线程所执行的字节码的行号指示器
-
虚拟机栈
- 线程私有 ,每一次方法调用就会生成栈帧
- 栈帧构成:局部变量表、操作数栈、动态连接、方法出口等信息
- 异常类型:StackOverflow 和 OutOfMemeory
-
Java堆
- 所有线程共享,保存对象实例(基本类型和Final Static修饰的),垃圾收集器管理的内存区域
- 物理上不一定连续,但是逻辑上连续
-
方法区
- 所有线程共享,存储虚拟机记载的类信息、常量、静态变量和即时编译的代码缓存等。
- java8:元空间
-
运行时常量池
- 方法区的一部分
- 每一个类信息都有常量池标,存放各种字面量和符号引用。
- 每一个已经加载的累都有一个运行时常量池
-
4、Java的泛型怎么理解*
- 在定义类和接口的时候使用类型参数,在使用时用具体的类型替换
- 提高代码的复用性
- 具体使用类型
- 泛型类 class Generic<T>
- 泛型接口 interface Generator<T>
- 泛型方法 <T> void printArray(E[] Array)
5、java为什么要有包装类
- Java是一个面相对象的语言,很多数据结构都必须存放Object对象,比如Collection
- 让基本类型具有对象的特征,丰富基本类型的操作类
6、java并发编程的了解
- 创建线程的多种方式
- 线程池
- Runnable接口,Callable接口等
- 线程同步的多种方式
- ThreadLocal
- Synchronized悲观锁
- ReentrantLock等AQS实现的锁
- volatile修饰词
7、缓存雪崩、缓存击穿、缓存穿透的区别的
缓存穿透
-
缓存和数据库都没有的数据,而用户不断发起请求。
- 专门有人利用不存在的key频繁攻击应用
- 正常情况下,是缓存中没有,然后去DB中查询,然后在写入缓存中,现在是DB中也没有,反复走两个过程。
-
解决方案
- 接口层增加校验,用户鉴权校验、id<0直接拦截
- 找不到设置为null,缓存去不到的数据,数据库中也没有,将key-value设置为key-null,并加入缓存,有效时间设置短点
- 布隆过滤器,快速判断某个元素是否在数据库中,不存在直接返回,关键在于Hash算法和容器大小。
- 通过布隆过滤器可知某个请求一定不存在或者可能存在
缓存击穿
- 缓存中没有,但是数据库中有的数据,在高并发的情况下,会给数据库带来瞬间大压力
- 热键在过期失效的一瞬间,还未生成,就有海量数据,直达数据库
- 解决方案
- 热点数据支持续期,持续访问的数据不断续期,避免因为失效而击穿
- 分布式锁重建缓存,发现缓存失效,重建缓存加互斥锁,当线程查询缓存发现缓存不存在就会尝试加锁,抢到锁的线程访问数据库重建缓存。
缓存雪崩
-
大量请求redis无法处理,直接到数据库中请求
- 缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至宕机
- 区别于缓存击穿:击穿是针对热点数据,这里是大量数据。
-
解决办法
- 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生
- 加互斥锁,并重建缓存
8、read系统调用返回0标识什么?
- read 系统调用返回 0 表示已经到达文件的末尾,读取操作不再有数据可用。这通常是在读取一个普通文件时遇到的情况。
9、linux怎么查看网络状态
- ifconfig: 显示和配置网络接口的状态。
- ip addr: 查看网络接口的详细信息(包括IP地址、MAC地址等)。这是 ifconfig 的现代替代品。
- netstat: 显示网络连接、路由表、接口统计、伪装连接、多播成员等信息。需要注意的是,netstat 已经被 ss 工具替代,但仍然广泛使用。
10、输入url到显示网页的过程
-
解析Url构造HTTP请求报文
- 解析url,解析出域名、端口号、资源路径等信息
-
域名解析为IP地址
- 查本地系统缓存和host文件,返回IP地址
- 查询本地DNS服务器=》根域名服务器=》顶级域名去服务器=》权威域名服务器
-
三次握手创建TCP连接
- 第一个SYN包,填上目标端口和源端口信息
-
网络层加上源IP地址和目标IP地址
-
数据链路层ARP协议获取MAC地址
- 通过ARP协议获取路径器的MAC地址,加上MAC头,天上目标MAC地址和源MAC地址
-
物理层转发路由器
- 物理层直接转发数据报给路由器,路由器再找到目标服务器,响应二次握手
-
完成握手发送信息
-
浏览器渲染响应消息显示
11、RPC协议和HTTP协议有什么区别
设计目标
- RPC协议
- 通过网络调用远程服务,表现得像调用本地函数一样。隐藏底层细节,专注于函数调用的借口和参数
- HTTP协议
- 通用通信协议,web浏览器和服务器之间的数据传输。
工作方式
-
RPC协议
- 调用方发送一个请求到远程服务器,服务器执行相应的函数,并将结果返回给调用方。
-
HTTP协议
- 基于请求和响应模式的协议。
使用场景
- RPC协议
- 适用于分布式系统内部服务之间的高效通信,适用于低延迟和高吞吐量的场景
- HTTP协议
- 广泛应用于客户端和服务端之间的通信,尤其是web应用和API服务中。
12、Spring单体服务能够支持的QPS大概是多少,可以从哪些方面进行提升?
13、写一个后端接口,出现延迟瓶颈的时候,大部分卡在那里?
-
数据库操作
- 查询时间过长:
- 缓存未命中、索引缺失、不必要的全表扫描、连接池耗尽、锁争用
- 优化建议
- 创建索引,优化查询语句
- 增加缓存层,减少数据库的直接访问
- 使用连接池,调优连接池参数
- 分区、分表、数据库读写分离
- 查询时间过长:
-
外部服务调用
- 调用第三方API或者微服务出现高延迟
- 网络延迟、服务不可达、超时重试、
- 优化建议
- 异步调用并返回结果
- 调用第三方API或者微服务出现高延迟
-
线程池和并发问题
- 线程池耗尽、线程争用资源
- 线程池大小配置不当、任务执行时间过长、锁争用或者死锁
- 优化建议
- 调整线程池大小(核心线程数、最大线程数等)
- 减少同步模块的大小,避免长时间持有锁
- 线程池耗尽、线程争用资源
-
网络IO
- 网络传输延迟、文件上传或者下载
- 网络带宽受限、带宽饱和等
- 优化建议
- 减少数据传输的大小或者压缩数据
- 采用更高效的数据传输协议
- 网络传输延迟、文件上传或者下载
-
垃圾回收GC
-
负载均衡和路由
14、正常情况下,MySQL单表存储多少条数据比较合适?
存储引擎角度
- InnoDB差不多是1000万到几亿条
表的宽度
- 每一行的数据很大,表很宽,查询和索引上的性能会下降很快
- 保持表结构紧凑
硬件限制
查询性能
15、MySQL查询时间大概是多少比较合适?
16、redis查询时间大概是多少比较合适?
17、Java的进程的内存大概分哪几个部分?
- 程序内存 = JVM内存 + 本地内存
19、JVM管理的内存会包括直接内存吗?
- 直接内存 = 元空间 + 直接内存
20、Spring框架的优缺点
优点
- 松耦合设计
- 通过控制反转和依赖注入实现了松耦合
- 轻量
- 版本大约2MB
- AOP面向切面编程
- 支持面向逻辑编程,将应用业务逻辑和系统服务分开
- 事务管理
- 提供了一个持续事物管理接口,可以扩展到上至本地事物,下至全局事物
- 容器管理
- spring包含并管理应用中对象的生命周期和配置
缺点
- 配置复杂
- 企业级配置比较复杂,设计多个模块
- 调试难度高
- 高度抽象和动态代理技术,调试定位比较困难
21、Spring中的IoC和DI是什么?作用是什么?
什么是IoC
- 依赖导致,一种设计原则。
- 程序依赖于抽象接口,不要依赖于具体实现,降低代码间的耦合度
具体实现方式
- 依赖注入DI
- 不通过new的方式在类内部创建依赖类对象,而是将依赖的类对象在外部创建好之后,通过构造器、函数参数等方式传入给类使用
- 依赖查找
- 容器中 的受控对象通过容器的API来查找自己所依赖的资源和协作对象
Spring IoC的理解
-
是一种基于容器的对象管理机制,Spring框架负责创建对象、管理对象之间的依赖关系、调用对象的方法等操作
- 应用程序只需要声明需要使用的对象和依赖关系,无需自己负责对象的创建和管理,实现了控制反转
-
基本流程
- 容器负责创建和管理对象,容器根据配置文件或者注解中的信息,自动创建和管理对象之间的依赖关系,然后将这些对象注入到应用程序中。
- 应用程序只需要声明需要使用的对象和依赖关系,通过注入的方式获取对象,避免了硬编码和耦合性的问题
-
主要实现方式
- 通过依赖注入实现的:创建对象的过程中,自动注入该对象所依赖的其他对象,构建对象之间的依赖关系
- 支持多种依赖注入的方式
- 构造函数注入、setter注入、字段注入
IoC实现原理
- 扫描加载:
- 扫描解析配置文件或者注解信息,将其转换为内部的对象定义和依赖关系,放到容器中BeanFactory中
- 反射注入
- 根据对象的定义和依赖关系,使用反射机制动态创建和初始化对象,并将对象注入到需要使用的地方
怎么理解SpringIoC容器
- 将创建对象的权利交给框架控制,不需要人为创建,实现了可插拔式的接口编程,降低了代码的耦合度,降低了扩展和维护成本
22、SpringMVC包含哪些组件?当收到请求的处理流程是什么?
什么是MVC
- 一种基于请求驱动类型的轻量级Web框架,将模型-视图-控制器分离,将web层进行职责解耦,分成逻辑清晰的几个部分。
请求流程
-
用户通过页面提出请求
- 用户通过view页面,向服务端提出请求
-
控制器接收解析并分发请求
- 服务端Controller控制器接收到请求后,对请求进行解析,找到相应的model,对用户请求到对应的model进行处理
-
控制器接收并转发结果
- model将运行结果交给controller,转发结果。
- 根据处理结果找到对应的响应view页面,经过渲染转发给客户端
23、介绍一下Bean的生命周期?BeanDefinition的作用?
什么是Bean
- 构成应用的主体,由SpringIoC容器管理的对象是Bean.
- 是由SpringIoC容器实例化、装配和管理的对象
- Bean是对象,一个或者多个不确定
- Bean托管在Spring中一个叫IoC的容器中
- 程序是由一个一个Bean构成的
生命周期
-
单例模式
- 单例对象的生命周期和容器相同
-
多例对象
- 出生:适用对象时,spring为我们创建
- 活着:对象只要在使用过程中,就一直活着
- 死亡:当对象长时间不用或者没有其他对象引用他时,由java垃圾回收机制回收
-
生命周期的4个阶段
- 实例化
- 实例化一个Bean对象
- 属性赋值
- 为Bean设置相关属性和依赖
- 初始化
- 检查依赖并进行前置处理,然后初始化前后执行
- 销毁
- 注册销毁的毁掉接口,使用Bean,通过DisposableBean和Destory-method进行销毁
- 实例化
BeanDefinition是什么
- 是Spring中定义Bean的配置元信息的接口
24、Bean的作用域有哪些?默认的是哪种?多线程并发环境下,Singleton作用域可能会有什么问题?为什么会出现Bean之间的循环依赖问题?Spring中三级缓存是如何解决Bean循环依赖问题的?
Bean的6个作用域
-
Singleton(默认的)
- 容器中只有唯一的bean实例,默认都是单例的,是对单例模式的应用
-
prototype
- 每一次获取都会创建一个新的实例对象,连续的getBean是获得了不同的实例
-
request(仅web可用)
-
session(仅web可用)
-
application(仅web可用)
-
websocket(仅web可用)
线程安全问题
- 单例模式不存在状态的情况下,不会有资源竞争问题,如果有状态,就会有线程安全问题
- 大部分的Bean都是无状态,比如说Dao或者Service
- 解决办法
- 在Bean中尽量定义不可变的成员变量
- 在类中定义一个ThreadLocal成员变量,将需要的成员变量保存在ThreadLoal中
循环依赖问题
- 多个Bean之间相互依赖,形成循环以来链
三级缓存解决依赖问题
- 将正在创建的对象放入一个专门用于缓存正在创建的Bean对象的缓存池中,后续创建其他的Bean对象,如果需要依赖于该缓存池中的正常创建的bean,直接使用,不在创建一个新的。
SpringBoot一般是可以处理的,程序可以运行,只是会出一些警告,没太关注过!
25、redis支持事物吗?不支持的话,采用什么方式实现?
- 不支持,一般是通过Lua脚本来实现事物。
26、redis哨兵模式是用来解决什么问题的?他是如何保证高可用性的?
解决问题
- 解决redis 主从复制架构中主服务器的故障切换问题,保证redis服务的高可用性。
实现方式
- 监控和检测
- 持续监控主服务器和从服务器的运行状态,通过发送ping命令实现的
- 哨兵之间会通过订阅/发布机制相互通信,共享监控信息
- 主观下线和客观下线
- 自动故障转移
- 选择优先级最高的服务器作为新的主服务器
- 配置持久化和更新
27、redis主从复制是如何工作的?说明其原理和过程?
- Redis主从复制是一种数据复制和备份的方式,它通过**将一个Redis实例(主节点)的数据复制到另一个或多个Redis实例(从节点)**来提高系统的可用性、读取性能和数据安全性。
- 数据流向:数据复制是单向的,只能从主节点流向从节点。主节点负责处理客户端的写请求,并将写操作同步到从节点;从节点则负责处理读请求,并将主节点发送过来的数据更新到本地。
- 角色分工:主节点以写操作为主,而从节点以读操作为主。这种分工有助于分担服务器的负载,特别是在读多写少的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
28、协程是什么?
- 轻量级线程:
- 协程是用户空间的轻量级线程,相比传统的线程,它的创建、销毁和切换上下文所需的资源更少,因此更加高效。
- 非阻塞挂起与恢复:
- 协程可以在执行到一定点时主动挂起自己,并将执行权还给其他协程或线程。随后,它可以在稍后的时间点恢复执行,而不需要创建新的线程或进程。
- 协作式调度
- 协程的调度是协作式的,即只有在协程主动挂起时才会切换到其他协程,而不是由操作系统或调度器强制进行的。
29、linux进程中,空间是如何分配的?
- Linux进程的空间分配是通过虚拟地址空间的划分和物理内存的映射来实现的。
- 虚拟地址空间被划分为用户空间和内核空间,并在用户空间中进一步细分为程序段、数据段、BSS段、堆和栈等内存段。
- 进程通过系统调用来分配和释放内存,以满足其运行时的需求
30、介绍一下Socket通信
- Socket不是一个协议,而是对TCP/IP协议的封装,位于网络传输层。
- 可以基于两种协议实现,分别是TCP和UDP
- 提供了在网络上进行通信的接口,允许不同的计算机通过网络进行数据交换
连接状态和方式
- 长链接,客户端和服务端一旦建立连接就不会主动断掉。
- 为了维持链接需要发送心跳消息
- 支持双向通信
应用场景
- 适合需要长时间保持连接、实时性要求较高的场景,比如在线游戏、即时通讯等
31、HashMap扩容过程
关键点
-
动态调整链表和红黑树
-
掩码优化,分散节点
-
计算新容量
- HashMap在扩容时,通常会将其容量增加到原来的两倍(即newCapacity = oldCapacity * 2)。但是,这个增长过程并不是无限制的,HashMap有一个最大容量限制。
-
创建新数组
- 根据计算出的新容量,HashMap会创建一个新的数组(Node[]类型)来存放扩容后的元素。
-
重新计算Hash值
- 扩容过程中,HashMap会遍历原数组中的每个元素,重新计算它们的hash值,并根据新的容量和hash值确定这些元素在新数组中的位置。然后,将这些元素迁移到新数组的对应位置上。
扩容优化措施
-
使用位运算进行hash值的计算
- 会使用(n - 1) & hash的计算方法来得出该元素在集合中的位置,其中n是数组的长度。使得hash值在数组中的分布更加均匀,从而减少hash碰撞。
-
动态调整链表和红黑树
- 在扩容过程中,如果某个桶(bucket)中的元素过多,HashMap会将这些元素转换为红黑树结构,以提高检索效率。
32、堆排序原理
构建堆
- 对于大顶堆,由于树的根节点肯定是所有节点中值最大的,所以需要从树的最后一层开始,逐渐把大值往上调整(左右孩子节点中,较大的节点和父节点交换),直到第一层
33、红黑树的原理
- 每个节点要么是红色,要么是黑色。
- 根节点是黑色。
- 每个叶子节点(NIL节点,空节点)是黑色。
- 如果一个节点是红色的,则它的两个子节点都是黑色的(也就是说在树上不可能有连续的红色节点)。
- 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
34、TCP和UDP的区别
面向连接
- TCP是面向连接的协议,再发送数据的时候,需要建立TCP三次握手
- UDP是无链接的协议,直接可以发送数据
可靠传输
- TCP通过超时重传、流量控制、拥塞控制保证数据的可靠传输
- UDP不考虑数据的可靠性
传输方式
- TCP是基于字节流的传输方式,包头更复杂
- UDP是基于包发送的,数据与数据之间是有边界的,包头更简单
35、调试过程使用的 Linux 命令
top指令
- 实时显示系统中各个进程的资源占用情况
netstat - 显示网络连接、路由表、接口统计等信息。
36、undolog和redolog
Undo Log回滚日志
-
功能
- 支持事务回滚
- 记录了事务开始之前的状态,事物执行失败需要回滚,使用Undo Log恢复到事务开始之前的状态
- 支持MVCC
- 多用户并发环境中,提供了行的旧版本数据,即使在某个事务正在修改数据,其他事务也能够看到该数据的旧版本
- 支持事务回滚
-
写入时机
- 事务对数据修改之前,数据的原始状态会记录到Undo Log日志中
-
记录内容
- 数据库的具体的值
- 当事务对数据库中数据进行修改时,存储的是没有修改之前的原始值
Redo Log重做日志
-
功能
- 回复已经提交事务的修改
- 记录所有修改数据库的操作,事务崩溃,也能通过redo日志进行恢复
- 保证数据的持久性
- 一旦事务提交,相关修改会被记录到redolog中,重放redo log回复修改
- 回复已经提交事务的修改
-
写入时机
- 事务提交之前就完成日志的写入
-
记录内容
- 具体值的变更,字段A变成字段B
- 事务日志想
注意
- Undo log并不是事务开始之前,而是事务中涉及到相关数据修改操作,才会记录对应修改数据的相关操作的
算法
1、并查集
- 这里整理了几道并查集的连接,具体如下,就不在这里写了!
- 学习整理连接
2、快速排序
- 两个注意点
- i小j大做交换
- j做划分两边排
import javax.xml.transform.Source;
import java.util.*;
class Main{
static void quickSort(int[] nums,int l ,int r){
if(l >= r) return;
int i = l -1,j = r + 1,x = nums[ (l + r) >> 1];
while(i < j){
do i ++;while(nums[i] < x);
do j --;while(nums[j] > x);
if(i < j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
quickSort(nums,l,j);
quickSort(nums,j + 1,r);
}
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int m = in.nextInt();
int[] nums = new int[m];
Random random = new Random(100);
for(int i = 0;i < m;i ++){
nums[i] = random.nextInt(100);
}
quickSort(nums,0,m - 1);
System.out.println(Arrays.toString(nums));
}
}
刚好看了一下关于随机数的生成
- 需要创建一个Random的实例,Random random = new Random();
- random.nextInt(100):随机生成0到100的数字
- random.nextDouble():随机生成0到1之间的浮点数
- random.nextBoolean():随机生成布尔型变量
3、位运算
- 只含有0和1的字符串,如果0的数量是奇数,就输出1,否则输出零,要求额外的空间复杂度不超过两个char型。
这里想到的是使用异或进行操作
import javax.xml.transform.Source;
import java.util.*;
class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int m = in.nextInt();
StringBuilder str = new StringBuilder();
Random random = new Random();
for (int i = 0; i < m; i++) {
if (random.nextBoolean())
str.append("1");
else
str.append("0");
}
System.out.println(str.toString());
byte res = 0;
for (int i = 0; i < m; i++) {
if(str.charAt(i) == '1'){
res ^= 1;
}
}
System.out.println(res);
}
}
这里补充一下java中不同基本数据类型所占据的字节数
- byte:1个字节
- short:2个字节
- int:4个字节
- long:8个字节
- float:4个字节
- double:8个字节
- char:2个字节
- boolean:1个字节
4、树的子结构
LCR 143. 子结构判断
- 题目链接
- 这个应该两者同时进行子结构的遍历,不过需要返回指定对应的状态,什么情况是可以继续进行匹配遍历,什么情况是匹配成功
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
boolean dfs(TreeNode a,TreeNode b){
if(b == null) return true;
if(a == null && b != null) return false;
if(b.val != a.val) return false;
if(!dfs(a.left,b.left)) return false;
if(!dfs(a.right,b.right)) return false;
return true;
}
public boolean isSubStructure(TreeNode A, TreeNode B) {
if(B == null) return false;
// 层序遍历一下,逐个判定每一个节点
Queue<TreeNode> q = new ArrayDeque<>();
q.offer(A);
while(!q.isEmpty()){
TreeNode temp = q.poll();
if(dfs(temp,B)) return true;
if(temp.left != null) q.offer(temp.left);
if(temp.right != null) q.offer(temp.right);
}
return false;
}
}
自己写出来了,面试的时候应该能够写出来吧
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
return (A != null && B != null) && (recur(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B));
}
boolean recur(TreeNode A, TreeNode B) {
if(B == null) return true;
if(A == null || A.val != B.val) return false;
return recur(A.left, B.left) && recur(A.right, B.right);
}
}
上述这个虽然时间效率高,但是明显是爆栈的代码!
5、二重锁的单例模式
public class Singleton {
// 使用volatile关键字确保多线程环境下instance变量的可见性和禁止指令重排序
private static volatile Singleton instance;
// 私有构造函数,防止外部通过new创建实例
private Singleton() {}
// 双重检查锁定方法
public static Singleton getInstance() {
// 第一次检查,如果instance已经被创建,则直接返回实例,无需同步
if (instance == null) {
// 同步块,确保只有一个线程能进入这个块
synchronized (Singleton.class) {
// 第二次检查,如果instance仍然为null,则创建实例
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
6、数据分布在不同的电脑中,如何高效的统计出这些数据出现次数前10的数据?
7、一个有序数组,如何优化插入,数组很大,不可以变成链表?
二分查找插入点
- 一种是返回左节点,如果不存在,就返回从左往右,第一个大于目标的值的索引
whle(l < r ){
int mid = (l + r) >> 1;
if(nums[mid] >= tar) r = mid;
else l = mid + 1
}
return l;
- 一种是返回右节点,如果不存在,就返回从右往左第一个小于目标的值的索引
while(l < r){
int mid = (l + r) >> 1;
if(nums[mid] <= tar) l = mid;
else r = mid - 1;
}
return r;
8、归并排序
- 这里是通过递归实现的,用到了两个数组,q是原始的数组,然后temp是和原始数组相同大小中间数组,用来保存临时交换之后的数组
- 主要流程分以下三步
- 找到中间点mid
- 遍历左半部和右半部,谁小移动谁
- 左半部小右半部大,就移动左半部到temp
- 右半部小左半部大,就移动右半部到temp
- 将剩余的元素移动到目标的数组中
- 将temp数组移动到q的l到r中
void merge_sort(int q[], int l, int r)
{
if (l >= r) return;
int mid = l + r >> 1;
merge_sort(q, l, mid);
merge_sort(q, mid + 1, r);
int k = 0, i = l, j = mid + 1;
while (i <= mid && j <= r)
if (q[i] <= q[j]) tmp[k ++ ] = q[i ++ ];
else tmp[k ++ ] = q[j ++ ];
while (i <= mid) tmp[k ++ ] = q[i ++ ];
while (j <= r) tmp[k ++ ] = q[j ++ ];
for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];
}
场景题
1. 如何编写程序让一个CPU的占用率是50%
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Main {
private static final int NUM_CORES = Runtime.getRuntime().availableProcessors(); // 获取CPU核心数
public static void main(String[] args) {
int numThreads = NUM_CORES / 2 - 4;
if (numThreads == 0) {
numThreads = 1; // 至少需要一个线程
}
// 创建对应的thread数组
Thread[] threads = new Thread[numThreads];
for (int i = 0; i < numThreads; i++) {
threads[i] = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
// 空循环,尽可能占用CPU资源
// 注意:这可能会导致CPU过热,因此请谨慎使用
}
});
threads[i].start();
}
try {
System.out.println("Press Enter to stop the CPU load simulation...");
System.in.read(); // 等待用户输入
} catch (Exception e) {
e.printStackTrace();
}
// 中断所有线程
for (Thread thread : threads) {
thread.interrupt();
}
System.out.println("CPU load simulation stopped.");
}
}
预防当前线程被中断,使用空循环进行实现!
2 、1000万个数字找到前十个最大的数?(最小堆,总是将数值最大的十个元素保持在堆顶)
分批次处理数据
- 内存不足以一次加载所有数据,将所有数据分成若干批次,每一个批次仅仅处理一部分数据,使用最小堆来找出每批次中前十个最大数,然后将这些批次的结果合并,再次使用最小堆处理,就得到全局前十大数。
3、一等奖一个1%,二等奖2个2%,三等奖3个3%,如何设计这个抽奖系统?(长度100的数组,放1个1,2个2,3个3,其余位置放垃圾值,每次随机生成索引去数组里取值即可)
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Main {
private int[] lotteryArray;
private void fillPrizes(){
// 填充奖项
List<Integer> availablePrizes = new ArrayList<>();
for(int i = 0; i < 100; i++){
availablePrizes.add(i);
}
// 抽取一等奖
Collections.shuffle(availablePrizes);
int firstPrizeIdx = availablePrizes.get(0);
lotteryArray[firstPrizeIdx] = 1;
availablePrizes.remove(firstPrizeIdx);
// 抽取二等奖
Collections.shuffle(availablePrizes);
for(int i = 0;i < 2;i ++){
int secondPrizeIdx = availablePrizes.get(0);
lotteryArray[firstPrizeIdx] = 2;
availablePrizes.remove(secondPrizeIdx);
}
// 抽取三等奖
Collections.shuffle(availablePrizes);
for(int i = 0;i < 3;i ++){
int thirdPrizeIdx = availablePrizes.get(0);
lotteryArray[firstPrizeIdx] = 3;
availablePrizes.remove(thirdPrizeIdx);
}
}
public String drawLottery(){
Random random = new Random();
// 生成0到100的随机数
int index = random.nextInt(lotteryArray.length);
int prize = lotteryArray[index];
switch (prize){
case 1:
return "一等奖";
case 2:
return "二等奖";
case 3:
return "三等奖";
default:
return "未中将";
}
}
public Main(){
lotteryArray = new int[100];
Arrays.fill(lotteryArray,0);
fillPrizes();
}
public static void main(String[] args) {
Main main1 = new Main();
for(int i = 0;i < 30;i ++)
System.out.println(main1.drawLottery());
}
}
4、敏感词检测,给定一个敏感词库,然后让你判断一个文件(认为是一个string)中是否有敏感词
- 长文本查找多个关键词,当关键词数量较多时,比起暴力算法性能更高。
Trie树
- Trie树是一种树形结构,用于存储字符串集合,并支持快速检索。在Trie树中,每个节点代表一个字符串的前缀,从根节点到某个节点的路径表示一个字符串。对于敏感词检测,我们可以将所有敏感词构建成一颗Trie树,然后遍历文本,使用Trie树进行匹配,从而找到文本中所有的敏感词。
Aho-Corasick算法
- Aho-Corasick自动机是一种基于Trie树和有限状态自动机的字符串匹配算法,它能够在单个扫描中查找文本中所有与Trie树中任何关键字相匹配的子串。与Trie树相比,Aho-Corasick自动机增加了“失败链接”(fail links),这些链接在匹配失败时提供了跳转到另一个节点继续匹配的可能性,从而提高了匹配效率。
- 提高了识别的效率
为什么不能使用KMP算法
- KMP算法常用于单模式串匹配,也就是在一个文本中查找一个固定的子串
- 需要为每一个敏感词单独运行一次匹配过程
为什么不适用字典进行匹配
- 存在哈希冲突,并且对于较长的敏感词或者需要匹配的字串过长,可能就不适用了
5、内存池伪代码手撕.让你实现一个分配和释放固定字节(32)的内存池
- 主要内容
- 连续内存块管理
- 追踪记录空闲内存块和已分配的机制
- 具体步骤如下
- 定义内存池结构体
- 包含内存块的数组
- 空闲列表(通过位图或者链表实现)
- 初始化内存池
- pool.pool = allocate(capacity * blockSize); // 分配足够的内存
- pool.freeBlocks[i] = true; 使用数组记录使用情况
- 分配内存块
- 遍历整个数组,查找第一个空闲内存块,freeBlocks,然后置为false
- 释放内存块
- 检查是否为无效内存块,如果不是已经分配的,就不进行操作
- 正常情况下,是将内存块的使用情况,置为false
- 清理内存池
- 析构函数,如果不在进行访问,就释放
- 定义内存池结构体
注意
- 并发访问
- 错误处理
- 内存对齐
6、 十万个文件重命名,根据每个文件的前32字节进行排序,最大的文件重命名为为十万,最小的文件重命名为一
固定长度键值的排序算法——选择排序算法
- 每一个排序字段都是32个字节固定大小,使用基数排序,时间复杂度是O(n * K),n是文件数,k是常数,k是文件字符的长度。
二进制格式读取
- 读取的32个字节数据,采用二进制数据,避免编码问题
- Java中使用byte[]来存储这些数据。
更快的操作
- 并行提高效率
- 1、并行读取文件
- 文件读取是IO密集型的操作,使用并行操作,多个线程同时读取不同的文件的内容,减少总得执行时间
- 2、并行排序
- 将整体数据分成若干个小的部分,然后分别进行特定位的基数排序,然后在合并。重复上述步骤,直到排序完毕!
- 这里使用了计数排序,通过计数排序知道的当前索引下的数字有几个,然后后续数字应该摆放在什么位置
- 1、并行读取文件
7、秒杀场景设计(前端限流、Nginx限流、队列限流、加锁防止超卖、延迟队列兜底)
要求
- 处理高并发
- 数据一致性
- 系统可用性
具体措施
-
系统并发能力
- 使用缓存
- 将商品信息数据保存在内存中,比如redis,减少对数据库的直接访问压力
- 前端限流
- 前端设计倒计时,按钮防抖等方式,减少无效或者恶意的请求发送到后端
- 后端限流
- 使用令牌桶或者流桶等限制请求速度,保护后端不过载
- 使用缓存
-
数据一致性和库存管理
- 库存一致性
- 使用乐观锁或者悲观锁,确保库存操作的原子性和一致性
- 异步处理
- 用户下单后,异步消息队列处理订单,快速响应用户请求 ?为什么?
*
- 用户下单后,异步消息队列处理订单,快速响应用户请求 ?为什么?
- 库存一致性
-
系统可用性和稳定性
- 负载均衡
- 准备多个服务器分摊流量,使用Nginx或者负载均衡起来分散用户请求
- 负载均衡
8、一个web网站如何保证高性能
- 主要分为四个部门,分别是前端优化、服务器优化、数据库优化、网络优化
前端优化
-
最小化和压缩资源
- 使用工具压缩HTML、CSS和JS文件,减少文件大小,加快加载速度
-
优化图片
- 压缩图片和使用更高级的格式,压缩图片大小,不损失质量
-
使用CDN内容分发网络
- 将内容还存在全球多个位置,使用户可以从最近的服务器加载资源,减少延迟
-
异步加载非关键资源
- 通过异步加载或者延迟加载 JS或者CSS,确保关键内容优先加载和渲染
-
减少HTTP请求
- 合并文件
服务器优化
-
负载均衡
- 使用负载均衡器,分散流量到多个服务器,提高网站的处理能力和可用性
-
缓存策略
- 在服务器或者引用层面建立缓存,包括数据库查询缓存、对象缓存等
数据库优化
- 索引优化
- 确保数据库查询使用有效的索引,提高查询速度
- 查询优化
- 编写高效的SQL语句,避免不必要的数据加载
- 使用数据库缓存
- 读写分离
网络优化
- 启用压缩
- 压缩需要传输的文件
- 使用HTTP/2和HTTP/3.0
- 新版的协议支持请求和相应的多路复用,减少延迟
9、一个成熟的商业的web网站应该有哪些安全保证
HTTPS
- 全站强制HTTPS
安全的用户认证
- 多因素认证
- 除了账号密码外,添加额外的认证步骤,包括短信、邮箱等
- 安全密码策略
- 安全会话管理
输入和输出过滤
- 防止SQL注入
- 使用参数化查询或者预编译语句来访问数据库,不是直接将用户输入嵌入到SQL语句中
- XSS防护(跨站脚本攻击)
- CSRF跨站请求伪造
安全的网络结构
-
隔离环境
- 将数据库、内部服务和公开的web应用分离,物理隔离公网和内部网
-
最小权限原则
- 所有系统组件实施最小权限原则,确保每一个组件只能执行其功能所必需的权限
智力题
1. 5升和3升杯子倒出4升水
首先判定,能否量出
-
欧几里得定理
- 如果两个容器的容量a和b的最大公约数可以整除需要测量出的水量c,那么这个问题就有解。在这个例子中,a和b分别是3和5,我们需要测量的水量c是4。
-
先凑出一个一升,然后三 加 一,就是四升
-
两个三升加起来,再减去五升,然后剩余一升,符合要求
2. 有八个球,用天平找出其中一个比较重的,要找出这个球,要几次?
- 将8个球分为3,3和2,然后使用排除法进行测量。
3、36匹马,6个赛道,至少赛多少次才能挑出前三名?
- 全部跑一遍,然后选出第一名,在进行跑一组,决出真正的第一名
- 然后奖第一名所在的组的第二名和第三名拉出来,第二名第一所在的第二名拉出来,然后第三名再拉进去,在比一次,就可以确定前三名。
4、硬币问题
- 23个硬币,10个正面朝上,然后别人遮住你的眼睛,你摸不出正反面,然后让你使用最好的方法,将这些硬币分成两堆,每面正面朝上的硬币个数相同。(可以进行硬币反转)
解决办法
- 分成10个硬币一堆,然后13个硬币一堆,然后将10个硬币的那堆完全翻过来
5、分盐问题
- 有一个天平,2克和7克砝码各一个,如何利用天平砝码在三次内将140克盐分成50克和90克
解决办法
- 两次平分,获得35克,然后在使用砝码调整15克和20克,相加即可。
6、100层楼两个鸡蛋问题
- 给你两个相同的鸡蛋,通过在100层的楼中的不同层扔下鸡蛋进行实验,试验出可以摔碎改嫁但的最高楼层,已知未碎的鸡蛋可以重复使用。求最少的试验次数n,使得在n次试验后,能够判断出该临界楼层?
解决办法
- 平均划分楼层,每组10个楼层,然后第一个鸡蛋决定了你要在哪个组开始,然后第二个鸡蛋,决定了你要从哪个组开始一个一个尝试!
- 最好的办法是14层。
10、判断灯和开关的对应关系
- 房间里有三盏灯,房间外有三个开关,只能进房间一次,怎么判断灯和开关的对应关系?
解决办法
- 将一盏灯开一段时间,再关掉,然后剩余两个开关随便开一个,发热的灯是第一个开的,亮着的是第二个的,没动静的是第三个的!
部门信息搜集
部门简介
- 腾讯游戏品质管理与云互动科技,致力于结合未来科技走向,推动互联网产品品质提升和互动科技应用。
- 我们深耕品质管理专业能力,建设业内顶级适配/性能/安全/数值等专项测试技术,支持《王者荣耀》、《和平精英》等全球知名游戏,为腾讯游戏保驾护航;搭建行业领先的质量云服务平台「WeTest」及性能分析专家「Perfdog」,服务全球数百万开发者,多次赢得国际国内权威机构认证;打造了荣获业界最佳云游戏平台奖项的「腾讯先锋」,建设基于AI技术的「Turing Lab」,积极布局云游戏、视觉AI、虚实互动等尖端科技,与北大、剑桥等研究机构紧密合作,在CVPR、NeurIPS等顶会发表论文;建设游戏测评平台「游测前线」,携手全球玩家为游戏工作室提供策略支持。
WeTest是干什么的?
- 一站式质量云。平台集成云手机、兼容测试、功能测试、性能测试、安全测试、自动化测试等优秀产品服务,满足众多开发者从研发到运营各阶段的测试需求,360度保障产品质量。
- WeTest第一是想通过品质标准,帮助开发者孕育高品质的品牌、产品,帮其获得客户,以及更多的流量、推荐,提升用户口碑、行业认可等等。
- 帮助开发者和企业能够更直接地接触到高品质标准,知道方向在哪。对于高品质,很多开发者不一定是不想,而是不知道。
- WeTest也和行业其他一些引擎公司、芯片公司和手机厂商合作,共同打造品质生态,让开发者能够更容易、更全面获得品质提升的机会
特点
-
APM性能监测工具
- APM用得更多的其实是开发和运营人员,并不是测试人员,比如APM能够监控产品哪部分不流畅、卡顿,帮助项目组迅速定位问题进行优化。
- 针对特定的性能问题,会推出对应的解决方案
-
企鹅风讯
- 通过企鹅风讯了解各个渠道,对于游戏的反馈声音,提炼告知项目组,项目组进行优化!
- 加强出海政策的帮扶,了解目标地的政策和舆情
-
适配兼容
- 通过腾讯云解决适配兼容问题
-
云测试
- 通过云测试,提高测试效率,取代早期适配兼容,单机逐个运行的效率低下问题
- 将不同类型的手机放到云上,通过自动化的方式提供测试服务,将开发涉及的各个方面的结果一并交付给开发者
总结
- 详细询问关于项目中性能检测的部分,这里要仔细看过,做过类似的测试
- 感觉也看不出来啥,还是好好看看项目吧!
正式面试
第一面
- 基本上题目都在上面了,这里就具体详细说明了!
第二面
简单介绍一下 以前做的项目,每一个项目中最有成就的部分?
你认为项目中的技术难点是什么?
Java中classloader的工作原理吗?
- 类加载:通过全限定名找打
- 连接
- 验证:魔法字,是否符合语义规范。检察引用对象是否可达
- 准备:静态变量分配默认值
- 解析:符号引用转化为直接引用
- 初始化:对静态变量进行赋值操作
类是怎么找到?
使用Class对象
- Java中,每一个类都有对应的Class对象,包含了与类有关的信息,通过以下几种方式获得类的Class对象
- 使用Class.forName方法,通过类的全限定名来动态加载类,并返回该类的class对象
- 使用.class语法,直接在类名后面加上.class获取该类的Class对象,不需要类的实例
- 使用对象的getClass方法,如果已经有一个类的实例,可以通过的getClass来获取Class对象
利用反射API
- 一旦有类的Class对象,使用反射机制访问该类的各种信息
Class<?> clazz = Class.forName("com.example.Person");
// 使用无参构造函数创建Person实例
// 首先获取无参构造函数对象
Constructor<?> constructor = clazz.getConstructor();
// 然后使用newInstance()方法创建实例(在Java 9及以后,建议使用Constructor的newInstance()的替代方法)
Object personInstance = constructor.newInstance();
Java中有哪些classLoader?
三类类加载器
- 启动类加载器,核心类,基本数据类型,C或者C++实现的
- 扩展类加载器
- 应用类加载器,自定义的类加载器
Java的解释语言再做一些系统优化的时候,会做哪些优化,提高他的编译性能?
即时编译技术
- 即时编译和多层编译
编译器优化技术
-
方法内联
- 将方法调用替换为方法体的直接执行,减少方法调用的开销
- 主要是针对小型且频繁调用的方法有效
-
逃逸分析
- 分析对象的使用情况,确定对象不会逃逸出当前作用域,JVM会将其分配到栈上,而不是堆上,减少垃圾回收的压力
-
循环展开
- 减少循环控制的开销,通过复制循环体减少循环次数,来提高执行效率
-
死码消除
- 移除永远不使用的代码,减少字节码和编译后的代码大小,提高执行效率
-
常量折叠
- 在编译时将常量表达式的结果计算出来,在代码中直接使用这些结果,减少运行的计算量
垃圾回收优化
- 垃圾回收算法的选择
- 堆内存的管理
线程优化
- 线程本地存储
- 为每一个线程独立分配存储空间,减少线程间的数据共享和竞争,提高并发性能。
- 锁优化
- 锁升级,减少锁的开销和提高并发性。
即时编译可以展开说一下吗?Java的JIT机制知道吗?(这道题就是在胡扯了)
解释执行和字节码
- 编译过程
- Java源代码首先通过javac编译器编译成字节码.class文件,在程序运行之前完成。
- 解释执行
- 当程序执行的时候,JVM回去加载并解释执行这些字节码
- JVM内部有一个解释器,直接读取字节码并转换为可以在当前硬件上执行的机器码
- 转换是运行时动态进行的,速度较慢
- JVM内部有一个解释器,直接读取字节码并转换为可以在当前硬件上执行的机器码
- 当程序执行的时候,JVM回去加载并解释执行这些字节码
即时编译JIT技术
-
作用对象
- 作用于JVM中正在运行的程序
- 当JVM检测到某个方法或者代码块被频繁调用时(成为热点代码),JIT编译器会介入,将热点代码从字节码直接编译成针对当前硬件优化的机器码。
- 作用于JVM中正在运行的程序
-
优化目的
- 提高程序的运行效率
- JIT 生成的机器码更加接近硬件,执行速度更快
- 使用各种优化技术:
- 方法内联
- 逃逸分析
- 循环展开
- 死码消除
- 提高程序的运行效率
-
与解释执行的关系
- JIT编译并不能取代解释执行,而是与解释执行并存
- 运行初期,JVM主要依赖解释执行来快速启动程序
- 运行中期,JIT编译器逐步介入,特点代码编译成高效的机器码
- JIT编译并不能取代解释执行,而是与解释执行并存
TCP和UDP的区别?(整理到了)
面向连接
- TCP是面向连接的协议,再发送数据的时候,需要建立TCP三次握手
- UDP是无链接的协议,直接可以发送数据
可靠传输
- TCP通过超时重传、流量控制、拥塞控制保证数据的可靠传输
- UDP不考虑数据的可靠性
传输方式
- TCP是基于字节流的传输方式,包头更复杂
- UDP是基于包发送的,数据与数据之间是有边界的,包头更简单
你提到了拥塞控制,你能说一下TCP中的拥塞控制是怎么实现的吗?具体讲讲慢启动是怎么样的过程?(这个也被准备到了)
- 网络层面的控制机制,防止过多的数据包同时在网络中传输,导致网路拥塞
- 作用
- 拥塞控制机制,通过慢启动、拥塞避免、快速重传和快速恢复等算法他调整发送速率避免网络拥塞。
- 出现拥塞时,TCP降低发送速率,减少网络负载,保证数据传输的可靠性。
- 拥塞控制机制,通过慢启动、拥塞避免、快速重传和快速恢复等算法他调整发送速率避免网络拥塞。
- 具体实现
- 主要分为五个步骤:慢启动、拥塞避免、超时重传、快速重传、快速恢复
- 慢启动
- 小窗口,确认就翻倍,指数增长,直到达到慢启动门限
- 拥塞避免
- 超过慢启动阈值,进入拥塞避免阶段,确认加一,线性增长
- 拥塞发生的处理阶段:发送速率不断增大,会出现数据都包,需要重传数据,两种机制
- 超时重传
- 一个ACK都收不到,直接慢启动阈值减半,恢复为慢启动窗口值,从头再来
- 快速重传和快速恢复
- 连续三个重复的ACK(接收是乱序的,ACK包含期望的数据包,1要2,13要2,134还是要2,会期望接收第一个空出来的包的序号),认为发生了丢包,拥塞窗口减少为原来的一半,慢启动门限设置为减少后的拥塞窗口大小,
- 进入快速恢复阶段,拥塞控制窗口加3,确认有三个包能够被收到,重传丢失报文
- 收到丢失报文的ACK后,将拥塞窗口设置为慢启动门限,直接进入拥塞避免,继续增大发送速率。
- 超时重传
一个网站冲输入URL到显示在网站经历了哪些过程?(背过了,但是背的不好)
-
解析Url构造HTTP请求报文
- 解析url,解析出域名、端口号、资源路径等信息
-
域名解析为IP地址
- 查本地系统缓存和host文件,返回IP地址
- 查询本地DNS服务器=》根域名服务器=》顶级域名去服务器=》权威域名服务器
-
三次握手创建TCP连接
- 第一个SYN包,填上目标端口和源端口信息
-
网络层加上源IP地址和目标IP地址
-
数据链路层ARP协议获取MAC地址
- 通过ARP协议获取路径器的MAC地址,加上MAC头,天上目标MAC地址和源MAC地址
-
物理层转发路由器
- 物理层直接转发数据报给路由器,路由器再找到目标服务器,响应二次握手
-
完成握手发送信息
-
浏览器渲染响应消息显示
浏览器获取内容到页面展示,这个页面展示的过程和展示的内容可以讲讲吗?(这个涉及到前端了,不是很懂了,又在胡扯了)
- 浏览器接收到响应之后,会执行如下工作
- 解析HTML文档,构建DOM树
- 加载CSS样式表和JS脚本,应用这些样式和脚本对页面进行渲染
- 绘制内容到视口中
- 按照HTML文档的结构和CSS样式规则,将页面会知道浏览器的视口
- 加载外部资源
- 如果涉及到外部资源,发起额外的http请求获取资源,并嵌入页面中的
- 显示页面
HTTP和HTTPS有什么样的区别?HTTPS的过程你知道吗?(这里不清楚这个HTTPS的RSA四次握手怎么实现了,简单说了一下,不行!)
- 这个当时没有回答出来,后续回去好好整理学习了一下,现在在遇到类似的问题,基本上都能回答出来了!
答案
- TCP三次握手
- TSL/SSL四次握手
-
第一次握手
- Client Hello
- 发送如下信息
- TLS版本号
- 密码套接字
- 随机数A
-
第二次握手
- Server Hello
- 发送如下信息
- TLS版本号
- 密码套接字
- 随机数B
- 数字证书
- 公钥
- Server Done
- 信息发送完毕
-
第三次握手
- 验证certificate
- 生成随机数
- 生成对称密钥,并生成摘要,开始使用对称密钥进行通信,请服务器进行验证
-
第四次握手
- 服务器执行相同操作
-
简陋了点,不过我觉得把三次随机数、CA等关键信息说出来,应该可以应付面试了!
HTTPS具体的协议有哪些你知道吗?(这里不会了,不过已经准备完毕了!)
- SSL(Secure Sockets Layer):
- 安全套接层协议,位于可靠的面向连接的网络层协议和应用层协议之间的一种协议层。
- SSL通过互相认证、使用数字签名确保完整性、使用加密确保私密性,以实现客户端和服务器之间的安全通讯。然而,随着技术的发展,SSL已经逐渐被TLS所取代。
- TLS(Transport Layer Security):
- 传输层安全协议,是SSL的后续版本,用于在两个通信应用程序之间提供保密性和数据完整性。
- TLS协议由TLS记录协议(TLS Record)和TLS握手协议(TLS Handshake)两层组成。
算法题
- 求解连续子数组最大和的算法实现!
- 当时只写出来一个暴力搜索算法,后来回家自己又写出了动态规划,具体连接
- 典型的区间DP,集合的划分点就是每一个序列的终点,
class Solution {
public int maxSubArray(int[] nums) {
int res = nums[0];
int n = nums.length;
int f = nums[0];
for(int i = 1;i < n;i ++){
f = Math.max(nums[i],f + nums[i]);
res = Math.max(res,f);
}
return res;
}
}
三分钟又刷了一遍,这个题目区分点,就是达到节点i的时候,是否需要增加其他的节点,还是仅仅只有当前点节点!
之前参过军,自己的想法还是怎么样?给我讲讲?
个人开始回顾往事了!
对未来职业发展有什么规划吗?
1、继续写博客
2、持续向周围大牛学习
(给面试官展示我的博客!!)
技术方向有什么选择吗?
不挑技术,主要能够让我长期从事,都能够深度发展
反问1:当前部门是做什么的,可以介绍一下吗?
- 腾讯里面做游戏的项目,涉及的技术栈比较多,前端、客户端、go、c++、虚拟引擎等,是一个涉及面分厂广的一个组。
- 后台开发主要是go,然后就是C++,需要转语言,java后台会少很多。
反问2:工作之后如何做技术积累?
- 很欣赏经常写博客的人,是非常好的习惯,可以做很多分享。希望你工作之后继续保持!
第三面
准备
- 三面主要会问项目内容,相关指标的检测
- qps
- 服务器性能
- 处理能力
- 然后总监面问场景题的相关内容,这部分得好好看看!
1、详细讲讲你的项目
2、你有什么技术提高?
3、你的东西思路在我们公司的环境用不上,这个设计没什么用?
4、你就说你有什么长板?到了这个环节,解决问题的能力基本上都没什么问题,我们主要是看你的长处!
反问1:总监比较忙,没有反问环节!
总结
8/10
- 一面过了,二面就不知道了,已经三天了,还没有任何结果,人家说要八天,祈祷通过吧!
8/21
- 今天三面,不知道怎么样,心里还是满忐忑的,不过基本上之前面试的问题都已经解决了,都已经整理过了!
- 不管明天怎么样,都得闯一闯,闯成功了,就成了,不成,就在来,继续面试!
- 今天早上心里的真的是超级紧张!又是等待论文录用通知,又是腾讯最后一面,超级兴奋!
- 加油!先冲了再说!
8/30
- 就这样,莫名其妙地挂了,枉我还花时间去准备了公司相关的一些业务知识!已经通过了二面,状态变成了复试中,应该约三面了,结果莫名其妙就挂了!HR也没有回复消息!无所谓了,再投吧!只能投诉了!
9/6
- 最终还是挂了,在主管面,整理了那么多,还是挂了,难受!