【牛客】腾讯后台面经整理

1. java虚拟机的GC原理

哪些需要回收(引用计数法和可达性分析法),什么时候回收(新生代、老年代、永久代回收时间,minorgc和fullgc),怎么回收(三种经典垃圾回收以及分代收集算法)

2.可达性算法

GC ROOTS作为根对象作为起点,根据引用关系向下搜索,走过的路径是“引用链”,如果某个对象到GC Roots没有引用链,那么就是不可达,就可以被回收

3.GC ROOTS有哪些

1.虚拟机栈中引用对象
2.方法区静态属性
3.方法区常量
4.本地方法栈Native方法引用对象

4.弱引用的用处

缓存和防止内存泄漏。比如threadlocalmap->entry->key这种引用,虽然想回收threadlocal,但是由于还有引用就回收不了,形成了内存泄漏。因此对key的引用作为弱引用,一旦取消了threadlocal的强引用后也会被回收

5.如何控制线程池的并发数

线程池的创建:存放线程的集合,核心线程最大线程,没有空闲线程时需要一个队列存储任务
方法1:使用semaphore控制
方法2:开一个ThreadPoolExecutor设置最大数量
方法3:用一个AtomicInteger做一个计数器
方法4:使用ArrayBlockingQueue进行阻塞

6.乐观锁是什么

假设数据一半不会造成冲突,在数据进行提交更新的时候,才对数据是否冲突进行检测。cas检测。
涉及到三个值:内存地址V,旧的预期值A,新值B。存在ABA问题,用版本号机制解决。

7.CAS的缺点

1.CPU消耗大
2.只能保证一个共享变量的原子性,多个要使用atomicReference或者用sychronize锁机制
3.存在ABA问题,可以使用atmomicStampedReference,不仅要检查值,还要检查标志

8.java里面怎么实现一个乐观锁(调用)

juc建立在cas上的,提供了atomicInteger, atomicBoolean和AtomicLong等

9.哈希冲突一般怎么解决

开放定址法:遇到冲突寻找一个新的地址(线性探测+1,二次探测前后看)
rehash法:再引入一个新的哈希,直到不重复为止,但是会有额外的时间消耗
链地址法:将哈希相同的存到同一条链上
建立公共溢出区:将发生冲突都放在溢出表中

10.拉链法如果重复比较多的时候,性能下降很厉害咋办

动态调整槽位的数量,避免冲突过多
良好的哈希函数,减少冲突概率
负载因子控制,控制每个链的平均长度
冲突解决策略,使用开发定址或者再哈希的方法

11.红黑树和AVL的区别

AVL比红黑树更加平衡,但是在插入和删除的时候可能会存在大量的旋转
因此,如果插入和删除频繁的情况下,优先考虑性能更好的红黑树
反之,如果查找更频繁,优先使用AVL树

12.AVL和RBT旋转次数比较

AVL:插入最多一次旋转,删除最多logN次
RBT:插入最多2次,删除最多3次

13.一致性哈希知道吗

使用环形的哈希空间,在增加或删除一个服务器的时候,尽可能少的改变原有的映射关系
将请求均匀地分配到服务器上,确保同样的用户总是访问同一个服务器等,虚拟节点解决哈希倾斜
顺时针移到的第一个服务器就是对应的哈希服务器

14.time_wait和close_wait

time_wait:我方主动调用close后,收到对方确认之后变成time_wait
close_wait:对方主动关闭或者网络异常导致中断,我方会变成close_wait, 需要调用close正确关闭

15.time_wait保持多长时间

2MSL

16.为什么是2MSL

保证旧的连接状态不会对新的连接状态产生影响
去向ACK的最大存活时间(MSL) + 来向FIN的最大存活时间(MSL)

17. 一个32位的JVM可以管理多大的内存

32位处理器的内存空间有4GB,但是JVM会相对小一点,例如2-3GB

18.跳表数据结构,时间复杂度

多个索引层,快速查找、插入、删除
时间复杂度都是logN

19.跳表应用场景

LSM TRee:生成有序文件,用跳表在内存忘磁盘flush效率很高,level DB和Rocket DB都用了LSM TRee
其他场景:Lucene, elasticSearch, Redis等实现快速搜索

20.为什么redis中用跳表而不用RBT?

实现有序集合zset,集群节点中做内部数据结构时候用到skiplist
原因:
1.跳表的范围查找更加简单
2.平衡树的插入和删除可能引起子树的调整
3.跳表的内存占用更小,平衡树每个节点2个指针,跳表的指针平均为1 / (1 - p)
4.实现更简单

