集合
List有序,可重复
ArrayList动态数组
初始10,扩容1.5倍
add/romove O(n) 其他0(1)
LinkedList,实现Deque的双向链表
add/romove O(1) 其他0(n)
Node数据和前,后,头,尾指针
Map键值对
HashMap,hash数组加单链表或红黑树
数组长度最近2的整数幂,默认16,装载因子0.75,扩容阈值12
最高位1低位全0,无符号右移加或运算加1
hash值添加扰动,高16位和低16位异或,高效映射数组
数组大于 64且单链表大于8转红黑树,小于 6还原
添加方法,计算Key的hash值,putVal添加或更新
数组为空扩容
(n - 1) & hash,hash值转数组下标
数组无冲突,插入
有冲突,链表尾部添加,超过8转红黑树
大于阈值扩容
扩容方法,计算阈值和新容量
创建新数组
将旧数组上Entry再散列到新数组
LinkedHashMap,继承HashMap,双向链表,保留插入顺序
实现LRU,添加数据时回调removeEldestEntry,超过最大值返回True
Set无序,不可重复
HashSet,由HashMap实现
LinkedHashSet,由LinkedHashMap实现,有序
Iterator 迭代器,遍历集合
hasNex是否存在下一个
next下一个
remove删除
IO
经典 IO
字节流 ,操作字节
- Input和OutputStream读写抽象父类
- FileInput和FileOutputStream读写文件
- BufferedInput和BufferedOutputStream读写缓冲文件
- DataInput和DataOutputStream读写基本类型
- ObjectInput和 ObjectOutputStream读写可序列化对象
字符流 ,操作有编码的字符
- Reader和WriterStream读写抽象父类
- FileReader和FileWriter读写文件
- BufferedReade和BufferedWriter读写缓冲件
- InputStreamReader和 OutputStreamWrite字节转字符流
随机访问流,RandomAccessFile既是输入也是输出流,实现断点续传
OKIO
简化经典IO,支持超时检测
Source/Sink输入输出流
BufferedSource/BufferedSink输入输出缓存流
Segment数据片段
Buffer缓存区,包含Segment链表
Timeout超时控制,如OkHttp网络请求超时监控
扩展方法
XXStream.source()/sink()转OKIO
Source/Sink.buffer()转缓存流
BufferedSource/BufferedSink.inputStream/outputStream转经典IO
NIO*
非阻塞IO,IO调用和执行是异步的,Linux通过epoll实现
JDK1.7新增
- AsynchronousSocketChannel
- AsynchronousServerSocketChannel
- AsynchronousFileChannel
- AsynchronousDatagramChannel
多线程
安全方案
非阻塞: CAS, 原子类AtomicXXXX
阻塞同步:Snchronized 和 ReentrantLock
不用同步: Thread Local
CAS
硬件实现的对比交换原子指令,先比较两个值是否相等再更新值
ABA问题,每次更新,版本号加1
循环时间长,pause指令提升效率
只用于一个共享变量
原子类
UnSafe实现,可操作内存和实现CAS,外部慎重
Atomic包下基本和基本类型数组,引用和引用数组,字段等类
S ychronied
阻塞保证
- 原子性,线程调度器不能中断
- 可见性,修改共享变量所有线程可见
- 有序性,禁止编译器指令重排
1.6前锁是字节码指令
monitor对象监视器,和每个对象关联
monitorenter获取,被占用阻塞
monitorexit释放
1.6后锁是对象头状态Mark Word
偏向锁
查看锁线程ID,是否指向当前线程
是执行,不是CAS尝试获得锁
失败,挂起线程升级为轻量级锁
轻量级锁
进入时建立锁记录,CAS尝试更新锁记录指针,成功得到锁
失败,检查Mark Word是否指向锁记录,是得到锁
不是,自旋等待,一定次数后升级为重量级锁
重量级锁,阻塞同1.6前
volatile
变量前关键字,不阻塞保证
可见性,从主内存读写,跳过CPU缓存
有序性,指令前插入内存屏障,禁止重排
AQS
构建锁和同步器的框架,共享资源
空闲,请求线程为工作线程,锁定
被占用,线程阻塞及唤醒机制
同步状态int,线程队列,CAS原子操作状态
同步队列
每个线程封装成Node
每个结点都由前一个结点唤醒
当结点前驱是head获取锁成功
继承AQS重写
tryAcquire(int)/tryRelease(int)独占方式,尝试获取释放资源
tryAcquireShared(int)/tryReleaseShared(int)共享方式,尝试获取释放资源
ReentrantLock
实现Lock接口,内部类Sync继承AQS
NonfairSync继承Sync,默认
非公平,每次都尝试获取锁
FairSync继承Sync
公平,先判断队列是否有更长等待线程,有加入尾部
初始化state = 0,未锁定
A线程lock()时独占锁,state+1
A线程重复获取,state累加,可重入
其他线程失败,直到A线程unlock到state=0
线程池
Executors工具类创建
Single、Fixed、Cache不同入参的 ThreadPoolExecutor
核心线程数,最大线程数
非核心线程存活时间,时间单位,线程工厂
任务队列
LinkedBlockingDeque双向并发队列,用于CPU线程池
SynchronousQueue不存储任务,直接交给新线程,用于 IO 线程池
任务队列满后才创建非核心线程执行新任务
拒绝策略
默认抛异常,阻止任务提交
SingleThreadScheduled,ScheduledThreadPool支持调度
自定义线程池
CPU 线程池
核心线程数等于最大线程数,一般为CPU核数
非核心线程存活时间0
任务队列LinkedBlockingDeque,默认无限大,设置为512
拒绝策略,实现RejectedExecutionHandler异常上报,handler兜底执行任务
线程工厂,可设置线程名和优先级
IO 线程池
IO 操作会交给 DMA硬件处理,不需要CPU
每IO 任务可直接给一个独立线程执行,不需要缓存队列
核心线程数,和业务相关
最大线程数,不要太多几十个
任务队列SynchronousQueue,不需要
非核心线程存活时间,和业务相关
拒绝策略,和cpu线程池一样
监控
耗时监控
封装Runnable,设置耗时阈值
run方法中根据线程池类型,判断阈值
超过打印日志,上报
死锁监控
任务开始执行前,放入Map键任务名,值时间戳
周期任务线程池,10秒检测一次长时间没移除任务
没移除任务超过30秒,打印日志,上报
JVM
用于运行Java编译后的字节码
class文件常量池,字节码一部分,存放
字面量,包括字符串,final常量,基本类型值
符号引用,包括类和接口全限定名,字段和方法名称和描述符
直接内存,堆外向系统申请的一块内存空间
内存模型
程序计数器,当前线程执行到的字节码行号,线程私有
栈,方法相关局部变量,参数,返回值等,线程私有
栈帧,存储局部变量表、操作数栈、动态链接、方法出口等
空间不足StackOverFlow
本地方法栈,和Java栈类似,用于Native
堆,存放对象实例和数组,线程共享
方法区,存放类信息、常量、静态变量等,线程共享
运行时常量池,拷贝class文件常量池到内存,运行时添加新常量
JDK8前在永久代可gc,8后Native元空间受本地内存限制
gc
垃圾回收,GC Root为起点,搜索引用链,判断是否可达
- 正运行方法的局部变量
- 静态变量和常量
- JNI和存活线程中的对象
gc算法
标记清除,遍历内存,保留可达对象
有碎片,不移动对象
标记整理,存活对象压缩到内存一侧,清理另一侧
无碎片,要移动对象
复制,存活对象复制到未使用块,清除正使用
无碎片,可用大小缩小一半,频繁复制
分代回收
根据对象周期,划分不同区域
年轻代,分Eden与Survivor区 8:1:1
新生对象优先放Eden
空间不足,清除非存活对象,minor gc
次数为对象年龄
首次,Eden清除非可达,存活的拷贝Survivor1,年龄加1
再次,标记Eden和Survivor1清除,存活拷贝到Survivor2,年龄加1
年龄大于15升级到老年代
老年代,大对象直接进入
分配担保,年轻代空间不足时
检查老年代可用空间,是否大于新生代对象总空间
是,Minor gc
不是,Full GC
Gc 后,存活对象进入Survivor或老年代
老年代空间不足,触发Full GC,还不足OOM
引用类型
强引用:new对象
软引用:堆内存不足被回收,设计缓存
弱引用:GC到来时被回收,监控内存泄漏
虚引用:任何时候都可能被回收,监控GC回收
类加载
过程
加载,用类全限定名获取字节流,转方法区运行时数据,堆生成访问入口Class类
验证,校验文件格式,语义,符号引用
准备,为类静态变量分配内存和默认值
解析,常量池符号引用转物理地址指针
初始化,构造器初始化父类和自身
双亲委托
类加载请求时,先委派给父类,找不到指定类才尝试自己加载
防止重复加载
不篡改系统类
Class有classLoader字段,类名且类加载器相同才是同类
安卓类加载器
BootClassLoader加载Framework类
PathClassLoader加载指定类,如dex,jar、apk或zip的dex
DexClassLoader加载指定类,同上
InMemoryDexClassLoader加载内存的dex
反射
运行时判断和构造类对象
运行时判断和调用对象方法,字段,包括私有
动态代理
- Proxy.newProxyInstance生成代理类
- CGLIB基于ASM,运行时修改字节码,继承实现代理
反射效率低
方法参数要拆装箱,校验类型
不能内联优化
动态加载,无法JIT
Android虚拟机
ART
5.0默认使用,支持 64位
7.0前: 安装 APK 时字节码AOT(预编译)成机器码到磁盘,运行时直接使用
7.0后: 安装 APK不全量AOT,运行时JIT(即时编译)
- 解释器运行时检查,是否是热点,添加到JIT代码缓存区
- 记录到Profile文件
- 设备闲置和充电,启动编译守护进程,将Profile AOT成机器码
相关文件
dex: 可执行文件,dexdump查看
odex: 和dex类似,dexopt后产物,优化操作码
oat:本地可执行ELF文件,dex2oat产物,oatdump查看
vdex:8.0跳过验证减少dex2oat时间,包含dex和quicken info
art:Image文件,记录热点函数地址
区别
JVM 基于栈,ART基于寄存器
JVM 执行class,ART执行dex或oat
- 各应用进程独立
- 多个class 打包成dex,去除冗余,减少I/O
堆
Image Space ,预加载的系统类,不分配新对象,不gc
Zygote Space,启动过程加载的类、资源和对象,进程共享
Allocation Space ,应用创建对象,fork前从Zygote堆划分,非进程共享
Large Object Space:
请求分配内存大于3 个页面大小
Allocation Space已创建
被分配对象是基本数据数组
GC
8前默认并发标记清除,支持有条件内存压缩,可能有内存碎片
8后默认并发复制,复制对象执行堆碎片整理,不stop the world
10后并发粘性标记清除,基于分代更快更频繁
GC日志
主动GC或暂停超5ms或持续超100ms才打印
Reason:
- Concurrent:并发 GC
-
- Alloc:堆内存已满尝试分配GC
- NativeAlloc:Native同Alloc
- Explicit:手动请求gc如System.gc()
- CollectorTransition:堆转换gc,内存较小设备
Name垃圾收集器
- 并发标记清除,暂停短,释放Image外所有空间
- 并发局部标记清除,释放Image和 Zygote外所有空间
- 并发粘性标记清除:基于分代,释放上次GC后分配的对象,更频繁更快
- 标记清除和半空间:非并发,用于堆转换及碎片整理
其他
- Objects_freed:从非大对象空间回收的数量
- Size_freed:从非大对象空间回收的字节数
- Large_objects_freed和size_freed:从大对象空间回收的数量和字节数
- Heap_stats:堆空闲内存百分比,(已用内存 / 堆的总内存)
- Pause_times:暂停时间