知识架构
2023-03-17 18:36:45 61 举报
AI智能生成
java知识图谱及常见问题分析
作者其他创作
大纲/内容
集合
Collection
List
Stack
线程安全
Vector
线程安全
ArrayList
随机访问效率高
增加和删除效率低(非首尾操作)
内存占用相对于 LinkedList要小得多
线程不安全
LinkedList
双向循环链表
随机访问效率低(要移动指针)
增加和删除效率高
现对于 ArrayList更占内存
线程不安全
Set
HashSet
TreeSet
有序
唯一
LinkedHashSet
Queue
BlockingQueue
Deque
Map
Hashtable
put
get
HashMap
jdk1.7
数组
链表
jdk1.8
数组
链表
红黑树
数组大小为2的幂次方
hash % leght = hash & (leght - 1)
为什么不使用hashcode()值作为table下标
hashcode()值是int整型,范围为-(2 ^ 31)~(2 ^ 31 - 1)
HashMap容量范围为16 ~ 2 ^ 30
通常取不到最大值
设备提供不了那么大的存储空间
ConcurrentHashMap
SortedMap
NavigableMap
TreeMap
key有序
key不允许为null
value可以为null
只保存节点,占用空间小
HashMap要保存数组,占用空间大
HashMap要保存数组,占用空间大
非线程安全
SynchronizedMap
所有方法均使用synchronized修饰
JUC
线程池 ThreadPoolExecutor
核心线程数
计算密集型
cpu利用率100%,设为N+1
主要利用cpu计算
为什么要+1?
IO密集型
cpu利用率不高,设为2N
主要是IO操作耗时
等待时间与计算时间的比率W/C趋近于1
最大线程数
线程数过多会造成什么异常
线程生命周期开销非常高
消耗过多的CPU
降低稳定性JVM
空闲等待时间
空闲等待时间单位
阻塞队列
ArrayBlockingQueue
由数组结构组成的有界阻塞队列
size值为int类型,最大只能放 Integer.MAX_VALUE
LinkedBlockingQueue
由链表结构组成的有界阻塞队列
默认 Integer.MAX_VALUE
PriorityBlockingQueue
支持优先级排序的无界阻塞队列
初始容量默认为 11,可扩容,无上限
DelayQueue
使用优先级队列实现的无界阻塞队列
SynchronousQueue
不存储元素的阻塞队列
公平
new TransferQueue<E>()
非公平
new TransferStack<E>()
LinkedTransferQueue
由链表结构组成的无界阻塞队列
LinkedBlockingDeque
由链表结构组成的双向阻塞队列
默认 Integer.MAX_VALUE
ThreadFactory
拒绝策略
DiscardPolicy
啥都不做,放弃任务
AbortPolicy
抛 RejectedExecutionException 异常
CallerRunsPolicy
当前线程下再次执行当前任务
DiscardOldestPolicy
从队列里拿一个新的线程执行当前任务
最终线程池保持核心线程数的线程
一个线程运行时发生异常会怎样?
ReentrantLock
Sync
AbstractQueuedSynchronizer(AQS)
FairSync
当前线程获取锁
若有其他线程持有锁
则进入阻塞队列顺序执行
若有其他线程持有锁
则进入阻塞队列顺序执行
NonfairSync
当前线程执行任务
若有其他线程持有锁
则会尝试竞争到锁
没竞争到则进入阻塞队列
若有其他线程持有锁
则会尝试竞争到锁
没竞争到则进入阻塞队列
Semaphore
限制某段代码块的并发数
允许多个线程同时访问
CountDownLatch
不能复用
强调一个线程等多个线程完成某件事情
调用countDown方法后,当前线程不会阻塞
CyclicBarrier
可以复用
调用await方法后,会阻塞当前线程
多个线程互相等待,大家都完成,再携手共进
ConcurrentHashMap
jdk1.7
Segment
HashEntry
ReentrantLock
jdk1.8
Node
CAS
synchronized
不支持key或value为null
Sentinel 中间件
作用
限流
降级
原理
消息中间件
MQ
为什么使用MQ
优点
异步处理
应用解耦
流量削峰
日志处理
消息通讯
缺点
系统可用性降低
系统复杂度提高
一致性问题
幂等性
消费端解决
消息顺序问题
分布式事务问题
消息可靠性传输
生产者丢失消息
消息列表丢失消息
消费者丢失消息
RocketMQ
消息有序性
全局有序
局部有序
消息持久化原理
节点间数据同步原理
消息类型
普通消息
延迟消息
顺序消息
广播消息
死信队列消息
ActiveMQ
RabbitMQ
开源的,Erlang编写
基于AMQP协议
工作模式
simple模式
simple
work工作模式(资源的竞争)
work
publish/subscribe发布订阅(共享资源)
发布订阅模式
routing路由模式
路由模式
topic 主题模式(路由模式的一种)
topic
消息确认机制
发送方确认
接收方确认
自动确认模式
手动确认模式
消息可靠性传输
生产者丢失消息
transaction模式
吞吐量下降
confirm模式(用的居多)
消息列表丢失消息
消息持久化
持久化可以和confirm模式配合着使用
消费者丢失消息
自动确认消息模式
手动回复确认消息(解决方案)
Kafka
数据库
关系型数据库
MySQL
存储引擎
InnoDB
聚簇索引
支持事务
MyISAM
非聚簇索引
不支持事务
三大范式
1NF.对属性的原子性,要求属性具有原子性,不可再分解
2NF.确保表中的每列都和主键相关
3NF.确保表中的每列都和主键直接相关,而非间接相关
索引
BTree
B+Tree
Hash
索引设计
索引失效
like %a 或者 like %a%
a = 1 or b = 2至少一个字段未加索引
联合索引,没有使用最左侧字段条件
类型隐式转换,如varchar类型的条件没有加单引号
索引字段进行了函数运算
sql内部优化,认为全表扫描比走索引效率更快
什么情况下不建议创建索引
字段区分度低,如性别:男、女
频繁更新的字段
不用于查询的字段
表数据量较小,没有必要添加索引
锁机制
表级锁
表共享锁(Table Read Lock)
表独占写锁(Table Write Lock)
行级锁
页级锁
MVCC多版本并发控制
一个undo日志版本控制链
事务首次查询时会生成readview(一致性视图)
可重复读隔离级别下,生成的readview不会发生变化
读已提交隔离级别下,首次查询时会生成最新的readview
undo log
结合 Read View 实现 MVCC
redo log
InnoDB存储引擎独有
让MySQL有了崩溃恢复能力
磁盘顺序写
innodb_flush_log_at_trx_commit 参数控制刷盘策略
0
每次事务提交时不进行刷盘
1
每次事务提交时都进行刷盘(默认)
2
每次事务提交时
都只把 redo log buffer 内容写入 page cache
都只把 redo log buffer 内容写入 page cache
后台线程刷盘
redo log buffer 占用空间即将达到 innodb_log_buffer_size 一半的时候,后台线程会主动刷盘
binlog
statement
row
mixed
磁盘顺序写
事务
ACID特性
原子性
一致性
隔离性
持久性
隔离级别
读未提交
读已提交
可重复读
为什么默认的是RR,而不是RC
RR隔离级别下,存在间隙锁,死锁几率比RC大得多
RC隔离级别不存在间隙锁
查询条件未命中索引,会进行全表扫描,并锁表
RC隔离级别下,Mysql进行了优化,过滤条件不满足,会调用unlock_row方法,并释放对应记录的锁
RC隔离级别下,半一致性读(semi-consistent)特性增加了update操作的并发性
为什么其他数据库默认选择RC
RC隔离级别下,主从复制使用什么binlog格式
序列化
数据库连接池
Durid
主从架构
子主题
子主题
读写分离
Aop实现
三方工具实现
分库分表
垂直拆分
单库垂直分表
单表字段数建议20-50个
拆分字段到不同表
主表
明细表
多库垂直分表
把一个库中的多个表,按照一定维度分到多个库
订单库
商品库
用户库
......
实现业务数据隔离
水平拆分
非关系型数据库
Redis
数据类型
字符串string
哈希hash
列表list
集合set
有序集合zset
持久化方式
RDB
优点
快照式持久化方式
快照文件持久化完成之后才会替换上一份快照,文件完整可用
redis单独创建(fork)一个子进程进行持久化操作,不影响主进程,从而确保redis性能
数据恢复对于完整性不敏感时,RDB比AOF高效
缺点
持久化过程中服务宕机,会导致数据丢失
AOF
优点
只允许追加不允许改写
可以把执行过的写命令记录下来,在进行数据恢复时,重新执行一下该文件即可
默认每秒持久化一次,数据即使丢失,也只是丢了这1s的数据
追加日志时磁盘满了,可以通过redis-check-aof工具进行日志修复
日志文件达到阈值,会进行AOF文件重写,并进行内容压缩,只保留可以恢复数据的最小指令集
AOF重写是先写入临时文件,写完之后再覆盖的流程,安全可靠
缺点
同样数据规模下,AOF比RDB文件大
恢复速度比RDB慢
RDB + AOF
数据持久化时都会执行
数据恢复时以AOF为准
常见问题
缓存穿透
布隆过滤器
给null值做短期缓存
接口层校验,拦截非法请求
缓存击穿
并发不高的情况下,加互斥锁
热点数据永不过期
缓存雪崩
失效时间追加随机值
热点数据永不过期
并发不高时,加互斥锁
DB个数默认为16个,从0~15,如果设置了其他值会报错
大key
危害
客户端超时等待
引发网络阻塞
阻塞工作线程
内存分布不均匀
使用 SCAN 查找大key
删除大key
del命令
SCAN 查找,分批删除
unlink命令(异步删除)
怎样避免
压缩value
大key拆分
大key清理
监控Redis内存、网络带宽、超时等指标
过期策略
定期删除
惰性删除
定期惰性删除
内存淘汰策略
noeviction
当内存使用超过配置的时候会返回错误,不会驱逐任何键
volatile-random
从过期的键集合中随即删除
volatile-lru
写入数据时,内存不足,从设置了过期时间的键集合中取最近最少使用的删除
volatile-ttl
从设置了过期时间的键集合中取将要过期的删除
allkeys-lru
写入数据时,内存不足,根据LRU算法取最近最少使用的删除
allkeys-random
随机删除
为什么redis这么快?
完全基于内存操作
数据结构简单,且是经过单独设计的结构
单线程
没有锁竞争和线程上下文切换
不会出现死锁场景
多路复用IO
套接字描述符(简称 FD)
多路复用程序
文件事件处理器
内部有自己的VM机制,调用系统函数时不需要进行移动和请求
Redis 集群
主从复制
优点
支持主从复制、读写分离
主机采用非阻塞形式给从机提供服务
从机同样采用非阻塞形式完成数据同步
缺点
不具备自动容错和恢复功能
数据不一致问题
较难支持在线扩容
哨兵模式
哨兵作用
监控主服务器和从服务器是否正常运行
主服务器出现故障时,自动将从服务器升级为主服务器
优点
所有主从模式的优点都具备
主从可以自动切换,系统更健壮,可用性更高
缺点
较难支持在线扩容
集群容量达到上限时,扩容会变得非常复杂
集群模式
特点
所有redis节点彼此互联(PING + PONG机制)
节点的fail是通过集群中超过半数节点的检测失效时才生效的
客户端与redis节点直连,不需要中间代理层
优点
无中心化架构
数据按照slot存储分布在多个节点,节点间数据共享,可动态调整数据分布
可扩展性
高可用
降低运维成本,提高系统的扩展性和可用性
缺点
节点会因为某些原因发生阻塞
数据通过异步复制,不能保证数据一致性
资源隔离性差
不支持多数据库空间,只能用 db0
key批量操作限制
MongoDB
JVM
内存模型
堆
是否逃逸
是
在堆中分配
否
会尝试在栈中分配
栈(虚拟机栈)
栈帧
局部变量表
操作数栈
动态链接
方法出口
本地方法栈
方法区(元数据)
程序计数器
内存分配
是否逃逸分析
是
栈
否
堆
分配方法
指针碰撞(Bump the Pointer)默认
Java堆中内存规整
以指针作为分界点指示器,分配内存时,指针直接移动对应距离
以指针作为分界点指示器,分配内存时,指针直接移动对应距离
空闲列表(Free List)
Java堆中内存不规整
JVM维护一个列表,记录可用内存,分配时更新列表
JVM维护一个列表,记录可用内存,分配时更新列表
并发问题解决方案
CAS(Compare And Swap)
CAS配上失败重试机制保证更新操作的原子性
从而对分配内存空间的动作做同步处理
从而对分配内存空间的动作做同步处理
TLAB(Thread Local Allocation Buffer)
本地线程缓冲,把内存分配的动作按照线程划分放在不同空间中进行
即每个线程在Java堆中预先分配一小块内存
即每个线程在Java堆中预先分配一小块内存
垃圾收集器
新生代收集器
Serial
单线程收集,有较大的STW时间,可以和CMS搭配使用
Parallel Scavenge
并行收集,注重吞吐量,而CMS、ParNew更多的关注用户线程停顿时间
ParNew
Serial的多线程版本,可以和CMS搭配使用
老年代收集器
Serial Old
Serial的老年代版本,JDK1.5以前和Parallel Scavenge搭配使用,
也可以在CMS发生promotion failed问题时,作为备选方案
也可以在CMS发生promotion failed问题时,作为备选方案
Parallel Old
Parallel Scavenge老年代版本,JDK8默认新生代和老年代收集器
CMS
初始标记
STW,记录GC Roots能直接引用的所有对象
并发标记
用户线程和标记线程并行运行
重新标记
重新标记并行过程中产生变化的引用关系
三色标记
黑色
对象被垃圾收集器访问过,且所有引用均扫描过
灰色
对象被垃圾收集器访问过,且部分引用被扫描过
白色
对象未被垃圾收集器访问过
分析结束仍是白色,则表示不可达,需要回收
并发清理
用户线程和清理线程并行,如果产生新的对象,则直接标记为黑色
并发重置
重置本次回收过程中的标记数据
整堆收集器G1
分区回收
整个内存区域划分成一个个小的内存区域
每个内存区域没有固定为某个年龄代
本次回收时是年轻代
下次回收时可能就是老年代了
性能调优
XMX和XMS等设置一样大,减轻伸缩堆大小带来的压力
promotion failed问题
对象内存空间分配
大对象直接进入老年代
Serial
ParNew
分代年龄机制
长期存活的对象进入老年代
默认最大年龄为15
每次Minor GC后存活的对象,年龄+1
动态年龄判断机制
Survivor 幸存区
一批对象总大小大于当前 Survivor区的50%
大于等于这批对象最大年龄的对象直接进入老年代
老年代空间分配担保机制
每次 Minor GC 之前会计算老年代剩余可用空间
老年代剩余可用空间小于年轻代现有所有对象大小之和(包含垃圾对象)
若大于,则直接进行 Minor GC
JVM参数 -XX:-HandlerPromotionFailure
设置了
老年代可用空间与
之前每次 Minor GC 进入老年代的对象的平均大小
进行比较
之前每次 Minor GC 进入老年代的对象的平均大小
进行比较
大于
正常进行 Minor GC
小于
触发 Full GC
未设置
触发 Full GC
JVM内存溢出OOM
堆
当堆内存不足,并且已经达到 JVM 设置的最大值
当一次从数据库查询大量数据,堆内存没有足够的内存存放
大量的强引用对象在堆内存中存活,GC 无法回收
虚拟机栈
StackOverflowError
线程请求的栈深度大于虚拟机允许的最大深度
检查代码是否出现深度递归的情况
递归的终止条件没有设置
OutOfMemoryError
虚拟机在扩展栈时无法申请到足够的内存空间
线程的栈内存空间太小
通过 -Xss 设置每个线程的栈内存空间
本地方法栈
同虚拟机栈
元空间(方法区)
加载的类过多导致方法区没有足够的内存
程序中使用大量的 cglib 或者动态代理等对目标类进行代理
系统代码过多(说白了还是类信息、常量等过多)
程序计数器
唯一一个没有OOM场景的区域
ThreadLocal
set赋值
获取当前线程t
ThreadLocalMap map = getMap(t)
实际是获取Thread的某个属性
map.set(this, value)
get获取值
获取当前线程t
ThreadLocalMap map = getMap(t)
ThreadLocalMap.Entry e = map.getEntry(this)
e.value即为结果值
静态内部类
ThreadLocalMap
Entry数组
threshold = len * 2 / 3
len默认为16
resize时,扩大2倍,oldLen * 2
当Entry数组大小 size > threshold - threshold / 4时,进行resize
子类
InheritableThreadLocal
子线程获取主线程信息
增强(阿里中间件)
TransmittableThreadLocal
内存泄漏
原因
key是弱引用(GC 时会被回收)
value可能是长生命周期的对象,属于强引用
GC时,key被回收,value仍有引用,无法回收
解决方案
每次用完ThreadLocal后,主动进行 remove
IO 相关
0 条评论
下一页