21.如何定位数据库的慢查询

1.看slow_query_log, 看看超过long_query_time才会记录到日志中
2.explain查看sql执行计划
3.步骤2只是预估的,使用profiling看到详细的执行耗时,例如资源切换、io和上下文信息等
4.optimizer trace看到具体的执行过程信息

22.为什么索引可以加快查询速度

可以由全表扫描变成索引扫描,例如从on变成ologn

23.b+t和rbt有什么区别

结构差异:rbt是自平衡二叉搜索树,通过对节点进行染色和旋转保持平衡,每个节点存储一个关键字
b+t是多路平衡查找树,一个节点可以存多个关键字和指针;b+t非叶子结点只能是索引,叶子才有关键字
使用场景:b+t存储多个关键字,例如数据库查询建立索引
rbt有快速的增删查效率,但是批量读或顺序遍历时候效率不如b+t,在内存中进行简单操作可用红黑树
存储空间:红黑树需要更多的额外内存维护颜色和指向父节点指针

24. 3层b+t可以存多少行数据

在innodb,最小存储单位是page,一个page是16kb,3层大概是2kw,要三次io
b树非叶子结点可以存的索引更少,导致树的高度更大,导致io更大

25.一个进程fork一个子进程,内存占用会不会翻倍

不会,用了写时复制的技术,只有当父进程的各段内容要发生变化时,才会将父进程的内容copy一份给子进程
在没有改变之前,两者的物理空间是同一个,但是虚拟空间不同

26.java里的垃圾回收机制,这几种标记法的区别?

1.标记清除算法:对存活的进行标记,对未标记的进行清除;两个阶段效率不高,存在空间碎片
2.标记复制算法:将内存分成等大小的两块,每次只用一块,用完一块就将还存活的对象放到另一块,然后清理这一块;不存在碎片(适用新生代)
3.标记整理法:复制算法要在对象存活率高的时候进行,否则效率变低;整理法将存活的对象向一端移动,然后清理掉边界外的(使用老年代)

27.hashMap底层结构

jdk1.7及之前:数组 + 链表
jdk1.8:数组+链表+红黑树

28.什么时候转红黑树

链表长度大于等于8,根据poison分布得到链表元素个数和概率的对照表,长度为8的时候已经很小,变成红黑树提高查找效率

29.hashmap如何扩容

负载因子0.75,太多会产生哈希碰撞,太少会浪费空间
当一个map填满了75%的bucket,将会创建原来大小两杯的bucket数组,重新调整map的大小,并将原来的对象放入新的bucket当中,这叫rehash

30.为什么链表和红黑树之间转换有个6和8之间的缓冲区

大于等于8,链表转rbt
小于等于6,rbt转链表
中间有个7可以防止数据结构频繁切换

31. hashmap是非线程安全的,怎么变成线程安全的

hashmap全部操作没有加锁,会存在数据覆盖等,hashtable只是简单+synchronized,效率低下
concureenthashmap通过原子操作和局部加锁保证了线程安全同时减少了性能损耗
1.8后,concurrenthashmap从segment到hashentry,进一步降低锁的粒度

32. 如果读多写少的场景,怎么设计

引入copyonwrite的思想,去掉锁,写数据的时候利用拷贝的副本来执行,用空间换时间
配合volatile修改后立马更新到新的值
如果用读写锁的话,会导致写锁阻塞大量读操作

33. 假设我们写个服务器,估计要支持几万人同时在线,这时候应该怎么来做?

一台云服务器大概支持2000左右用户
通过负载均衡技术如nginx将流量分配到多台服务器上提高系统可靠性和可扩展性
同时可以使用io多路复用技术提高程序并发新能

34.io多路复用

bio:等待整个read的过程
nio:需要轮询,知道成功
io多路复用:select之后等到socket激活,激活后直接read完成,优点就是可以在同一个线程内同时处理多个io请求

35.ws底层原理

实现了浏览器和服务器的全双工通信,更好地节省服务器资源和带宽来实现实时通讯
ws基于tcp,握手用http,后建立tcp连接,解决了轮询造成了额外通信开销

36.tcp udp对比

tcp:面向连接、可靠、字节流、传输效率慢、所需资源多、文件邮件、 首部字节多
udp:无连接、不可靠、数据报文段、传输块资源少、语音视频、 首部字节少

37. 为什么tcp慢

tcp传输数据前要先建立连接,还有拥塞控制流量控制等以及可靠性控制,所以传输效率更慢

