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