# Java
## 中间件
### redis
- redis的数据结构和应用场景
- redis的数据结构
- dict
- dictht ht[2];
- dictEntry **table;
- dictEntry *next
- 渐进式rehash
- 为ht[1]分配空间,同时持有ht[0]和ht[1],将rehashidx设为0
rehash期间,每次对字典执行任意操作时,除了执行对应操作之外,还将ht[0]在rehashidx索引上的键值对rehash到ht[1],随后rehashidx+1
直到rehashidx增加到ht[0].size,此时ht[0]的所有键值都迁移到ht[1],rehashidx重置为-1,rehash完成
rehash的过程中,ht[0]和ht[1]可能同时存在键值对,因此在执行查询操作的时候两个哈希表都得查,而如果是执行插入键值对操作,则直接在ht[1]上操作就行。
- ht[0]只会减少不会增加
- https://blog.csdn.net/llllllkkkkkooooo/article/details/115141263
- https://blog.csdn.net/cxc576502021/article/details/82974940
- https://tech.meituan.com/2018/07/27/redis-rehash-practice-optimization.html
- https://www.cnblogs.com/jeemzz/p/11440923.html
- String
- list
- 双端链表
- ziplist
- set
- hash
- zset
- skiplist
- https://blog.csdn.net/yangbodong22011/article/details/78467583
- 数据结构存储
- https://blog.csdn.net/weixin_34942265/article/details/113581423
- redis 锁
- 简单的锁,基于setnx 带ex
- 有多个命令,如何保证原子性(事务、pipeline、lua脚本)
- redisson可重入、自旋、
- 公平锁
- redisson_lock_queue
- redisson_lock_timeout
- https://blog.csdn.net/Howinfun/article/details/121366238
- redlock
- 利用多个 Redis 集群,用多数的集群加锁成功,减少 Redis 某个集群出故障
- 缓存
- 缓存穿透
- 增加空缓存
- 雪崩
- 加锁建立缓存
- 击穿
- ttl随机化
- 用redis实现
- 排行榜
- 延迟队列
- 任务分发队列
- set和string存json,用起来有什么区别?
- hmset和直接set string 性能区别
- 判断一个值在不在集合里面 性能区别
- redis事务
- watch MULTI exec
- 乐观锁
- redis的内存用完时会怎么样
- 有哪些淘汰机制
- allkeys-lru
- volatile-lru
- 从已设置过期时间的挑选
- redis是如何清理的?
- 懒删除
- 扫描
- 如何提高内存使用效率
- redis可靠性
- rdb快照
- AOF日志
- 双云
- redis的部署模式有哪些?你们项目使用哪种?规模有多大?
- 集群模式
- 20主20从
- 双az通过kafka同步
- gossip协议
- meet、ping、pong、fail
- redis集群过大,会导致内部通讯的开销越来越多
- 16379端口
- 一致性hash
- 哈希环
- 虚拟节点
- slot分区
- crc16(key)%16384
- slot分配给节点
- reshard
- 如何扩容
- 如何数据同步
- 数据恢复
- 哨兵
- redis单节点的qps有多少
- redis为什么快?
- 数据存放在内存中,cpu内存寻址读取很快
- 使用dictht数据结构,指数级时间复杂度
- reactive模式,io多路复用
- 热点key问题
- 热key会导致redis集群汇总一个节点的延迟升高,进而导致调用方的连接池大量阻塞在这个缓慢节点的调用上,导致连接池耗尽。
- 副本key
- 内存缓存
- caffeine本地缓存
- 如何保证一致性
- redis集群模式,每个客户端都和每个redis节点建立连接吗
- 连接池配置了1000个连接,在Java程序启动的时候并不会创建1000个,在使用的时候根据需要来创建,最多只能创建1000个。
- 如果Redis集群如果很大,比如存在1000个master节点,那么连接池最大只能设置60个左右,因为操作系统最多只能维护65535个tcp端口,而java程序最大情况下需要向每个master节点都创建60个连接,在这种情况下创建的连接个数为60*1000=60000个连接,基本耗尽了可用的tcp端口。
- redis大key是什么
- 为什么不建议大key
- 内存不均衡,出现节点内存耗尽
- 网络io
- 单线程影响其他命令响应
- redis的key的存储
- 一个实例最多能放多少个key?(2^32个)
- https://zhuanlan.zhihu.com/p/43462366
- rehash的过程是什么样的
- https://blog.csdn.net/belalds/article/details/93713491
- redis最新版本使用了多线程
- https://www.cnblogs.com/mumage/p/12832766.html
### mysql
- 索引
- 物理结构:b+tree,page
- 索引的长度太长会有什么影响
- 索引的最左原则
- idx(a,b,c) : where a=xx and c = xx是否走索引?
- 索引覆盖
- 聚簇索引,回表
- 什么情况下建索引
- 数据离散,且离散程度好的放在前
- 字段变化次数相对于查询次数较少
- 索引失效
- mysql的单表大小、为什么要控制大小?
- 分表、分库的实现方法
- b+tree的存储计算
- 系统的块是4k,innodb的页是16k
- innodb指针6byte、主键bigInteger 8byte
- 假如行数据1kb
- 非叶子单page:16k/(8+6)=1170
- 如果树高3:1170*1170*16约等于2200万
- 叶子单page:16k/1k
- https://www.cnblogs.com/leefreeman/p/8315844.html
- 主键
- 自增Id
- 好处是生成快、省索引空间,坏处是只能单表、对外场景会暴露业务量。
- UUID
- uuid、guid,好处是全局唯一,坏处是占空间影响性能、无序、当然也无法范围查询。
- 雪花算法
- 好处是按时间有序、可以自己搞生成规则,在id里面嵌入分表号。缺点是时间如果往前拨,会引起主键冲突。
- 时区问题如何解决
- java服务器时间、msyq服务器时间、连接串时区如何匹配
- 夏令时
- acid
- atomic
- undo log 回滚
- consistent
- binlog
- 存储引擎的上层产生的,不管是什么存储引擎,对数据库进行了修改都会产生二进制日志
- Statement
- Row
- Mixedlevel
- isolation
- 三个问题
- 脏读
- A事务读到其他事务修改但未提交的数据
- 不可重复读
- A事务过程中对同一条数据两次相同查询结果不一样
- 幻读
- A事务过程中,两次范围查询的结果条数不一样
- 事务的四个隔离级别
- 读未提交
- 读已提交
- 可重复读
- 可能幻读
- 使用范围锁
- 串行化
- 写操作对另一个事务写操作:锁
- 写操作对另一个事务读操作:MVCC
- durable
- buffer pool中保存了page的缓存,优先读写缓存,异步刷新到磁盘中。
- 为了crash recovery,引入了redo log,先写log再写缓存。
- https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html
- innodb层产生
- 以块为单位进行存储,记录page的变化
- redo log恢复
- lsn:log sequence number
- check point: 触发脏页刷盘
- 数据页lsn小于redo log lsn
- 子主题 1
- https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html
- 事务
- 事务如何能保证RR
- mvcc
- 数据行隐藏列:中包含了事务Id, Undo Log指针, undo Log中有一条版本链
- 事务第一次读取前生成readView,获取事务系统trx_sys快照。读行数据的时候,先对比行数据事务id和快照
- trx_sys
- low_limit_id:下一个新事务Id
- 如果行数据事务Id比low更大,说明快照时,事务未开始,找undo log
- up_limit_id:活跃中的事务最小的id
- 如果行数据事务Id比up_limit_id更小,说明事务已经提交,对ReadView可见
- rw_trx_ids: 活跃中的事务Id列表
- 如果行数据事务Id不在列,说明已经提交,可见
- 如果存在,说明不可见,找undo log
- undolog
- 逻辑日志:记录反向操作
- 是采用段(segment)存储
- 每个undo操作在记录的时候占用一个undo log segment
- rollback segment称为回滚段,每个有1024个undo log segment。
- 存放在共享表空间
- 行锁、行间锁、间隙锁
- https://www.cnblogs.com/kismetv/p/10331633.html
- 分布式事务
- alter table加字段会锁表吗
- 5,6之后 更改字段类型会锁表
- https://www.shiqidu.com/d/940
### kafka
- 基本结构
- producer
- consumer
- broker
- 实际的kafka物理节点
- topic
- partition
- 一个topic可以有多个partition
- 一个broker可以放多个partition
- topic的每个partition在一个group中的消费者是唯一的,group+topic+partition保持一个offset
- 如果消费者比partition多,多的消费者会闲置
- 一个partiton就是一个独立的log文件,数据写入是顺序的,磁盘利用高,partition太多打开的文件就多,顺序性就可能变成随机写,影响磁盘吞吐
- 一个消费者可能消费多个partition
- partition副本(AR)
- Leader Follower
- 同步副本ISR
- LSO log start offset
- 高水位HW
- 已经同步的
- LEO:Log End Offset
- OSR
- out of sync replica
- partition key
- 相同的partition key会落到同一个partition,在同一个partition消费有序
- 什么情况会消息丢失、如何避免
- 避免发送失败
- 重试
- ack=0 不重试
- ack=1 只等待leader成功
- ack >1 等待leader和若干个isr写入成功
- ack=-1、all 等待全部ISR写入成功
- min.insync.replicas >1
- 至少要有x个isr,否则抛异常
- unclean.leader.election.enable=true
- 不允许OSR成为leader,防止丢HW到LEO之间的消息
- 异常处理,持久化发送失败消息,重试
- 避免消费失败
- 先消费再commit
- 可能导致重复消费
- 死信队列
- 什么情况会重复消费、如何避免
- 拉多条消息,消费时异常,导致未commit
- 在使用auto commit时尤其有可能
- 消费超时,超过max.poll.interval.ms、触发rebalance,另一个节点又拉消息重复消费
- 加快消费速度、减少消息拉取数量、增大超时时间、消费幂等去重机制
- zk在kafka中起什么作用(答案为老版本)
- /broker/ids 维护broker信息
- /broker/topics/ 维护所有topic消息,以及每个topic下的partitions信息。每个partition有个state节点,维持leader分区、ISR的brokerId等。
- /consumers/{group_id}/owners/{topic}/{broker-id.partition_id}消费者和partition的注册关系
- /consumers/{group_id}/offsets/{topic}/{broker-id.partition_id} 消费offset(老版本是这么做的,新版本存放在 Kafka 集群中的一个叫 __consumer_offsets 的topic中
- kafka的存储结构
- kafka性能好的原因
- 顺序append写入,按segment存储
- 零拷贝,不走用户态,直接在内核态传输
- 消息堆积如何处理
- 如何提升写入性能、消费性能
- rebalance
- when
- 消费者数量变化
- 消费者消费超时
- 分区数变化
- group订阅的topic变化
- how
- coordinator
- partition的leader所在的broker一般就是协调者
- 负责监控group中的consumer的心跳
- 超时:coordinater从组里面选举产生新的leader,leader发送syncGroup给coordinater,coordinator通过心跳下发syncgroup
- 数量变化:由leader consumer通知coordinator
- rebalance会标记一个generation给consumer,提交offset时,会比较。从而防止rebalance前提后重复提交的问题。
### zk
- 应用
- 锁
- zk作分布式锁和redis 做分布式锁的区别
- zk如何实现公平锁(临时顺序节点)
- https://www.nowcoder.com/discuss/205156?type=1
- 注册中心
- zk和eureka作为注册中心的区别
- Zk强调CP
- 当zk的节点失效时,会进行崩溃回复,进行leader选举,期间zk服务不可用
- eureka强调AP
- eureka的集群是平等的,某个节点的故障不会影响其他节点提供注册和查询服务。eureka如果发现85%的服务心跳丢失,就会进入保护模式。
- 消息通知
- 配置管理
- 配置信息保存在znode中,znode变化时通过watch通知各个客户端,从而更改配置
- 集群管理
- 监控集群机器状态,加入和剔除(断连的机器临时目录被删除,其他所有机器都收到通知
- kafka为什么依赖zk
- https://www.cnblogs.com/TM0831/p/13386184.html
- zk集群是什么结构:https://www.cnblogs.com/TM0831/p/13386184.html
- znode
- 既是数据文件也是目录,每个节点最大存1MB
- 原子性,读会读全部、写会替换节点
- 持久节点
- 临时节点
- 客户端断开就会删掉
- zab协议
- 消息广播
- 写操作都会路由到leader节点,转换为proposal,广播给follower。当超过半数follower反馈后,广播commit
- 崩溃恢复
- 初始化、leader节点故障、leader与超过半数节点断连时,会进行leader选举
- 节点的三种状态
- leading
- following
- election
- zxid
- 高32位叫做epoch,周期Id,每次有新的leader选举的时候,都会+1
- 低32位是自增计数器,每次事务请求都加1
- watch机制
- 客户端可以watch某个znode的变化,当znode变化时,会通知客户端这个事件(不会告知内容,只通知事件
- 父节点的创建修改删除会触发watch
- 子节点的创建删除会触发
- 1、客户端像zk注册watcher,并把监听
### es
- 如何保证和主数据仓比如mysql的一致性
## java特性
### jdk 8 stream、future等
### 数据结构
- arraylist
- 数组,随机访问性能好,插入删除较差,尤其是需要移动数据的时候
- new ArrayList(20) 不放数据的话,实际上此时内部数组还是空的,get会报越界。
- linkedList
- 双向链表,随机访问性能差,O(1)插入删除
- HashMap
- 初始数组长度16,负载因子0.75,大于16*0.75时会扩容,容量扩展一倍
- 如果能预估出大小,就在建立hashmap的时候指定大小,可以提高效率
- 存储结构
- 初始:数组+链表
- 链表较长时put:数组+红黑树
- resize和remove时,如果小于6,会退化为链表
- 对象的hashcode(定位数组)和equals(判重)方法
- 重写equals必须重写hashCode方法
- put过程
- hash: key.hashCode 高16位于低16位异或
- 原因是初始数组大小为16,只有低4位参与运算
- 通过高低位异或,扩大参与的位数,增强散列度
- treefy:链表binCount 大于TREEFY_THRESHOLD(8)时
- 如果数组长度小于64,只resize
- 否则treefy这个链表,转为红黑树
- resize: map.size 大于数组长度*负载因子
- 数组的每个槽位会拆分为low、hi
- 并发问题
- hashmap在1.7的时候,并发put,扩容rehash中会对链表倒序,可能出现环形链表,导致死循环,1.8不会有这个问题
- 1.8 put ++size非原子,可能导致丢失数据
- 抛出ConcurrentModificationException
- concurrentHashMap
- 1.7使用分段锁:划分16个segment,嵌套了一层,所以极限是16个并发
- 1.8使用synchronized,按槽位的粒度控制
- 如果槽位为空,使用cas
- priorityQueue
- TreeSet
- 布隆过滤器
- bitmap
### 常量
- String、StringBuilder、StringBuffer
- String是不可变类型,每次都会创建新的String对象,并且会在常量池缓存
- StringBuffer是线程安全的
- String ==,equas,new String("123"),"123"
- hashcode重载
- integer -128到127
- 对象放int和integer的区别
### jvm
- jvm参数
- flume
- tomcat
- -XX:MaxMetaspaceSize=512m -XX:MetaspaceSize=256M -XX:+UseG1GC -XX:+PrintGCDateStamps -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -Xms3967M -Xmx3967M -XX:OnOutOfMemoryError=xxx
- JVM内存模型
- jvm stacks
- native method stacks
- metaspace
- 常量池
- 1.8的String在这里
- 方法元信息
- heap
- young
- eden
- minor gc
- eden+from -> to
- 经过若干次回收依然存活的,会进入老年代
- 假如垃圾产生的速度过快或者大对象,会直接进入老年代
- survivor
- from
- to
- young区的三个区域的内存一般是8:1:1
- young区一般占堆区的三分之一
- old
- tenured
- full gc
- 一个请求中对象的产生到消亡的过程,在各个jvm内存区域的流动过程
- qps高时,对象的生命周期会有什么变化?为什么会出现fgc?
- java内存模型jmm
- 主内存和工作内存,线程对变量的操作在工作内存进行,工作内存存放该线程读写变量的副本
- 可见性
- 重排序
- happens-before
- volatile:保证可见性,禁止重排序
- jvm运维命令
- jstat gcutil pid
- 查看gc统计、内存使用
- jps
- 查看java进程
- jmap
- 定位oom、major gc等问题
- jstack
- 定位死锁、线程池占用等问题
- OOM问题如何定位
- jmap -dump:format=b,file=/usr/local/base/02.hprof 12942
- 自动oom dump:-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/base
- 使用mat分析dump文件
- 一般业务造成的oom,看huawei开头的类,哪个实例比较多
- 找到path to gc roots
- gc roots
- 方法区的静态属性引用、常量引用
- 栈中引用对象
- 本地方法栈中JNI引用对象
- 常用的gc算法
- g1
- https://www.cnblogs.com/lsgxeva/p/10231201.html
- region
- 基于region:将堆内存分为比如2048个相同大小的区,大小可以在1M到32M,分区时不区分young和old
- 大对象
- TLAB(Thread Local Allocation Buffer)线程本地分配缓冲区
- 子Eden区中分配
- Humongous 区:如果一个h区存不下,就要找连续的h区,为了找到连续的h区,就可能触发full gc
- 跟踪各个Region 的垃圾堆积价值,即垃圾对象占比越多的Region回收的收益越大,优先回收价值最大的region
- 清理时,将对象从一个区域复制到另一个区域,同时完成了压缩,不会有碎片内存
- 停顿时间模型
- 设定目标停顿时间
- -XX:+UseG1GC -Xmx32g -XX:MaxGCPauseMillis=200
- 根据前几次回收Region所用时间来估算要回收哪些Region,用最小的时间获取最大收益
- rememberSet
- 使用cardTable作为RS
- 每个Region都有自己的cardTable,记录来自其他Region的引用
- gc过程
- youngc
- 过程
- 根扫描
- 静态和本地对象被扫描
- 更新remembered set
- RS
- 作用是跟踪指向某个heap区内的对象引用
- 处理RS
- 对象拷贝
- eden+survivor -> survivor
- survivor->survivor
- survivor->old
- 处理弱引用、虚引用
- mix gc
- 过程
- 全局并发标记
- 初始标记(stw
- 对gc根标记
- 根区域扫描
- 初始标记的存活区扫描对老年代的引用,并标记被引用的对象
- 并发标记:三色标记算法
- 黑色:根对象,或者该对象与它的子对象都被扫描
- 灰色:对象本身被扫描,但还没扫描完该对象中的子对象
- 白色:未被扫描对象,扫描完成所有对象之后,最终为白色的为不可达对象,即垃圾对象
- 最终标记
- 清除垃圾
- 拷贝存活对象
- cms
- 单线程,且会产生内存碎片
- 适用于老年代:标记清除法
- 如果取舍
- g1的内存占用和cpu比cms高
- g1如果目标时间过小,可能导致垃圾堆积
- g1适合内存大的场景,至少4g
- g1内存没有碎片,停顿时间可控
### 对象
- 对象头
- mark word
- 类指针
- 数组对象长度
- 实例数据
- 对齐填充字节
- 补齐到8bit的倍数
### class类文件
- magic
- 文件类型
- version
- jdk版本
- constant pool
- ..Fields
- Methods
- https://www.jianshu.com/p/4d89f803396a
### juc
- 线程池
- 线程的创建和销毁开销很大
- 线程栈是需要分配空间的
- -Xss128K
- 内存-堆区)\栈大小,再多会OOM
- https://blog.csdn.net/z69183787/article/details/100098725
- 操作系统限制
- /proc/sys/kernel/pid_max
/proc/sys/kernel/thread-max
max_user_process(ulimit -u)
- restTemplate底层是httpclient
- io时阻塞,让出cpu、不让出线程
- io多路复用+aio/nio
- 把io密集型转化成cpu密集型
- 这就是为何select、poll 、epoll、nginx可以用很少的线程可以获得极大吞吐量的原因
- 参数
- PoolSize
- core
- max
- keepAliveTime
- workQueue
- threadFactory
- handler
- 核心线程 corePoolSize 、任务队列 workQueue (满了才会扩大)、最大线程 maximumPoolSize ,如果三者都满了,使用 handler处理被拒绝的任务。
当线程池中的线程数量大于 corePoolSize 时,如果某线程空闲时间超过 keepAliveTime ,线程将被终止。
- 计算密集型,最好不超过N*CPU IO密集型可以到2N*CPU
- 实际上tomcat的线程池默认都是200了
- 异常处理
- 在runnable逻辑中使用try catch
- 设置uncaughtExceptionHandler
- 只适用于excute,submit不能捕获
- io
- aio bio nio
- wait、sleep
- wait只能在synchronized中使用,
- sleep、join、interrupt
- https://blog.csdn.net/weixin_43190941/article/details/82914348
- interrupt只会改变标记,需要被中断线程调用isInterrupted()进行检查
- 如果处于阻塞(sleep),会抛出InterruptedException
- synchronized
- 加锁对象
- 修饰静态方式时,加锁的是类
- 修饰非静态方法时,加锁的是this对象
- 修饰对象时,加锁的是对象
- 可重入,不可中断、非公平
- 对象头Mark Word,
- 锁标记位:最后两个bit
- 偏向锁:
- 当一个线程竞争时
- 记录获得偏向的线程Id
- 轻量锁
- 当两个线程竞争时,其他线程会通过自旋方式获取锁,轻量锁是不阻塞的,通过自旋+Cas获取锁
- 线程栈帧中建立lock record:存储锁对象的Mark Word的拷贝
- CAS尝试将mark word的指针指向lock record,成功即获取锁。或者mark word的指向就是当前线程的锁记录,说明是重入。
- 重量锁
- 当等待轻量锁的线程自旋超过一定次数,或者有第三线程竞争时,会升级为重量锁
- 指向Monitor对象
- owner
- 获得monitor对象的线程或者锁
- WaitSet
- 调用wait方法后处于wait状态的线程,被加入到这个linkedList
- EntryList
- 等待锁block状态的线程的队列
- recursions
- 重入次数
- monitor基于操作系统的mutex lock实现,需要从用户态切换到和形态,因此效率不高
- 上锁
- owner字段CAS操作,如果owner为空,赋值指向自己,获取锁后recursions++
- 释放锁
- monitorexit,recursions--,如果计数为 0,则 monitor owner置空
- wait只能在synchronized中使用,调用加锁对象的notify方法可以唤醒
- synchronized的锁升级
- https://my.oschina.net/u/4591203/blog/4410947
- 无锁->偏向->轻量锁->自旋->重量锁
- 子主题 3
- aqs
- reentrantLock
- 可重入、可中断、公平
- condition
- lock = new ReentrantLock();
condition1 = lock.newCondition();
- condition.await()
- condition.signal()
- Semaphore
- new Semaphore(10)
- acquire
- release
- 非公平
- countDownLatch
- 异步请求
- completableFuture
### spring
- aop
- Spring AOP
- PointCut
- 注解
- 定义
- @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Inherited
public @interface Cache
- @Aspect
@Pointcut("@annotation(xx.Cache)")
public void cachePointCut() {
}
- 类方法
- @Pointcut("execution(* com.abc.service.*.many*(..))"
- Advice
- @Aspect
@Around("cachePointCut()")
public Object handleCache(ProceedingJoinPoint joinPoint) {
- Spring Aop都是使用动态代理实现的,使用了aspectJ的注解
- 同时配置多个切面 @Order(1) 越小优先级越高
- Spring AOP的介绍https://zhuanlan.zhihu.com/p/29483023
- 动态代理
- 类
- JDK动态代理
- 如果实现了接口,默认使用
- 切面实现 InvocationHandler
- Proxy.newProxyInstance(loader, interfaces, this)创建代理对象
- cglib
- 切面实现MethodInterceptor
- enhancer.create创建代理对象
- 底层通过asm实现,具备缓存,效率较好
- JDK动态代理和CGLIB动态代理的示例www.freesion.com/article/7543522446/
- 静态代理
- aspectJ:基于预编译
- 编写XXAspect.aj
- 使用ajc编译织入
- ajc -d . xxx.java xxx.aj
- 优势:性能更好
- 使用AspectJ的示例https://www.jianshu.com/p/aa293edba2e4
- ioc
- 通过反射,维护一个bean容器上下文
- 事务注解
### 动态代理
- 接口:动态代理
- 类:cglib,运行时生成子类
### threadlocal
- 使用场景
- 隐藏传递一些header内容
- 传递traceId、事务Id
- 语法
- ThreadLocal<Context> threadLocal = new ThreadLocal<>();
- threadLocal.set(context)
- threadlocal.get()
- new 两个threadLocal对象,即使在同一个线程,作为key也是不同的
- 问题
- 内存泄漏
- threadlocal继承weakReference,当Threadlocal对象作为Entry的key被gc,对象会内存泄漏
- 引用链:Thread -> ThreaLocalMap -> Entry[]
- Entry extends WeakReference<ThreadLocal<?>>
- Entry(ThreadLocal<?> k, Object v)
- key:ThreadLocal<?> k为弱引用,会被gc回收,但是value还被强引用。
- 如果线程一直存在,比如线程池,那么value的强引用一直存在,内存泄漏
- 解决方法
- 主动调用ThreadLocal.remove
- 将ThreadLocal变量定义成static,一直存在ThreadLocal的强引用
- 强引用 gc roots
- 父子线程
- 子线程
### applicationContext
### springMVC 请求路由过程
### 自定义类加载器
- 热部署:继承ClassLoader,实现loadClass方法,比如获取文件流,调用defineClass创建类。
- 如果自定义的找不到,会尝试父类
- nuwa 依赖隔离
- 每个中间件的classloader各自加载自己的版本的同一个类路径的类
- 代码保护:加密
## shell定位问题常用命令
### 如何在日志中找关键字、以及其上下行内容
### 如何用命令统计请求日志中某个接口的qps?如何对接口请求量排序
### 查看cpu信息
- lscpu
- 8C16t 3.0ghz
- cat /proc/meminfo
- 16GB
## 计算机、网络常识
### 线程池的概念、主要参数
### 网络模型(http和tcp和ip分别在那一层
## 微服务
### 限流、熔断、降级区别
- 熔断:快速失败,避免故障扩散。比如抢购时返回系统繁忙请重试,一般用超时请求计数判断。
- 降级:在系统过载时提供有损服务,从而降低负载。比如评论的es查询降级到mysql,可以手动配置,也可以根据tps\响应时间和失败率降级
- 限流:在系统过载时主动丢弃部分业务请求。被调方判断,比如appinfo单服务调用qps有限制
- 限流的表现形式可以是拒绝服务、或者降级
- 隔离
### hystrix
- HystrixCommand
- setter
- GroupKey
- CommandKey
- ThreadPoolKey
- 超时、错误
- ExecutionTimeoutInMilliseconds
- CircuitBreakerErrorThresholdPercentage
- CircuitBreakerRequestVolumeThreshold
- 线程池配置
- HystrixObservableCommand
- 隔离
- 线程池
- 信号量
### rpc比http好在哪里
## 设计
### 秒杀系统
- 缓存:页面静态化预热到cdn、数据预热到缓存缓存、最好用本地缓存、排查redis单key问题
- 库存:写入redis,通过原子操作对库存进行减少。库存在每台服务器本地做计数,如果超过总量的一定比例直接失败。
- 客户端:校验系统时间、校验登录、点击频率限制
- 服务端:秒杀开始前的请求直接失败,量大的ip、账号可计入黑名单。
- 秒杀时间到,请求涌入,负载均衡到各个服务器;写入消息队列削峰,接口快速返回提示:正在抢购中,客户端保持在这个界面,等一会儿再查结果
- 接口端幂等设计;url动态化,加入随机数让人无法提前猜到;加入token,一次消耗,防刷
- 服务器连接数放开
### 海量数据排序
- 评论墙、热帖榜
- 定时任务:切片,各自排一小部分,取前面的写入redis zset汇聚,做二次排序
### 推荐
- 召回:根据特征向量取数据
- 用户模型
- 粗排、精排、调整
## 算法
### 二分查找
- 递归
- 循环
### DFS、BFS
### 动态规划
- 0-1背包问题
### tsp问题
### 最短路径
- 弗洛伊德算法
- 三层循环(点、邻接矩阵)
- 多源点
- 地杰斯特拉
- 单原点
- 每次找距离最近的没访问过点,松弛各条边
### 字典树
### 字符串转double(soul
### leetcode
- 回文(PDD
- 42.trapping train water
- 31.next permutation
- 最长上升子序列
- 最大连续和
## rx java
### https://juejin.cn/post/6844903447280484360
### https://blog.csdn.net/qq_26787115/article/details/65629072