38. udp接受到包不是按顺序的,怎么办?

1.发送端对包进行标号
2.接受端允许短暂时延等待乱序的包

39. redis和mysql的区别

redis缓存数据库,mysq关系型数据库
mysq持久化数据到硬盘,但是速度较慢,但没有空间限制
redis将使用频繁的数据写缓存,读取块,基于内存,但是空间小
高性能用redis,不需要高性能用mysql,两者间进行同步

40. redis持久化

RDB:在指定时间间隔把内存数据写入侧畔,fork一个子进程,可能会有丢失
AOF:以日志的形式记录每一个操作,可以重写减少容量,比较慢

41.如果有一个群消息,要怎么处理?每个人读取的进度不同

redis实现多人消息队列
redis基于订阅发布模式实现广播模式,多个消费者同时订阅多个信道,即时接受,但是如果不在线可能会丢失或者积压(类似rabbitmq)
stream类型:消息可回溯,可以被多个消费者读取,可以阻塞读取,功能完善的消息队列

42.拆表的依据

垂直拆分:将一张列比较多的表拆成多张表(把不常用的字段单独一个表,将大字段单独拆分,把经常组合查询的列放一张表)
水平拆分:行拆分,超过200w就会变慢

43.如何定位cpu占用过高的问题

top命令找到进程pid
用top -H -p找到进程中消耗资源的线程id
将线程转换16进制
用jstack进程id | grep 线程id 查看线程状态信息即可

44.消息队列

不需要马上获得结果,但是对并发量又有控制,就使用消息队列
解耦异步削峰

45.redis+lua扣减库存怎么做

执行lua脚本时,服务器活动在期间都被阻止
业务逻辑:调用redis的get查看库存,判断是否足够,如果足够则扣减
lua脚本变成字符串,添加脚本预加载机制

46.为什么redis+lua可以保证原子性

redis通过eval调用lua脚本
因为redis使用了单线程执行模型,redis会把lua脚本作为一个整体当作任务加入队列,单线程按照顺序依次执行

47.redis和mysql的一致性

读写双写:更新mysql的同时更新redis
数据过期策略:确保缓存数据不会过期太久,减少数据不一致
异步更新:mq + redis异步更新,提高系统性能可靠性
监控和恢复:定期监控redis和mysql的一致性并进行修复

48.rocketmq底层原理

生产者发送信息,broker存储信息追加到commitLog中,消费者拉去信息(offset记录位置)
pageCache:加速消息的读写操作,物理缓存
consumeQueue:逻辑缓存,消费者快速拉去和跟踪消息
崩溃恢复机制:offset上次消费, checkpooint读取offset, commmitLog校验

49.redis分布式锁

意义:控制分布式系统全局的共享资源
在分布式系统中控制共享资源的访问问题
使用setnx,返回1就有锁;使用del释放锁;设置expire超时时间

50.sql的索引类型

哈希表:使用于唯一等值查询,通过哈希函数快速定位,缺点是不支持范围、排序、匹配等
b树:每个节点存储key和data,叶子结点指针null
b+树:叶子存的data,其他存索引

51.哪些场景索引会越来越慢

索引失效的场景:
字段类型不匹配,使用了表达式,用了内置函数,like用了左模糊匹配,不是联合索引的最左边,使用了or、in、not in之类的

52.java语言是怎么运行的

java通过javac到字节码文件.class,再通过java命令运行
具体而言,得到字节码后通过类加载器classloader加载内存,通过字节码校验器校验后,并翻译成机器码
jvm:负责将编译后的class文件转换成当前计算机能够执行的指令

53.java支持热更新吗

热更新是指在不中断应用服务器情况下更新程序,主要实现依赖于java的类加载器机制
当需要更新一个类的时候,创建一个新的类加载器加载新版本的类,旧版本的不需要改变

54.nignx的作用

路由功能:根据访问选择后台服务器
负载均衡:选择服务器,降低服务端压力,提高并发量
动静分离:充当静态服务器,比如html文件等

55.nignx为什么要分发

例如分发到不同的微服务,根据不同的host、域名、开发语言、浏览器之类的

56.跨域是谁控制

跨域是浏览器的限制,解决跨域问题需要前端和服务端在http请求头响应头上的配合

57.java中锁有哪些类型

悲观锁vs乐观锁
公平锁vs非公平锁
可重入锁vs不可重入锁
自旋锁
共享锁vs独享锁

猜你喜欢

转载自blog.csdn.net/weixin_40986490/article/details/132287573