├── 网络基础 ├── 代理.png ├── 网关.png ├── GCM.jpg ├── IP分片.png ├── SMTP.png ├── 流控制.png ├── 长轮询.png ├── Ajax轮询.png ├── IPv4首部.png ├── IP地址的分类.png ├── OSI七层协议.png ├── OSI七层模型.png ├── TCP状态转换.jpg ├── TCP连接建立.jpg ├── TCP连接管理.png ├── TCP首部格式.png ├── UDP首部格式.png ├── URI格式.png ├── max-age.png ├── 协议之间的关系.png ├── 响应报文的构成.png ├── 响应首部字段.png ├── 拥塞控制曲线图.png ├── 数字证书机构.png ├── 数据包的结构.png ├── 滑动窗口控制.png ├── 状态码类别.png ├── 缓存响应指令.png ├── 缓存请求指令.png ├── 网络构成要素.png ├── 请求报文格式.png ├── 请求首部字段.png ├── 通用首部字段.png ├── HTTPS图解过程.png ├── TCP&HTTP.png ├── min-fresh.png ├── no-cache.png ├── HTTP&HTTPS.png ├── Http cache总结.png ├── SSL-TLS握手过程.png ├── socket底层实现.jpg ├── 请求报文和响应报文的结构.png ├── Cookie&Session.png ├── Set-Cookie字段属性.png ├── TCP-IP与OSI参考模型.png ├── 缓存机制-浏览器第一次请求.png ├── 缓存机制-浏览器第二次请求.png ├── Cache-Control响应首部取值.png ├── Cache-Control请求首部取值.png ├── multipart-form-data.png ├── websocket_request_header.png ├── Socket.md ├── WebSocket.md ├── 推送PSUH总结.md ├── Http缓存策略总结.md ├── Https总结.md ├── Http学习笔记.md └── TCP_IP.md ├── 设计模式 ├── uml.jpg ├── Adapter.jpg ├── Bridge.jpg ├── Proxy.jpg ├── builder.png ├── command.png ├── Decorator.jpg ├── Strategy.jpg ├── FactoryMethod.jpg ├── StaticFactory.jpg ├── AbatractFactory.jpg ├── 读整洁代码之道--重构.md └── 设计模式.md ├── Android ├── AMS.png ├── MVC1.png ├── MVP.png ├── UI优化.png ├── 属性动画.jpg ├── DecorView.png ├── Java的四种引用.jpg ├── View的绘制过程.png ├── View绘制入口.png ├── apk打包过程.png ├── okhttp拦截器.png ├── 消息机制时序图.jpg ├── 线程消息发送时序图.png ├── Binder │ ├── mmap.png │ ├── Binder工作原理.png │ ├── C++层Binder.png │ ├── Java中的Binder.png │ ├── Binder对象的交互过程.png │ ├── binder_thread.jpg │ ├── cpp_binder_uml.png │ ├── binder_ipc_arch.jpg │ ├── binder_overview.png │ ├── Binder_Architecture.png │ ├── binder_main_struct.png │ ├── binder_proxy_transact.png │ ├── binder_request_sequence.png │ └── Binder&ProcessState&binder_proc.png ├── Picasso时序图.jpg ├── Retrofit框架.png ├── RxJava │ ├── 线程切换.jpg │ └── 操作符原理.png ├── Window的删除过程.png ├── Window的添加过程.png ├── okhttp请求流程.jpg ├── service生命周期.png ├── 消息循环loop时序图.png ├── AIDL-Interface.png ├── Android消息机制架构.jpg ├── Dalvik与Java虚拟机.png ├── bindService时序图.jpg ├── okhttp各个拦截器作用.jpg ├── startActivity.png ├── 在新进程启动服务的完整过程图.wmf ├── 在进程内绑定服务的完整过程图.wmf ├── 输入系统的总体流程与参与者.png ├── ACTION_DOWN事件分发.png ├── ACTION_MOVE事件分发.png ├── AMS&WMS&APP进程的关系.png ├── Android显示系统的三个层次.png ├── IActivityManager.png ├── Instrumentation.png ├── listview_layout.jpg ├── listview_scroll.jpg ├── startActivity时序图.jpg ├── startService时序图.jpg ├── start_process时序图.jpg ├── 插件化 │ ├── Tinker热修复方案.png │ ├── Activity启动的简要过程.png │ ├── 美团Robust热修复原理图.png │ ├── atlas_apk_struct.png │ ├── Atlas原理.md │ └── 热修复和插件化小结.md ├── IApplicationThread.png ├── ViewRoot 和 WMS 的关系.png ├── activity_lifecycle.png ├── recyclerview_layout.jpg ├── start_app_process.jpg ├── 根Activity的完整启动过程图.wmf ├── Dalvik与Java虚拟机class结构.png ├── DecorView添加到Window的过程.png ├── 子Activity在进程内的完整启动过程图.wmf ├── Looper和MessageQueue关系图.png ├── WindowMangerGlobal 的窗口管理.png ├── Android消息机制的C++层native_init.png ├── ListView.md ├── Retrofit框架思路浅析.md ├── Android内存泄漏.md ├── 性能优化总结.md ├── Android新进程的启动过程.md ├── RecyclerView.md ├── Activity-LifeCircle&Launchmode.md ├── Android-Drawable.md ├── 高效加载Bitmap.md ├── 深入理解Android的窗口系统.md └── Android动画.md ├── C&C++ ├── C的编译过程.png ├── string的操作.png ├── vector的操作.png ├── prinf()转换说明符.png ├── C++ Primer.md ├── C Primer.md └── C&C++面试题总结.md ├── Java ├── 基础 │ ├── IO模型.jpg │ ├── Java异常.jpg │ ├── Pipe管道.png │ ├── JVM异常处理.png │ ├── Selector.png │ ├── Buffer-Mode.png │ ├── JDK各个版本的新特性.png │ ├── buffer内存映射.png │ ├── Java字符流和字节流的区别.jpg │ ├── data-buffering-at-os-level.png │ ├── JNI调用机制.md │ ├── Java异常处理.md │ ├── select&poll&epoll.md │ ├── Java反射实现原理.md │ ├── Java泛型.md │ └── Java IO.md ├── 多线程 │ ├── JMM.png │ ├── 同步队列.png │ ├── Executor框架.png │ ├── 同步器可重写的方法.png │ ├── 同步器提供的模板方法.png │ ├── 同步队列与等待队列.png │ ├── TPE#addWorker.png │ ├── TPE.addWorker.png │ ├── acquire方法调用流程.png │ ├── FutureTask&AQS.png │ ├── ConcurrentHashMap.jpg │ ├── BlockingQueue的插入和移除操作.png │ ├── 深入ThreadLocal.md │ └── 深入ThreadPoolExecutor.md ├── JVM │ ├── 分代搜集法.jpg │ ├── 复制算法.png │ ├── 用户线程.png │ ├── 虚函数表.png │ ├── CMS运行图.png │ ├── JVM内存分代.png │ ├── JVM内存结构.png │ ├── LWP与KLT.png │ ├── UT与LWP混合.png │ ├── 双亲委派模型.png │ ├── 可达性分析算法.png │ ├── 对象的内存布局.png │ ├── 工作内存和主内存.png │ ├── 类的生命周期.png │ ├── 线程状态转换关系.png │ ├── 双亲委派模型bak.png │ ├── ClassLoader.png │ ├── Java Class的结构.jpg │ ├── method_info.png │ ├── CONSTANT_UTF8_INFO.png │ ├── CONSTANT_NameAndType.png │ ├── fields_count和fields.png │ ├── JVM内存分区.md │ ├── 摘要笔记.md │ ├── 深入理解JVM方法调用的内部机制.md │ ├── Java内存模型与并发.md │ └── GC算法与内存分配策略.md └── JDK │ ├── addEntry.png │ ├── getEntry.png │ ├── resize.png │ ├── 简单的容器分类.png │ ├── JDK各个版本的新特性.png │ ├── Java集合类继承类图.jpg │ ├── Java集合框架概括.md │ ├── fail-fast机制.md │ ├── LinkedHashMap&LruCache.md │ ├── ArrayList.md │ └── HashMap.md ├── 操作系统 ├── DMA结构图.png ├── 五状态进程模型.png ├── 进程组成结构.png ├── Linux内核组件.png ├── 分段系统中的地址转换.png ├── 分段系统中的地址转换1.png ├── 分页系统中的地址转换.png ├── 操作系统控制表的通用结构.png ├── 段页式系统中的地址转换.png ├── 计算机部件:顶层视图.png └── 操作系统知识拾遗.md ├── 数据结构与算法 ├── 排序算法.png ├── 红黑树.png ├── 排序算法_.png ├── 红黑树旋转.png ├── 红黑树构造过程.png ├── 图和树的相关公式和定理.md ├── 红黑树.md ├── 一致性Hash算法.md └── 排序算法.md └── 数据库 └── 数据库的基础知识.md /网络基础/代理.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/代理.png -------------------------------------------------------------------------------- /网络基础/网关.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/网关.png -------------------------------------------------------------------------------- /网络基础/GCM.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/GCM.jpg -------------------------------------------------------------------------------- /网络基础/IP分片.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/IP分片.png -------------------------------------------------------------------------------- /网络基础/SMTP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/SMTP.png -------------------------------------------------------------------------------- /网络基础/流控制.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/流控制.png -------------------------------------------------------------------------------- /网络基础/长轮询.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/长轮询.png -------------------------------------------------------------------------------- /设计模式/uml.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/设计模式/uml.jpg -------------------------------------------------------------------------------- /Android/AMS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/AMS.png -------------------------------------------------------------------------------- /Android/MVC1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/MVC1.png -------------------------------------------------------------------------------- /Android/MVP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/MVP.png -------------------------------------------------------------------------------- /Android/UI优化.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/UI优化.png -------------------------------------------------------------------------------- /Android/属性动画.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/属性动画.jpg -------------------------------------------------------------------------------- /C&C++/C的编译过程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/C&C++/C的编译过程.png -------------------------------------------------------------------------------- /Java/基础/IO模型.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/基础/IO模型.jpg -------------------------------------------------------------------------------- /Java/多线程/JMM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/多线程/JMM.png -------------------------------------------------------------------------------- /操作系统/DMA结构图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/操作系统/DMA结构图.png -------------------------------------------------------------------------------- /操作系统/五状态进程模型.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/操作系统/五状态进程模型.png -------------------------------------------------------------------------------- /操作系统/进程组成结构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/操作系统/进程组成结构.png -------------------------------------------------------------------------------- /数据结构与算法/排序算法.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/数据结构与算法/排序算法.png -------------------------------------------------------------------------------- /数据结构与算法/红黑树.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/数据结构与算法/红黑树.png -------------------------------------------------------------------------------- /网络基础/Ajax轮询.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/Ajax轮询.png -------------------------------------------------------------------------------- /网络基础/IPv4首部.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/IPv4首部.png -------------------------------------------------------------------------------- /网络基础/IP地址的分类.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/IP地址的分类.png -------------------------------------------------------------------------------- /网络基础/OSI七层协议.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/OSI七层协议.png -------------------------------------------------------------------------------- /网络基础/OSI七层模型.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/OSI七层模型.png -------------------------------------------------------------------------------- /网络基础/TCP状态转换.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/TCP状态转换.jpg -------------------------------------------------------------------------------- /网络基础/TCP连接建立.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/TCP连接建立.jpg -------------------------------------------------------------------------------- /网络基础/TCP连接管理.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/TCP连接管理.png -------------------------------------------------------------------------------- /网络基础/TCP首部格式.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/TCP首部格式.png -------------------------------------------------------------------------------- /网络基础/UDP首部格式.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/UDP首部格式.png -------------------------------------------------------------------------------- /网络基础/URI格式.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/URI格式.png -------------------------------------------------------------------------------- /网络基础/max-age.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/max-age.png -------------------------------------------------------------------------------- /网络基础/协议之间的关系.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/协议之间的关系.png -------------------------------------------------------------------------------- /网络基础/响应报文的构成.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/响应报文的构成.png -------------------------------------------------------------------------------- /网络基础/响应首部字段.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/响应首部字段.png -------------------------------------------------------------------------------- /网络基础/拥塞控制曲线图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/拥塞控制曲线图.png -------------------------------------------------------------------------------- /网络基础/数字证书机构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/数字证书机构.png -------------------------------------------------------------------------------- /网络基础/数据包的结构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/数据包的结构.png -------------------------------------------------------------------------------- /网络基础/滑动窗口控制.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/滑动窗口控制.png -------------------------------------------------------------------------------- /网络基础/状态码类别.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/状态码类别.png -------------------------------------------------------------------------------- /网络基础/缓存响应指令.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/缓存响应指令.png -------------------------------------------------------------------------------- /网络基础/缓存请求指令.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/缓存请求指令.png -------------------------------------------------------------------------------- /网络基础/网络构成要素.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/网络构成要素.png -------------------------------------------------------------------------------- /网络基础/请求报文格式.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/请求报文格式.png -------------------------------------------------------------------------------- /网络基础/请求首部字段.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/请求首部字段.png -------------------------------------------------------------------------------- /网络基础/通用首部字段.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/通用首部字段.png -------------------------------------------------------------------------------- /设计模式/Adapter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/设计模式/Adapter.jpg -------------------------------------------------------------------------------- /设计模式/Bridge.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/设计模式/Bridge.jpg -------------------------------------------------------------------------------- /设计模式/Proxy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/设计模式/Proxy.jpg -------------------------------------------------------------------------------- /设计模式/builder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/设计模式/builder.png -------------------------------------------------------------------------------- /设计模式/command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/设计模式/command.png -------------------------------------------------------------------------------- /Java/JVM/分代搜集法.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/分代搜集法.jpg -------------------------------------------------------------------------------- /Java/JVM/复制算法.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/复制算法.png -------------------------------------------------------------------------------- /Java/JVM/用户线程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/用户线程.png -------------------------------------------------------------------------------- /Java/JVM/虚函数表.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/虚函数表.png -------------------------------------------------------------------------------- /Java/基础/Java异常.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/基础/Java异常.jpg -------------------------------------------------------------------------------- /Java/基础/Pipe管道.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/基础/Pipe管道.png -------------------------------------------------------------------------------- /Java/多线程/同步队列.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/多线程/同步队列.png -------------------------------------------------------------------------------- /操作系统/Linux内核组件.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/操作系统/Linux内核组件.png -------------------------------------------------------------------------------- /数据结构与算法/排序算法_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/数据结构与算法/排序算法_.png -------------------------------------------------------------------------------- /数据结构与算法/红黑树旋转.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/数据结构与算法/红黑树旋转.png -------------------------------------------------------------------------------- /网络基础/HTTPS图解过程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/HTTPS图解过程.png -------------------------------------------------------------------------------- /网络基础/TCP&HTTP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/TCP&HTTP.png -------------------------------------------------------------------------------- /网络基础/min-fresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/min-fresh.png -------------------------------------------------------------------------------- /网络基础/no-cache.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/no-cache.png -------------------------------------------------------------------------------- /设计模式/Decorator.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/设计模式/Decorator.jpg -------------------------------------------------------------------------------- /设计模式/Strategy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/设计模式/Strategy.jpg -------------------------------------------------------------------------------- /Android/DecorView.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/DecorView.png -------------------------------------------------------------------------------- /Android/Java的四种引用.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Java的四种引用.jpg -------------------------------------------------------------------------------- /Android/View的绘制过程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/View的绘制过程.png -------------------------------------------------------------------------------- /Android/View绘制入口.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/View绘制入口.png -------------------------------------------------------------------------------- /Android/apk打包过程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/apk打包过程.png -------------------------------------------------------------------------------- /Android/okhttp拦截器.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/okhttp拦截器.png -------------------------------------------------------------------------------- /Android/消息机制时序图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/消息机制时序图.jpg -------------------------------------------------------------------------------- /Android/线程消息发送时序图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/线程消息发送时序图.png -------------------------------------------------------------------------------- /C&C++/string的操作.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/C&C++/string的操作.png -------------------------------------------------------------------------------- /C&C++/vector的操作.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/C&C++/vector的操作.png -------------------------------------------------------------------------------- /Java/JDK/addEntry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JDK/addEntry.png -------------------------------------------------------------------------------- /Java/JDK/getEntry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JDK/getEntry.png -------------------------------------------------------------------------------- /Java/JDK/resize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JDK/resize.png -------------------------------------------------------------------------------- /Java/JDK/简单的容器分类.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JDK/简单的容器分类.png -------------------------------------------------------------------------------- /Java/JVM/CMS运行图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/CMS运行图.png -------------------------------------------------------------------------------- /Java/JVM/JVM内存分代.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/JVM内存分代.png -------------------------------------------------------------------------------- /Java/JVM/JVM内存结构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/JVM内存结构.png -------------------------------------------------------------------------------- /Java/JVM/LWP与KLT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/LWP与KLT.png -------------------------------------------------------------------------------- /Java/JVM/UT与LWP混合.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/UT与LWP混合.png -------------------------------------------------------------------------------- /Java/JVM/双亲委派模型.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/双亲委派模型.png -------------------------------------------------------------------------------- /Java/JVM/可达性分析算法.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/可达性分析算法.png -------------------------------------------------------------------------------- /Java/JVM/对象的内存布局.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/对象的内存布局.png -------------------------------------------------------------------------------- /Java/JVM/工作内存和主内存.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/工作内存和主内存.png -------------------------------------------------------------------------------- /Java/JVM/类的生命周期.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/类的生命周期.png -------------------------------------------------------------------------------- /Java/JVM/线程状态转换关系.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/线程状态转换关系.png -------------------------------------------------------------------------------- /Java/基础/JVM异常处理.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/基础/JVM异常处理.png -------------------------------------------------------------------------------- /Java/基础/Selector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/基础/Selector.png -------------------------------------------------------------------------------- /操作系统/分段系统中的地址转换.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/操作系统/分段系统中的地址转换.png -------------------------------------------------------------------------------- /操作系统/分段系统中的地址转换1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/操作系统/分段系统中的地址转换1.png -------------------------------------------------------------------------------- /操作系统/分页系统中的地址转换.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/操作系统/分页系统中的地址转换.png -------------------------------------------------------------------------------- /操作系统/操作系统控制表的通用结构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/操作系统/操作系统控制表的通用结构.png -------------------------------------------------------------------------------- /操作系统/段页式系统中的地址转换.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/操作系统/段页式系统中的地址转换.png -------------------------------------------------------------------------------- /操作系统/计算机部件:顶层视图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/操作系统/计算机部件:顶层视图.png -------------------------------------------------------------------------------- /数据结构与算法/红黑树构造过程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/数据结构与算法/红黑树构造过程.png -------------------------------------------------------------------------------- /网络基础/HTTP&HTTPS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/HTTP&HTTPS.png -------------------------------------------------------------------------------- /网络基础/Http cache总结.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/Http cache总结.png -------------------------------------------------------------------------------- /网络基础/SSL-TLS握手过程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/SSL-TLS握手过程.png -------------------------------------------------------------------------------- /网络基础/socket底层实现.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/socket底层实现.jpg -------------------------------------------------------------------------------- /网络基础/请求报文和响应报文的结构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/请求报文和响应报文的结构.png -------------------------------------------------------------------------------- /Android/Binder/mmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Binder/mmap.png -------------------------------------------------------------------------------- /Android/Picasso时序图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Picasso时序图.jpg -------------------------------------------------------------------------------- /Android/Retrofit框架.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Retrofit框架.png -------------------------------------------------------------------------------- /Android/RxJava/线程切换.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/RxJava/线程切换.jpg -------------------------------------------------------------------------------- /Android/Window的删除过程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Window的删除过程.png -------------------------------------------------------------------------------- /Android/Window的添加过程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Window的添加过程.png -------------------------------------------------------------------------------- /Android/okhttp请求流程.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/okhttp请求流程.jpg -------------------------------------------------------------------------------- /Android/service生命周期.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/service生命周期.png -------------------------------------------------------------------------------- /Android/消息循环loop时序图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/消息循环loop时序图.png -------------------------------------------------------------------------------- /C&C++/prinf()转换说明符.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/C&C++/prinf()转换说明符.png -------------------------------------------------------------------------------- /Java/JVM/双亲委派模型bak.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/双亲委派模型bak.png -------------------------------------------------------------------------------- /Java/基础/Buffer-Mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/基础/Buffer-Mode.png -------------------------------------------------------------------------------- /Java/基础/JDK各个版本的新特性.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/基础/JDK各个版本的新特性.png -------------------------------------------------------------------------------- /Java/基础/buffer内存映射.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/基础/buffer内存映射.png -------------------------------------------------------------------------------- /Java/多线程/Executor框架.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/多线程/Executor框架.png -------------------------------------------------------------------------------- /Java/多线程/同步器可重写的方法.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/多线程/同步器可重写的方法.png -------------------------------------------------------------------------------- /Java/多线程/同步器提供的模板方法.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/多线程/同步器提供的模板方法.png -------------------------------------------------------------------------------- /Java/多线程/同步队列与等待队列.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/多线程/同步队列与等待队列.png -------------------------------------------------------------------------------- /网络基础/Cookie&Session.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/Cookie&Session.png -------------------------------------------------------------------------------- /网络基础/Set-Cookie字段属性.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/Set-Cookie字段属性.png -------------------------------------------------------------------------------- /网络基础/TCP-IP与OSI参考模型.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/TCP-IP与OSI参考模型.png -------------------------------------------------------------------------------- /网络基础/缓存机制-浏览器第一次请求.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/缓存机制-浏览器第一次请求.png -------------------------------------------------------------------------------- /网络基础/缓存机制-浏览器第二次请求.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/缓存机制-浏览器第二次请求.png -------------------------------------------------------------------------------- /设计模式/FactoryMethod.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/设计模式/FactoryMethod.jpg -------------------------------------------------------------------------------- /设计模式/StaticFactory.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/设计模式/StaticFactory.jpg -------------------------------------------------------------------------------- /Android/AIDL-Interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/AIDL-Interface.png -------------------------------------------------------------------------------- /Android/Android消息机制架构.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Android消息机制架构.jpg -------------------------------------------------------------------------------- /Android/Dalvik与Java虚拟机.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Dalvik与Java虚拟机.png -------------------------------------------------------------------------------- /Android/RxJava/操作符原理.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/RxJava/操作符原理.png -------------------------------------------------------------------------------- /Android/bindService时序图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/bindService时序图.jpg -------------------------------------------------------------------------------- /Android/okhttp各个拦截器作用.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/okhttp各个拦截器作用.jpg -------------------------------------------------------------------------------- /Android/startActivity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/startActivity.png -------------------------------------------------------------------------------- /Android/在新进程启动服务的完整过程图.wmf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/在新进程启动服务的完整过程图.wmf -------------------------------------------------------------------------------- /Android/在进程内绑定服务的完整过程图.wmf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/在进程内绑定服务的完整过程图.wmf -------------------------------------------------------------------------------- /Android/输入系统的总体流程与参与者.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/输入系统的总体流程与参与者.png -------------------------------------------------------------------------------- /Java/JDK/JDK各个版本的新特性.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JDK/JDK各个版本的新特性.png -------------------------------------------------------------------------------- /Java/JDK/Java集合类继承类图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JDK/Java集合类继承类图.jpg -------------------------------------------------------------------------------- /Java/JVM/ClassLoader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/ClassLoader.png -------------------------------------------------------------------------------- /Java/JVM/Java Class的结构.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/Java Class的结构.jpg -------------------------------------------------------------------------------- /Java/JVM/method_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/method_info.png -------------------------------------------------------------------------------- /Java/基础/Java字符流和字节流的区别.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/基础/Java字符流和字节流的区别.jpg -------------------------------------------------------------------------------- /Java/多线程/TPE#addWorker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/多线程/TPE#addWorker.png -------------------------------------------------------------------------------- /Java/多线程/TPE.addWorker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/多线程/TPE.addWorker.png -------------------------------------------------------------------------------- /Java/多线程/acquire方法调用流程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/多线程/acquire方法调用流程.png -------------------------------------------------------------------------------- /设计模式/AbatractFactory.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/设计模式/AbatractFactory.jpg -------------------------------------------------------------------------------- /Android/ACTION_DOWN事件分发.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/ACTION_DOWN事件分发.png -------------------------------------------------------------------------------- /Android/ACTION_MOVE事件分发.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/ACTION_MOVE事件分发.png -------------------------------------------------------------------------------- /Android/AMS&WMS&APP进程的关系.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/AMS&WMS&APP进程的关系.png -------------------------------------------------------------------------------- /Android/Android显示系统的三个层次.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Android显示系统的三个层次.png -------------------------------------------------------------------------------- /Android/IActivityManager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/IActivityManager.png -------------------------------------------------------------------------------- /Android/Instrumentation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Instrumentation.png -------------------------------------------------------------------------------- /Android/listview_layout.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/listview_layout.jpg -------------------------------------------------------------------------------- /Android/listview_scroll.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/listview_scroll.jpg -------------------------------------------------------------------------------- /Android/startActivity时序图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/startActivity时序图.jpg -------------------------------------------------------------------------------- /Android/startService时序图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/startService时序图.jpg -------------------------------------------------------------------------------- /Android/start_process时序图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/start_process时序图.jpg -------------------------------------------------------------------------------- /Android/插件化/Tinker热修复方案.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/插件化/Tinker热修复方案.png -------------------------------------------------------------------------------- /Java/多线程/FutureTask&AQS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/多线程/FutureTask&AQS.png -------------------------------------------------------------------------------- /网络基础/Cache-Control响应首部取值.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/Cache-Control响应首部取值.png -------------------------------------------------------------------------------- /网络基础/Cache-Control请求首部取值.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/Cache-Control请求首部取值.png -------------------------------------------------------------------------------- /网络基础/multipart-form-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/multipart-form-data.png -------------------------------------------------------------------------------- /Android/Binder/Binder工作原理.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Binder/Binder工作原理.png -------------------------------------------------------------------------------- /Android/Binder/C++层Binder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Binder/C++层Binder.png -------------------------------------------------------------------------------- /Android/Binder/Java中的Binder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Binder/Java中的Binder.png -------------------------------------------------------------------------------- /Android/IApplicationThread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/IApplicationThread.png -------------------------------------------------------------------------------- /Android/ViewRoot 和 WMS 的关系.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/ViewRoot 和 WMS 的关系.png -------------------------------------------------------------------------------- /Android/activity_lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/activity_lifecycle.png -------------------------------------------------------------------------------- /Android/recyclerview_layout.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/recyclerview_layout.jpg -------------------------------------------------------------------------------- /Android/start_app_process.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/start_app_process.jpg -------------------------------------------------------------------------------- /Android/插件化/Activity启动的简要过程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/插件化/Activity启动的简要过程.png -------------------------------------------------------------------------------- /Android/插件化/美团Robust热修复原理图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/插件化/美团Robust热修复原理图.png -------------------------------------------------------------------------------- /Android/根Activity的完整启动过程图.wmf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/根Activity的完整启动过程图.wmf -------------------------------------------------------------------------------- /Java/JVM/CONSTANT_UTF8_INFO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/CONSTANT_UTF8_INFO.png -------------------------------------------------------------------------------- /Java/多线程/ConcurrentHashMap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/多线程/ConcurrentHashMap.jpg -------------------------------------------------------------------------------- /Android/Binder/Binder对象的交互过程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Binder/Binder对象的交互过程.png -------------------------------------------------------------------------------- /Android/Binder/binder_thread.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Binder/binder_thread.jpg -------------------------------------------------------------------------------- /Android/Binder/cpp_binder_uml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Binder/cpp_binder_uml.png -------------------------------------------------------------------------------- /Android/Dalvik与Java虚拟机class结构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Dalvik与Java虚拟机class结构.png -------------------------------------------------------------------------------- /Android/DecorView添加到Window的过程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/DecorView添加到Window的过程.png -------------------------------------------------------------------------------- /Android/子Activity在进程内的完整启动过程图.wmf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/子Activity在进程内的完整启动过程图.wmf -------------------------------------------------------------------------------- /Android/插件化/atlas_apk_struct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/插件化/atlas_apk_struct.png -------------------------------------------------------------------------------- /Java/JVM/CONSTANT_NameAndType.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/CONSTANT_NameAndType.png -------------------------------------------------------------------------------- /Java/JVM/fields_count和fields.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/JVM/fields_count和fields.png -------------------------------------------------------------------------------- /网络基础/websocket_request_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/网络基础/websocket_request_header.png -------------------------------------------------------------------------------- /Android/Binder/binder_ipc_arch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Binder/binder_ipc_arch.jpg -------------------------------------------------------------------------------- /Android/Binder/binder_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Binder/binder_overview.png -------------------------------------------------------------------------------- /Android/Looper和MessageQueue关系图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Looper和MessageQueue关系图.png -------------------------------------------------------------------------------- /Android/WindowMangerGlobal 的窗口管理.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/WindowMangerGlobal 的窗口管理.png -------------------------------------------------------------------------------- /Java/多线程/BlockingQueue的插入和移除操作.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/多线程/BlockingQueue的插入和移除操作.png -------------------------------------------------------------------------------- /Android/Binder/Binder_Architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Binder/Binder_Architecture.png -------------------------------------------------------------------------------- /Android/Binder/binder_main_struct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Binder/binder_main_struct.png -------------------------------------------------------------------------------- /Java/基础/data-buffering-at-os-level.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Java/基础/data-buffering-at-os-level.png -------------------------------------------------------------------------------- /Android/Android消息机制的C++层native_init.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Android消息机制的C++层native_init.png -------------------------------------------------------------------------------- /Android/Binder/binder_proxy_transact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Binder/binder_proxy_transact.png -------------------------------------------------------------------------------- /Android/Binder/binder_request_sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Binder/binder_request_sequence.png -------------------------------------------------------------------------------- /Android/Binder/Binder&ProcessState&binder_proc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TellH/Interview-Note/HEAD/Android/Binder/Binder&ProcessState&binder_proc.png -------------------------------------------------------------------------------- /设计模式/读整洁代码之道--重构.md: -------------------------------------------------------------------------------- 1 | - 代码的整洁度和质量成正比,一份整洁的代码在质量上是可靠的。 2 | - 面向对象设计原则等都是为了减少重复提高重用,Don’t repeat yourself(DRY)。 3 | - 整洁的代码简单直接。整洁的代码如同优美的散文。整洁的代码从不隐藏设计者的意图,充满了干净利落的抽象和直截了当的控制语句。 4 | - 如何解释重构。名词:对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。 -------------------------------------------------------------------------------- /数据结构与算法/图和树的相关公式和定理.md: -------------------------------------------------------------------------------- 1 | 1. 如果一个图有n个结点,那么它肯定至少有n-1条边。 2 | 2. 树的度是指树中结点拥有子结点的最大值;树结点的度是指其拥有子结点的个数,叶节点的度为0。 3 | 3. 在二叉树中,n0+n1+n2=n; n1+2*n2=n-1; 推导出:n2=n0-1;即度数为2的节点数等于叶节点数-1。 4 | 4. 完全二叉树的性质:深度为k的二叉树最多有2^k-1个节点;第i层最多有2^(k-1)个节点;n个节点的完全二叉树高度为(logn2)+1。 5 | 5. 有n个顶点的强连通图最多有n(n-1)条边,最少有n条边。 6 | 6. M阶的B树节点的关键数目最多为M-1个,最少M/2-1个(向上取整)。 7 | 8 | 9 | -------------------------------------------------------------------------------- /数据结构与算法/红黑树.md: -------------------------------------------------------------------------------- 1 | 红黑树是2-3-4树的一种等同。 2 | 3 | 红黑树相对于AVL树来说,牺牲了部分平衡性以换取插入/删除操作时少量的旋转操作,整体来说性能要优于AVL树。 4 | 5 | ### 来自维基百科 6 | 7 | 红黑树的性质和定义: 8 | 9 | 红黑树是每个节点都带有颜色属性的二查搜索树,颜色为红色和黑色。 10 | 11 | 1. 根节点是黑色的 12 | 2. 所有叶子节点是黑色的。(叶子是NIL节点) 13 | 3. 每个红色节点必须有两个黑色的子节点 14 | 4. 从任意节点到其每个叶子的所有简单路径都包含相同数目的黑色节点 15 | 16 | ![红黑树](红黑树.png)、 17 | 18 | ### 来自《算法》 19 | 20 | 用另一种等价的定义,定义边的红黑而不是节点的红黑。 21 | 22 | 红黑树就是用红链接表示3-结点的2-3树。 23 | 24 | 红黑树是满足下列条件的二叉查找树: 25 | 26 | 1. 红链接均为左链接。 27 | 2. 没有任何一个结点同时和两条红链接相连。 28 | 3. 该树是完美黑色平衡的,即任意空链接到根结点的路径上的黑链接数量相同。 29 | 30 | 如果我们将一颗红黑树中的红链接画平,那么所有的空链接到根结点的距离都将是相同的。 31 | 32 | #### 旋转 33 | 34 | ![红黑树旋转](红黑树旋转.png) 35 | 36 | #### 插入 37 | 38 | ![红黑树构造过程](红黑树构造过程.png) -------------------------------------------------------------------------------- /Java/JDK/Java集合框架概括.md: -------------------------------------------------------------------------------- 1 | ### 集合框架 2 | 3 | ![Java集合类继承类图](Java集合类继承类图.jpg) 4 | 5 | 集合类主要分为两大类:Collection和Map。 6 | 7 | **Collection**是List、Set等集合高度抽象出来的接口,它包含了这些集合的基本操作,它主要又分为两大部分:List和Set。 8 | 9 | Collection保存单一元素,而Map保存相关联的键值对。 10 | 11 | **List**接口通常表示一个列表(数组、队列、链表、栈等),其中的元素可以重复,常用实现类为ArrayList和LinkedList,另外还有不常用的Vector。另外,LinkedList还是实现了Queue接口,因此也可以作为队列使用。 12 | 13 | **Set**接口通常表示一个集合,其中的元素不允许重复(通过hashcode和equals函数保证),常用实现类有HashSet和TreeSet,HashSet是通过Map中的HashMap实现的,而TreeSet是通过Map中的TreeMap实现的。另外,TreeSet还实现了SortedSet接口,因此是有序的集合(集合中的元素要实现Comparable接口,并覆写Compartor函数才行)。 14 | 15 | ![简单的容器分类](简单的容器分类.png) 16 | 17 | ### 关于Java容器类的知识点 18 | 19 | 1# Collection不支持随机访问,没有get和set方法,只能用迭代器访问元素。 -------------------------------------------------------------------------------- /Java/JDK/fail-fast机制.md: -------------------------------------------------------------------------------- 1 | http://www.cnblogs.com/skywang12345/p/3308762.html 2 | 3 | **fail-fast 机制是java集合(Collection)中的一种错误机制。**当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。 4 | 例如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件。 5 | 6 | ### 解决方法 7 | 8 | 若在多线程环境下使用fail-fast机制的集合,建议使用“java.util.concurrent包下的类”去取代“java.util包下的类”。比如:ConcurrentHashMap,CopyOnWriteArrayList等容器。 9 | 10 | ### 原理 11 | 12 | ```java 13 | final void checkForComodification() { 14 | if (modCount != expectedModCount) 15 | throw new ConcurrentModificationException(); 16 | } 17 | ``` 18 | 19 | 在调用 next() 和 remove()时,都会执行 checkForComodification()。若 “**modCount 不等于 expectedModCount**”,则抛出ConcurrentModificationException异常,产生fail-fast事件。 20 | 21 | 当多个线程对同一个集合进行操作的时候,某线程访问集合的过程中,该集合的内容被其他线程所改变(即其它线程通过add、remove、clear等方法,改变了modCount的值);这时,就会抛出ConcurrentModificationException异常,产生fail-fast事件。 -------------------------------------------------------------------------------- /Java/基础/JNI调用机制.md: -------------------------------------------------------------------------------- 1 | ## Java访问C 2 | 3 | 当调用native函数时,Java会自动产生一个对应的C中函数名称。例如Framework中AssetManager类中声明了以下方法: 4 | 5 | ```java 6 | private native final void init(); 7 | ``` 8 | 9 | 该方法对应在C中是: 10 | 11 | ```c 12 | static void android_content_AssetManager_init(JNIEnv* env, jobject clazz) 13 | ``` 14 | 15 | 当Java调用native时,编译器会向native引擎传递调用者的包名,以及函数名和参数类型。 16 | 17 | 在产生的C函数中,会包含至少两个参数。前者是JNIEnv对象,该对象是一个JVM所运行的环境,相当于JVM的管家,通过它可以访问JVM内部的各种对象;第二个参数jobject是调用该函数的对象。 18 | 19 | 20 | 21 | ## C访问Java 22 | 23 | C调用Java时,也需要把想要访问的类名,函数名和参数传递给Java引擎。其步骤如下: 24 | 25 | 1. 获取Java对象的类 26 | 27 | ```c 28 | cls = env->GetObjectClass(jobject) 29 | ``` 30 | 31 | 2. 获取Java方法的id 32 | 33 | ```c 34 | jmethodId mid = env->GetMethodId(cls, "method_name", "(Ljava/lang/String;)V"); 35 | ``` 36 | 37 | 第三个参数是函数签名,包括参数和返回值。 38 | 39 | 3. 找到这个函数,并调用它 40 | 41 | ```c 42 | env->CallXXXMethod(jobject,mid,ret); 43 | ``` 44 | 45 | XXX代表函数的返回值类型,包括Void,Object,Boolean,Byte,Char,Short,Int,Long,Float和Double。 -------------------------------------------------------------------------------- /数据结构与算法/一致性Hash算法.md: -------------------------------------------------------------------------------- 1 | 哈希算法好坏的四个定义: 2 | 3 | 1、平衡性(Balance):平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用。很多哈希算法都能够满足这一条件。 4 | 5 | 2、单调性(Monotonicity):单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到原有的或者新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。 6 | 7 | 3、分散性(Spread):在分布式环境中,终端有可能看不到所有的缓冲,而是只能看到其中的一部分。当终端希望通过哈希过程将内容映射到缓冲上时,由于不同终端所见的缓冲范围有可能不同,从而导致哈希的结果不一致,最终的结果是相同的内容被不同的终端映射到不同的缓冲区中。这种情况显然是应该避免的,因为它导致相同内容被存储到不同缓冲中去,降低了系统存储的效率。分散性的定义就是上述情况发生的严重程度。好的哈希算法应能够尽量避免不一致的情况发生,也就是尽量降低分散性。 8 | 9 | 4、负载(Load):负载问题实际上是从另一个角度看待分散性问题。既然不同的终端可能将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同的内容。与分散性一样,这种情况也是应当避免的,因此好的哈希算法应能够尽量降低缓冲的负荷。 10 | 11 | 12 | 13 | 普通的哈希算法(也称硬哈希)采用简单取模的方式,但是当cache环境动态变化时,这种静态取模的方式显然就不满足单调性的要求。 14 | 15 | 一致性哈希算法的基本实现原理是将机器节点和key值都按照一样的hash算法映射到一个0~2^32的圆环上。当有一个写入缓存的请求到来时,计算Key值k对应的哈希值Hash(k),如果该值正好对应之前某个机器节点的Hash值,则直接写入该机器节点,如果没有对应的机器节点,则顺时针查找下一个节点,进行写入,如果超过2^32还没找到对应节点,则从0开始查找(因为是环状结构)。 16 | 17 | 参考:http://blog.huanghao.me/?p=14 18 | 19 | http://blog.csdn.net/cywosp/article/details/23397179 -------------------------------------------------------------------------------- /Android/ListView.md: -------------------------------------------------------------------------------- 1 | ## RecycleBin缓存机制 2 | 3 | ```java 4 | class RecycleBin { 5 | /** 6 | * The position of the first view stored in mActiveViews. 7 | */ 8 | private int mFirstActivePosition; 9 | 10 | /** 11 | * 储存屏幕上的View 12 | */ 13 | private View[] mActiveViews = new View[0]; 14 | 15 | /** 16 | * Unsorted views that can be used by the adapter as a convert view. 17 | * 每个item type对应一个ArrayList 18 | */ 19 | private ArrayList[] mScrapViews; 20 | 21 | private int mViewTypeCount; // item type 的个数 22 | } 23 | ``` 24 | 25 | RecyleBin实现了两级缓存: 26 | 27 | - `View[] mActiveViews`: 缓存屏幕上的View,在该缓存里的View不需要调用`getView()`。 28 | - `ArrayList[] mScrapViews;`: 每个Item Type对应一个列表作为回收站,缓存由于滚动而消失的View,此处的View如果被复用,会以参数的形式传给`getView()`。 29 | 30 | ## layout过程 31 | 32 | ![listview_layout](listview_layout.jpg) 33 | 34 | ## 滑动过程 35 | 36 | ![listview_scroll](listview_scroll.jpg) 37 | 38 | ## 参考 39 | 40 | - http://blog.csdn.net/guolin_blog/article/details/44996879 41 | - http://chuansong.me/n/1453225651817 -------------------------------------------------------------------------------- /Java/JDK/LinkedHashMap&LruCache.md: -------------------------------------------------------------------------------- 1 | - **1.**LruCache 是通过 LinkedHashMap 构造方法的第三个参数的 `accessOrder=true` 实现了 `LinkedHashMap` 的数据排序**基于访问顺序** (最近访问的数据会在链表尾部),在容量溢出的时候,将链表头部的数据移除。从而,实现了 LRU 数据缓存机制。 2 | - **2.**LruCache 在内部的get、put、remove包括 trimToSize 都是安全的(因为都上锁了)。 3 | - **3.**LruCache 自身并没有释放内存,将 LinkedHashMap 的数据移除了,如果数据还在别的地方被引用了,还是有泄漏问题,还需要手动释放内存。 4 | - **4.**覆写 `entryRemoved` 方法能知道 LruCache 数据移除是是否发生了冲突,也可以去手动释放资源。 5 | - **5.**`maxSize` 和 `sizeOf(K key, V value)` 方法的覆写息息相关,必须相同单位。( 比如 maxSize 是7MB,自定义的 sizeOf 计算每个数据大小的时候必须能算出与MB之间有联系的单位 ) 6 | 7 | 8 | 每次调用LinkedHashMap的get方法时,如果`accessOrder=true` ,那么它会调用`makeTail`方法,将entry移到链表的尾部。 9 | 10 | 11 | 12 | https://github.com/LittleFriendsGroup/AndroidSdkSourceAnalysis/blob/master/article/LruCache%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90.md 13 | 14 | 15 | 16 | 17 | 18 | **LinkedHashMap** = HashMap + 双向链表 19 | 20 | ```java 21 | private static class Entry extends HashMap.Entry { 22 | // These fields comprise the doubly linked list used for iteration. 23 | Entry before, after; 24 | 25 | Entry(int hash, K key, V value, HashMap.Entry next) { 26 | super(hash, key, value, next); 27 | } 28 | ... 29 | } 30 | ``` 31 | 32 | Entry拥有的属性: 33 | 34 | - **K key** 35 | - **V value** 36 | - **Entry next** 37 | - **int hash** 38 | - **Entry before** 39 | - **Entry after** -------------------------------------------------------------------------------- /Android/插件化/Atlas原理.md: -------------------------------------------------------------------------------- 1 | ### APK结构 2 | 3 | 使用Atlas框架的apk是由一个主apk和一堆业务模块apk(bundle)组成。在打包的过程中,gradle插件将所有bundle中AndroidManifest注册信息合并到主apk的AndroidManifest文件中。打包完成后,业务bundle名变为libxxx_xxx_xxx.so的形式,放在主apk的lib目录下。在系统安装apk时,会将bundle解压到系统app的lib目录下。![atlas_apk_struct](atlas_apk_struct.png) 4 | 5 | ### 类加载 6 | 7 | Atlas里面通常会创建了两种classLoader,第一个是DelegateClassLoader,他作为类查找的一个路由器而存在,本身并不负责真正类的加载;DelegateClassLoader启动时被atlas注入LoadedApk中,替换原有的PathClassLoader;第二个是BundleClassLoader,参考OSGI的实现,每个bundle resolve时会分配一个BundleClassLoader,负责该bundle的类加载。DelegateClassLoader以PathClassLoader为parent,同时在路由过程中能够找到所有bundle的classloader;BundleClassLoader以BootClassLoader为parent,同时引用PathClassLoader,BundleClassLoader自身findClass的顺序为 8 | 9 | 1. findOwn: 查找bundle dex 自身内部的class 10 | 11 | 12 | 2. findDependency: 查找bundle依赖的bundle内的class 13 | 14 | 15 | 3. findPath: 查找主apk中的class 16 | 17 | 例子: 18 | 19 | 1. ActivityThread从LoadedApk中获取classloader去load Activity Class; 20 | 2. 根据上面的classloader关系,先去parent里面加载class; 21 | 3. 由于class在bundle里面,所以pathclassloader内查找失败,接着delegateclassloader根据bundleinfo信息查找到classloader在bundle中(假设为bundleA); 22 | 4. 从bundleA中加载class,并且创建class; 23 | 5. 后面在Activity起来后,如果bundleA对bundleB有依赖关系,那么如果用到了bundleB的class,又会根据bundlA的bundleClassloader的dependency去获取bundleB的classloader去加载; 24 | 25 | ### 资源加载 26 | 27 | 类似ClassLoader,LoadedApk中的Resources被替换成Atlas内部的DelegateResources,同时在每个Bundle安装的过程中,每个bundle的assetspath会被更新到DelegateResources的AssetsManager中。 28 | 29 | 30 | 31 | ### 参考 32 | 33 | - [Atlas原理](https://lark.alipay.com/youku_android_arch/atlas/nltxv9) 34 | - [Atlas文档](https://alibaba.github.io/atlas/principle-intro/Runtime_principle.html) 35 | 36 | -------------------------------------------------------------------------------- /Java/JDK/ArrayList.md: -------------------------------------------------------------------------------- 1 | 1. ArrayList在每次增加元素(可能是1个,也可能是一组)时,都要调用该方法来确保足够的容量。当容量不足以容纳当前的元素个数时,就设置新的容量为旧的容量的1.5倍加1,如果设置后的新容量还不够,则直接新容量设置为传入的参数(也就是所需的容量),而后用Arrays.copyof()方法将元素拷贝到新的数组 2 | 2. 无参构造方法构造的ArrayList的容量默认为10 3 | 3. System.arraycopy()方法。该方法被标记了native,调用了系统的C/C++代码,在JDK中是看不到的,但在openJDK中可以看到其源码。该函数实际上最终调用了C语言的memmove()函数,因此它可以保证同一个数组内元素的正确复制和移动,比一般的复制方法的实现效率要高很多,很适合用来批量处理数组。Java强烈推荐在复制大量数组元素时用该方法,以取得更高的效率。 4 | 4. public static T[] copyOf(T[] original, int newLength) 5 | 6 | ```java 7 | // 快速删除第index个元素 8 | private void fastRemove(int index) { 9 | modCount++; 10 | int numMoved = size - index - 1; 11 | // 从"index+1"开始,用后面的元素替换前面的元素。 12 | if (numMoved > 0) 13 | System.arraycopy(elementData, index+1, elementData, index, 14 | numMoved); 15 | // 将最后一个元素设为null 16 | elementData[--size] = null; // Let gc do its work 17 | } 18 | 19 | // 确定ArrarList的容量。 20 | // 若ArrayList的容量不足以容纳当前的全部元素,设置 新的容量=“(原始容量x3)/2 + 1” 21 | public void ensureCapacity(int minCapacity) { 22 | // 将“修改统计数”+1,该变量主要是用来实现fail-fast机制的 23 | modCount++; 24 | int oldCapacity = elementData.length; 25 | // 若当前容量不足以容纳当前的元素个数,设置 新的容量=“(原始容量x3)/2 + 1” 26 | if (minCapacity > oldCapacity) { 27 | Object oldData[] = elementData; 28 | int newCapacity = (oldCapacity * 3)/2 + 1; 29 | //如果还不够,则直接将minCapacity设置为当前容量 30 | if (newCapacity < minCapacity) 31 | newCapacity = minCapacity; 32 | elementData = Arrays.copyOf(elementData, newCapacity); 33 | } 34 | } 35 | ``` 36 | 37 | 38 | 39 | http://blog.csdn.net/ns_code/article/details/35568011 -------------------------------------------------------------------------------- /网络基础/Socket.md: -------------------------------------------------------------------------------- 1 | ## 什么是Socket 2 | 3 | Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API)。Socket通常也称作”套接字”,用于描述IP地址和端口,是一个通信链的句柄。网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket。 4 | 5 | ## 使用TCP 6 | 7 | 8 | 客户端 9 | 10 | ```java 11 | Socket socket = new Socket("ip", 端口); 12 | 13 | InputStream is = socket.getInputStream(); 14 | DataInputStream dis = new DataInputStream(is); 15 | 16 | OutputStream os = socket.getOutputStream(); 17 | DataInputStream dos = new DataOutputStream(os); 18 | ``` 19 | 20 | 服务器端 21 | 22 | ```java 23 | ServerSocket serverSocket = new ServerSocket(端口); 24 | Socket socket = serverSocket.accept(); 25 | //获取流的方式与客户端一样 26 | ``` 27 | 28 | 读取输入流 29 | 30 | ```java 31 | byte[] buffer = new byte[1024]; 32 | do{ 33 | int count = is.read(buffer); 34 | if(count <= 0){ break; } 35 | else{ 36 | // 对buffer保存或者做些其他操作 37 | } 38 | } 39 | while(true); 40 | 41 | 42 | ``` 43 | 44 | ## 使用UDP 45 | 46 | 客户端和服务器端一样的 47 | 48 | ```java 49 | DatagramSocket socket = new DatagramSocket(端口); 50 | InetAddress serverAddress = InetAddress.getbyName("ip"); 51 | //发送 52 | DatagramPackage packet = new DatagramPacket(buffer, length, host, port); 53 | socket.send(packet); 54 | //接收 55 | byte[] buf = new byte[1024]; 56 | DatagramPacket packet = new DatagramPacket(buf, 1024); 57 | Socket.receive(packet); 58 | ``` 59 | 60 | ## 底层实现 61 | 62 | 一般来讲,我们可以认为TCP连接上发送的所有字节序列在某一瞬间被分成了3个FIFO队列: 63 | 64 | 1. SendQ:在发送端底层实现中缓存的字节,这些字节已经写入输出流,但还没在接收端成功接收。它占用大约37KB内存。 65 | 2. RecvQ:在接收端底层实现中缓存的字节,这些字节等待分配到接收程序——即从输入流中读取。它占用大约25KB内存。 66 | 3. Delivered:接收者从输入流已经读取到的字节。 67 | 68 | 当我们调用OutputStream的write()方法时,将向SendQ追加字节。 69 | 70 | TCP协议负责将字节按顺序从SendQ移动到RecvQ。 71 | 72 | 接收程序从Socket的InputStream读取数据时,字节就从RecvQ移动到Delivered中,而转移的块的大小依赖于RecvQ中的数据量和传递给read()方法的缓冲区的大小。 73 | 74 | ![socket底层实现](socket底层实现.jpg) 75 | 76 | 参考:http://blog.csdn.net/ns_code/article/details/15813809 -------------------------------------------------------------------------------- /网络基础/WebSocket.md: -------------------------------------------------------------------------------- 1 | ## What WebSocket 2 | 3 | 首先,Websocket是一个**持久化**的协议,相对于HTTP这种**非持久**的协议来说。 4 | 1) HTTP的生命周期通过Request来界定,也就是一个Request 一个Response,那么**在**HTTP1.0**中**,这次HTTP请求就结束了。 5 | 在HTTP1.1中进行了改进,使得有一个keep-alive,也就是说,在一个HTTP连接中,可以发送多个Request,接收多个Response。 6 | 但Http里面,request与response是一一对应的,而且先有Request才会有Response,这个response是**被动**的,不能主动发起。 7 | 8 | WebSocket能实现服务端与客户端之间的全双工通信。WebSocket通信开始时需要借助HTTP来实现握手操作。 9 | 10 | 11 | 12 | ## Why WebSocket 13 | 14 | 在WebSocket之前,有两种方式来实现服务端向客户端推送数据。 15 | 16 | - Ajax 轮询:以频繁请求方式来保持客户端和服务端的同步 17 | 18 | ![Ajax轮询](Ajax轮询.png) 19 | 20 | - 长轮询:当服务端没有数据更新的时候,连接会保持一段时间周期知道数据或者状态改变或者过期,依次减少无效的客户端和服务端的交互。 21 | 22 | ![长轮询](长轮询.png) 23 | 24 | Websocket只需要**一次HTTP握手,所以说整个通讯过程是建立在一次连接状态中**,也就避免了HTTP的非状态性,服务端会一直知道你的信息,直到你关闭请求。 25 | 26 | 27 | 28 | ## How WebSocket 29 | 30 | - 客户端发起http请求,附加头信息为:“Upgrade Websocket” 31 | 32 | ``` 33 | GET /chat HTTP/1.1 34 | Host: server.example.com 35 | Upgrade: websocket 36 | Connection: Upgrade 37 | Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== 38 | Sec-WebSocket-Protocol: chat, superchat 39 | Sec-WebSocket-Version: 13 40 | Origin: http://example.com 41 | ``` 42 | 43 | ![websocket_request_header](websocket_request_header.png) 44 | 45 | - 服务端接收请求,建立连接 46 | 47 | ``` 48 | HTTP/1.1 101 Switching Protocols 49 | Upgrade: websocket 50 | Connection: Upgrade 51 | Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= 52 | Sec-WebSocket-Protocol: chat 53 | ``` 54 | 55 | 其实我们所用的程序是要经过两层代理的,即**HTTP协议在Nginx等服务器的解析下**,然后再传送给相应的**Handler(PHP等)**来处理。 56 | 简单地说,我们有一个非常快速的接**线员(Nginx)**,他负责把问题转交给相应的**客服(Handler)**。 57 | 本身**接线员基本上速度是足够的**,但是每次都卡在**客服(Handler)**了,老有**客服**处理速度太慢。,导致客服不够。 58 | Websocket就解决了这样一个难题,建立后,可以直接跟接线员建立持**久连接**,有信息的时候客服想办法通知接线员,然后**接线员**在统一转交给客户。 59 | 60 | 因此,WebSocket协议需要Web服务器的支持。 61 | 62 | 63 | 64 | ## 参考 65 | 66 | - https://www.zhihu.com/question/20215561 67 | - https://zhuanlan.zhihu.com/p/23386938 68 | - http://www.jianshu.com/p/bcefda55bce4# -------------------------------------------------------------------------------- /Android/Retrofit框架思路浅析.md: -------------------------------------------------------------------------------- 1 | ## 基本用法 2 | 3 | ```java 4 | public interface GitHubService { 5 | @GET("users/{user}/repos") 6 | Call> listRepos(@Path("user") String user); 7 | } 8 | Retrofit retrofit = new Retrofit.Builder() 9 | .baseUrl("https://api.github.com/") 10 | .build(); 11 | 12 | GitHubService service = retrofit.create(GitHubService.class); 13 | Call> repos = service.listRepos("octocat"); 14 | ``` 15 | 16 | ## 核心原理 17 | 18 | ```java 19 | @SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety. 20 | public T create(final Class service) { 21 | Utils.validateServiceInterface(service); 22 | if (validateEagerly) { 23 | eagerlyValidateMethods(service); 24 | } 25 | return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class[] { service }, 26 | new InvocationHandler() { 27 | private final Platform platform = Platform.get(); 28 | 29 | @Override public Object invoke(Object proxy, Method method, Object... args) 30 | throws Throwable { 31 | // If the method is a method from Object then defer to normal invocation. 32 | //来自Object类的方法,如toString() 33 | if (method.getDeclaringClass() == Object.class) { 34 | return method.invoke(this, args); 35 | } 36 | if (platform.isDefaultMethod(method)) { 37 | return platform.invokeDefaultMethod(method, service, proxy, args); 38 | } 39 | //每个接口方法都对应一个ServiceMethod,将接口方法的注解解析成Http请求需要配置参数,并缓存下来 40 | //内部含有ResponseConverter、CallAdapter 41 | ServiceMethod serviceMethod = loadServiceMethod(method); 42 | //封装了一个Http请求,OkHttpCall是对OkHttp的封装 43 | OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args); 44 | //近CallAdapter适配Call,并返回适配后的产物 45 | return serviceMethod.callAdapter.adapt(okHttpCall); 46 | } 47 | }); 48 | } 49 | ``` 50 | 51 | ## 图解 52 | 53 | ![Retrofit框架](Retrofit框架.png) 54 | 55 | ## 参考&拓展 56 | 57 | - [拆轮子系列:拆 Retrofit](http://blog.piasy.com/2016/06/25/Understand-Retrofit/) 58 | - [Retrofit分析-漂亮的解耦套路](http://www.jianshu.com/p/45cb536be2f4#) 59 | - [Retrofit分析-经典设计模式案例](http://www.jianshu.com/p/fb8d21978e38) 60 | 61 | -------------------------------------------------------------------------------- /Java/多线程/深入ThreadLocal.md: -------------------------------------------------------------------------------- 1 | ```java 2 | public class ThreadLocal { 3 | public T get() { 4 | Thread t = Thread.currentThread(); 5 | //从当前的Thread对象中取出ThreadLocalMap成员,key是ThreadLocal,value是set的值。 6 | ThreadLocalMap map = getMap(t); 7 | if (map != null) { 8 | ThreadLocalMap.Entry e = map.getEntry(this); 9 | if (e != null) 10 | return (T)e.value; 11 | } 12 | return setInitialValue(); 13 | } 14 | 15 | public void set(T value) { 16 | Thread t = Thread.currentThread(); 17 | //从当前的Thread对象中取出ThreadLocalMap成员,key是ThreadLocal,value是set的值。 18 | ThreadLocalMap map = getMap(t); 19 | if (map != null) 20 | map.set(this, value); 21 | else 22 | createMap(t, value); 23 | } 24 | 25 | ThreadLocalMap getMap(Thread t) { 26 | return t.threadLocals; 27 | } 28 | 29 | } 30 | 31 | //threadLocals变量存放在Thread对象中 32 | public class Thread{ 33 | /* ThreadLocal values pertaining to this thread. This map is maintained 34 | * by the ThreadLocal class. */ 35 | ThreadLocal.ThreadLocalMap threadLocals = null; 36 | 37 | /* 38 | * InheritableThreadLocal values pertaining to this thread. This map is 39 | * maintained by the InheritableThreadLocal class. 40 | */ 41 | ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; 42 | 43 | ... 44 | } 45 | ``` 46 | 47 | 每个ThreadLocal对象内部都没有保存Value对象,而是将Value存到调用线程所属的ThreadLocalMap里面,ThreadLocalMap的key为ThreadLocal,value就是线程本地变量。ThreadLocalMap对ThreadLocal的引用是弱引用。 48 | 49 | ```java 50 | static class Entry extends WeakReference> { 51 | /** The value associated with this ThreadLocal. */ 52 | Object value; 53 | Entry(ThreadLocal k, Object v) { 54 | super(k); 55 | value = v; 56 | } 57 | } 58 | ``` 59 | 60 | 61 | 62 | 为什么不构造一个Map,以Thread对象为key呢?原因有二: 63 | 1. 集合类强引用Thread对象容易造成内存泄漏。 64 | 2. 需要处理线程同步问题,加锁会有性能开销。 65 | 66 | 67 | ThreadLocalMap采用的开放地址法来解决key的冲突问题,而不是HashMap的链地址法。 68 | 为什么采用开发地址法呢?我的理解是:一个Thread对象的线程私有变量一般不会太多,因此用开放地址法来解决冲突可以提高hash map的空间利用率。 69 | 70 | 总的来说,对于多线程资源共享的问题,同步机制采用了“**以时间换空间**”的方式,而ThreadLocal采用了“**以空间换时间**”的方式。前者仅提供一份变量,让不同的线程排队访问;而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。 71 | 72 | 73 | 74 | http://www.sczyh30.com/posts/Java/java-concurrent-threadlocal/ -------------------------------------------------------------------------------- /网络基础/推送PSUH总结.md: -------------------------------------------------------------------------------- 1 | ## 推送的实现方式 2 | 3 | - 轮询(Pull)方式。 4 | 5 | 客户端定时轮询服务器,拉取消息。 6 | 7 | 短轮询:客户端发起请求,服务端立即响应; 8 | 9 | 长轮询:客户端发起请求,如果此时服务端没有消息就会hold住该连接,等有消息时才将响应返回给客户端。 10 | 11 | 缺点:不适合客户端比较多的场景;短轮询的实时性不好; 12 | 13 | 缺点:耗电,实时性低 14 | 15 | - SMS(Push)方式 16 | 17 | 服务器在有新消息时给用户的手机号发一条特殊的短信,客户端通过拦截这条短信获取服务器推送的消息。 18 | 19 | 缺点:运营商不配合,安全性不高 20 | 21 | - 长连接(Push) 22 | 23 | 应用程序与服务器保持一条长连接,服务器可以通过这条连接将消息推送到客户端。 24 | 25 | **WebSocket** 26 | 27 | 浏览器发起一个http请求给服务端,请求切换为WebSocket协议,服务端响应后,确认使用WebSocket协议。之后数据就可以在tcp连接上双向发送数据了。 28 | 29 | 优点: WebSocket实时性强,因为协议是双工协议,所以,服务端可以随时给客户端下发数据,比起轮询,少了客户端发起请求,延时更小。 30 | 缺点: 对浏览器和服务器的要求较高,稳定性有待提高。 31 | 32 | ## TCP长连接 33 | 34 | 长连接是相对于短连接而言的。短连接是通讯双方有数据交互时就建立一个连接,数据发送完成后,则断开连接。长连接就是建立连接后,连接不主动断开,双方的数据都通过这条连接发送。 35 | 36 | 一般来说,只要双方都不主动关闭连接,TCP长连接就会一直存在。但网络的环境总是复杂变化着的,有不少因素会影响到TCP长连接的寿命。 37 | 38 | ### NAT超时 39 | 40 | 为了解决IPV4地址短缺的问题,引入NAT协议来解决公有IP和私有IP相互转换的问题。一般来说,我们的通信设备所连接的路由器自带有NAT协议的功能,内部维护一张NAT映射表,里面包含了内网地址和外网地址的映射。当一个客户端将数据包发给路由器,路由器先将数据包的源IP地址修改成外网IP地址再转发出去,最终到达web服务器。而web服务器给客户端的响应或推送消息到达路由器时,路由器查询NAT映射表,将数据包的目的IP地址用所对应的内网IP地址替换,然后转发给内网中对应的客户端。 41 | 42 | 参考youtube视频:[How Network Address Translation Works](https://www.youtube.com/watch?v=QBqPzHEDzvo) 43 | 44 | 然而,成也NAT映射表,败也NAT映射表。国内移动无线网络运营商在链路一段时间内没有数据通讯后,会淘汰NAT表中的对应映射项,映射关系没了,服务器自然也就无法通过原来的IP地址将消息推送给客户端,长连接也就中断了。 45 | 46 | ### DHCP租期 47 | 48 | 当DHCP租期过期了,会导致老的TCP连接不能正常收发数据。 49 | 50 | 参考youtube视频:[Automatic IP Address Assignment: How DHCP Works](https://www.youtube.com/watch?v=RUZohsAxPxQ) 51 | 52 | ### 网络状态变化 53 | 54 | 手机数据网络和WIFI网络切换,网络断开和重连等网络变化也会使长连接变为无效连接,重新建立Push长连接。 55 | 56 | ## 长连接的实现 57 | 58 | ### GCM云端推送 59 | 60 | 在应用外的消息栏推送中,iOS可以使用苹果的APN服务,开发者只要注册后就可以通过APN服务推送自己app的通知消息。Android目前可以使用google类似APN的GCM服务(在国内因为某些不可抗力不能使用,可以使用华为,小米等厂商提供的推送服务),向终端推送通知。 61 | 62 | ![GCM](GCM.jpg) 63 | 64 | ### 使用第三方推送平台 65 | 66 | 推送平台:极光,信鸽,一推等等 67 | 68 | ### XMPP,MQTT即时通信协议 69 | 70 | XMPP,MQTT都是工作在应用层的即时通信协议。 71 | 72 | ### 设计心跳包 73 | 74 | 其实,TCP长连接的维持并不需要心跳包。心跳包的真正作用是防止NAT映射表中长连接对应的地址映射被淘汰,从而达到延长TCP长连接寿命的目的。 75 | 76 | 心跳包的时间间隔需要根据网络NAT超时间隔来设计,而wifi和移动3/4G的NAT超时间隔都不一样。还需要兼顾耗电和消息推送实时性等方面的权衡。 77 | 78 | 参考:[Android微信智能心跳方案](https://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=207243549&idx=1&sn=4ebe4beb8123f1b5ab58810ac8bc5994) 79 | 80 | ## 参考&拓展 81 | 82 | - [Android推送、智能心跳解决方案、手机休眠对心跳的影响](http://www.voidcn.com/blog/xx326664162/article/p-6004761.html) 83 | - [Android微信智能心跳方案](https://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=207243549&idx=1&sn=4ebe4beb8123f1b5ab58810ac8bc5994) 84 | - [Android推送技术研究](http://www.jianshu.com/p/584707554ed7#) 85 | 86 | -------------------------------------------------------------------------------- /Android/Android内存泄漏.md: -------------------------------------------------------------------------------- 1 | ## Java内存分配策略 2 | 3 | Java 程序运行时的内存分配策略有三种,分别是静态分配,栈式分配,和堆式分配,对应的,三种存储策略使用的内存空间主要分别是静态存储区(也称方法区)、栈区和堆区。 4 | 5 | - 静态存储区(方法区):主要存放静态数据、全局 static 数据和常量。这块内存在程序编译时就已经分配好,并且在程序整个运行期间都存在。 6 | - 栈区 :当方法被执行时,方法体内的局部变量都在栈上创建,并在方法执行结束时这些局部变量所持有的内存将会自动被释放。因为栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 7 | - 堆区 : 又称动态内存分配,通常就是指在程序运行时直接 new 出来的内存。这部分内存在不使用时将会由 Java 垃圾回收器来负责回收。 8 | 9 | ## 常见内存泄漏的场景 10 | 11 | - 集合类泄漏。 12 | - 单例模式中,单例引用的对象的生命周期与应用的生命周期不一致。 13 | - 非静态内部类持有外部类的引用。 14 | 15 | ```java 16 | public class MainActivity extends AppCompatActivity { 17 | private static TestResource mResource = null; 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | setContentView(R.layout.activity_main); 22 | if(mManager == null){ 23 | mManager = new TestResource(); 24 | } 25 | //... 26 | } 27 | class TestResource { 28 | //... 29 | } 30 | } 31 | ``` 32 | 33 | TestResource是Activity的非静态内部类,持有了Activity的引用。假如mResource对象在Activity的生命周期外仍被其他对象引用,那么就会导致Activity无法被回收,造成内存泄漏。 34 | 35 | - 匿名内部类也会持有外部类的引用。 36 | 37 | ```java 38 | public class MainActivity extends Activity { 39 | ... 40 | // 应该这样 41 | Runnable ref1 = new MyRunable(); 42 | // 不应该这样 43 | Runnable ref2 = new Runnable() { 44 | @Override 45 | public void run() { 46 | 47 | } 48 | }; 49 | // 可以这样 50 | static Runnable ref3 = new Runnable() { 51 | @Override 52 | public void run() { 53 | 54 | } 55 | }; 56 | ... 57 | } 58 | ``` 59 | 60 | - Handler 造成内存泄漏。解决方法是:在Activity中避免使用非静态内部类,Handler通过弱引用来引用Activity。 61 | 62 | ```java 63 | public class SampleActivity extends Activity { 64 | 65 | /** 66 | * Instances of static inner classes do not hold an implicit 67 | * reference to their outer class. 68 | */ 69 | private static class MyHandler extends Handler { 70 | private final WeakReference mActivity; 71 | 72 | public MyHandler(SampleActivity activity) { 73 | mActivity = new WeakReference(activity); 74 | } 75 | 76 | @Override 77 | public void handleMessage(Message msg) { 78 | SampleActivity activity = mActivity.get(); 79 | if (activity != null) { 80 | // ... 81 | } 82 | } 83 | } 84 | ``` 85 | 86 | Java四种引用的区别: 87 | 88 | ![Java的四种引用](Java的四种引用.jpg) 89 | 90 | 91 | 92 | **LeakCanary的原理** 93 | 94 | ReferenceQueue+WeakReference+手动调用GC 95 | 96 | WeakReference 创建时,传入一个 ReferenceQueue 对象。当被 WeakReference 引用的对象的生命周期结束,一旦被 GC 检查到,GC 将会把该对象添加到 ReferenceQueue 中,待ReferenceQueue处理。当 GC 过后对象一直不被加入 ReferenceQueue,它可能存在内存泄漏。 -------------------------------------------------------------------------------- /Java/JVM/JVM内存分区.md: -------------------------------------------------------------------------------- 1 | ## JVM内存结构 2 | ![JVM内存结构](JVM内存结构.png) 3 | 4 | ### 程序计数器 5 | 6 | PC是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。为了线程切换后能恢复到正确执行位置,每条线程都需要有一个独立的程序计数器。各条线程之间计数器互不影响。如果正在执行的是Native方法,这个PC值为Undefined。 7 | 8 | ### VM Stack 9 | 10 | Java虚拟机栈也是线程私有的。Stack描述的是Java方法执行的内存模型:每一个方法执行时都会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接表,方法出口等信息。 11 | 12 | 局部变量表存放了编译期可知的各种基本数据类型和对象引用。其中64位长度long和double类型的数据会占用2个Slot,其余类型只占用1个。局部变量表所需的内存空间在编译期已经决定,因此在方法运行期间不会改变局部变量表的大小。 13 | 14 | ### 本地方法栈 15 | 16 | 虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则为虚拟机使用到的Native方法服务。 17 | 18 | ### Java堆 19 | 20 | Java堆是被所有线程共享的一块内存区域,在JVM启动时创建。此内存区域唯一目的是存放对象实例,几乎所有的对象实例都是在这里分配内存。Java堆可以分为新生代和老年代;在细致一点有Eden空间,From Survivor空间,To Survivor空间等。线程共享的Java堆可能划分出多个线程私有的分配缓冲区(TLAB)。 21 | 22 | ### 方法区 23 | 24 | 方法区与Java堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息,常量,静态变量等。 25 | 26 | **运行时常量池**是方法区的一部分。Class文件中除了有类的描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,在类加载后进入方法区的运行时常量池中存放。在运行期间也可以将新的常量放入池中,比如String类的intern方法。 27 | 28 | ## 对象 29 | ### 对象的创建 30 | 31 | 虚拟机遇到一条new指令时,首先在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已被加载,解析和初始化过。如果没有那必须先执行相应的类加载的过程。 32 | 33 | 对象所需内存的大小在类加载完成后便可完全确认,在堆中分配内存的方式有: 34 | 35 | 1. 指针碰撞:假设Java堆中内存是绝对规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放一个指针作为分界点指示器。分配内存就仅仅把那个指针向空闲空间那边挪动一段与对象大小相等的距离。 36 | 2. 空闲列表:如果堆内存不是规整的,那么需要维护一个表,记录哪些内存块是可用的,在分配时在列表中找到一块足够大的空间划分给对象实例,记录哪些内存块是可用的。 37 | 38 | 在堆中为对象分配空间时候会有并发的问题,解决方法有: 39 | 40 | 1. 采用CAS配上失败重试的方式保证更新操作的原子性。 41 | 2. 每个线程在Java堆中预先分配一块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)。 42 | 43 | 执行new指令之后会接着执行``方法,把对象按照程序员的意愿进行初始化。 44 | 45 | ### 对象的内存布局 46 | 47 | 对象在内存中存储的布局可以分为3个区域:对象头(Header),实例数据(Instance Data)和对齐填充(Padding)。 48 | 49 | 对象头分为: 50 | 51 | 1. 用于存储对象自身的运行时数据,如HashCode,GC分代年龄,锁状态标志等。 52 | 2. 类型指针,用于确定这个对象是那个类的实例。 53 | 54 | 对齐填充: 55 | 56 | JVM规定对象的大小必须是8字节的整数倍。而对象头正好是8字节的倍数,如果对象实例数据部分不足8字节的整数倍,通过对齐填充来补全。 57 | 58 | ![对象的内存布局](对象的内存布局.png) 59 | 60 | ### 对象的访问定位 61 | 62 | - 使用句柄访问 63 | 64 | Java堆中会划分出一块内存作为句柄池,reference存储的是对象的句柄地址,而句柄中包含了对象实例数据和地址信息。 65 | 66 | 好处就是在对象被移动时只会改变句柄中实例数据的指针,而reference本身不必修改。 67 | 68 | - 直接指针访问 69 | 70 | reference直接存储对象的地址。 71 | 72 | 好处就是速度更快,节省了一次指针定位的开销,但对象地址发生改变时需要频繁改变reference的值。 73 | 74 | 75 | 76 | ## 谈谈String 77 | 78 | String.intern()是一个Native方法,它的作用是,如果字符串常量池中已经包含该String对象的字符串,则返回代表池中这个字符串的String对象;否则将此String对象包含的字符串添加到常量池中,并返回此String对象的引用。 79 | 80 | ```java 81 | public class Test { 82 | static String a = "ab"; 83 | public static void main(String[] args) { 84 | String s1 = "a"; 85 | String s2 = "b"; 86 | String s3 = s1 + s2; 87 | String s4 = "a1"; 88 | String s5 = "a" + "1"; 89 | System.out.println(s3 == a); // false 90 | System.out.println(s3.intern() == a); // true 91 | System.out.println(s4 == s5); // true 92 | } 93 | } 94 | ``` 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /Android/性能优化总结.md: -------------------------------------------------------------------------------- 1 | ### 性能优化的工具 2 | 3 | - TraceView:最好用也是最强大的性能跟踪工具。 4 | - SysTrace:主要用于查看UI的绘制问题。 5 | - Trace OpenGL:可以录制每一帧的绘制过程,逐个绘制命令查看。 6 | - Threads:显示所有线程信息。 7 | - Heap:堆的分配和使用信息。 8 | - AlloCation Tracker:用于内存分配跟踪,也是个调试性能的强大工具。 9 | - HierarchyViewer:用于查看层级以及一些耗时等。 10 | 11 | ### 关于对onDraw()的优化 12 | 13 | 1. 如果在onDraw里面执行内存分配的操作,会容易导致内存抖动,频繁触发GC。 14 | 2. 尽量使用ClipRect等方法来避免overdraw 15 | 3. 对于不在屏幕上的元素,可以使用canvas.quickReject把他们剔除 16 | 4. 对于不透明的View,显示它只需要渲染一次即可,可是如果这个View设置了alpha值,会至少需要渲染两次。原因是包含alpha的view需要事先知道混合View的下一层元素是什么,然后再结合上层的View进行Blend混色处理。 17 | 18 | ### 关于图片加载的优化 19 | 20 | 1. 常见的png,jpeg,webp等格式的图片在设置到UI上之前需要经过解码的过程,而解压时可以选择不同的解码率,不同的解码率对内存的占用是有很大差别的。在不影响到画质的前提下尽量减少内存的占用,这能够显著提升应用程序的性能。 21 | 2. 相比起JPEG,PNG能够提供更加清晰无损的图片,但是PNG格式的图片会更大,占用更多的磁盘空间。 22 | 3. 使用inBitmap属性可以告知Bitmap解码器去尝试使用已经存在的内存区域,新解码的bitmap会尝试去使用之前那张bitmap在heap中所占据的pixel data内存区域,而不是去问内存重新申请一块区域来存放bitmap。 23 | 24 | ### apk安装包瘦身 25 | 26 | - 减少图片资源的大小 27 | - 在build.gradle文件中开启`minifEnabled`与`shrinkResources`的属性,移除那些在程序中使用不到的代码 28 | - 有选择性的提供对应分辨率的图片资源 29 | - 使用代码的方式对已有的资源进行复用 30 | - 减少程序代码量 31 | - 开启MinifEnabled,Proguard。打开这些编译属性之后,程序在打包的时候就不会把没有引用到的代码编译进来,以此达到减少安装包大小的目的。 32 | 33 | ### UI优化 34 | 35 | 1. RelativeLayouts经常需要measure所有子节点两次才能把子节点合理的布局。如果子节点设置了weights属性,LinearLayouts也需要measure这些节点两次,才能获得精确的展示尺寸。如果LinearLayouts或者RelativeLayouts被套嵌使用,measure所费时间可能会呈指数级增长。 36 | 2. 理想情况下,总共的measure,layout,draw时间应该被很好的控制在16ms以内,以保证滑动屏幕时UI的流畅。 37 | 3. overdraw 重复绘制 38 | 4. GPU渲染 测量丢帧,卡顿 39 | 5. 使用**Systrace**工具定位jank问题。参考:https://zhuanlan.zhihu.com/p/27065828 40 | 41 | ![UI优化](UI优化.png) 42 | 43 | #### OverDraw 44 | 45 | 打开手机开发者选项中的Show GPU OverDraw,可以发现屏幕中有不同色块。颜色越深越红的色块就是过度绘制最严重的区域。 46 | 47 | 解决方案: 48 | 49 | - 移除不必要的background。容易被忽视的点是ListView中ImageView的背景和Activity中mDecorView中的背景。后者通过调用`mDecor.setWindowBackground(drawable)`来去除背景。 50 | - 使用`canvas.clipRect`将多余的部分剪切掉。 51 | 52 | 53 | 54 | #### 减小不必要的层次 55 | 56 | 使用Hierarchy Viewer工具可以看到分别看到View 的measure,layout,draw的时间。 57 | 58 | 使用include,merge标签减少嵌套布局的层数,使用ViewStub对View进行懒加载。 59 | 60 | http://blog.csdn.net/lmj623565791/article/details/45556391/ 61 | 62 | #### 卡顿检测 63 | 64 | - 利用Looper#loop方法中消息处理的时长来判断是否发生卡顿 65 | 66 | ```java 67 | public static void loop() { 68 | final Looper me = myLooper(); 69 | final MessageQueue queue = me.mQueue; 70 | // ... 71 | for (;;) { 72 | Message msg = queue.next(); // might block 73 | // This must be in a local variable, in case a UI event sets the logger 74 | Printer logging = me.mLogging; 75 | if (logging != null) { 76 | logging.println(">>>>> Dispatching to " + msg.target + " " + 77 | msg.callback + ": " + msg.what); 78 | } 79 | // focus 80 | msg.target.dispatchMessage(msg); 81 | if (logging != null) { 82 | logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); 83 | } 84 | // ... 85 | } 86 | } 87 | ``` 88 | 89 | - 利用Choreographer 90 | 91 | 参考:[Android UI性能优化 检测应用中的UI卡顿](http://blog.csdn.net/lmj623565791/article/details/58626355) -------------------------------------------------------------------------------- /Java/基础/Java异常处理.md: -------------------------------------------------------------------------------- 1 | ## Java异常的层次结构 2 | 3 | ![Java异常](Java异常.jpg) 4 | 5 | **Throwable:** 它是Java所有异常的一个共同祖先。有三个重要方法:`getCause`,`getMeage` ,`printStackTrace`。 6 | 7 | **Error:** 程序无法处理的错误,表示代码运行时JVM出现错误。如OutOfMemoryError,StackOverFlowError等。 8 | 9 | **Exception:** 程序可处理的错误。 10 | 11 | Java异常可以分为可查异常和不可查异常。 12 | 13 | **可查异常 Checked Exception:** 编译器要求必须处理的异常。除了RuntimeException及其子类以外,其他的Exception类及其子类都属于可查异常。这种异常的特点是Java编译器会检查它,也就是说,当程序中可能出现这类异常,要么用try-catch语句捕获它,要么用throws子句声明抛出它,否则编译不会通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。 14 | 15 | **运行时异常 RuntimeException:** 都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。 16 | 17 | 18 | 19 | 参考:http://blog.csdn.net/hguisu/article/details/6155636 20 | 21 | 22 | 23 | ## JVM异常处理的原理 24 | 25 | ```java 26 | public class Main { 27 | public static void main(String[] args) { 28 | int a = 1; 29 | int b = 3; 30 | try { 31 | b = a + b; 32 | return; 33 | } catch (RuntimeException e1) { 34 | b = a - b; 35 | } catch (Exception e2) { 36 | b = a * b; 37 | } finally { 38 | a = 0; 39 | } 40 | } 41 | } 42 | ``` 43 | 44 | ``` 45 | public static void main(java.lang.String[]); 46 | Code: 47 | 0: iconst_1 48 | 1: istore_1 // 给局部变量表1号slot赋值为1,代表a 49 | 2: iconst_3 50 | 3: istore_2 // 给局部变量表2号slot赋值为3,代表b 51 | 4: iload_1 // try块开始! 52 | 5: iload_2 53 | 6: iadd 54 | 7: istore_2 // 指令4-7 完成了操作:b = a + b 55 | 8: iconst_0 // finally块开始! 56 | 9: istore_1 // 指令8-9 完成操作a=0 57 | 10: return // 函数执行完毕,返回 58 | 11: astore_3 // RuntimeException e1异常处理块开始! 59 | 12: iload_1 60 | 13: iload_2 61 | 14: isub 62 | 15: istore_2 63 | 16: iconst_0 // finally处理块 64 | 17: istore_1 65 | 18: goto 38 // RuntimeException e1异常处理块结束! 66 | 21: astore_3 // Exception e2异常处理块开始! 67 | 22: iload_1 68 | 23: iload_2 69 | 24: imul 70 | 25: istore_2 71 | 26: iconst_0 // finally处理块 72 | 27: istore_1 73 | 28: goto 38 // Exception e2 异常处理块结束! 74 | 31: astore 4 // 其他任何异常处理块 75 | 33: iconst_0 76 | 34: istore_1 77 | 35: aload 4 78 | 37: athrow // 往将异常外抛 79 | 38: return 80 | Exception table: // 异常表 81 | from to target type 82 | 4 8 11 Class java/lang/RuntimeException //4-8 号指令中,碰到 NullPointerException时,跳到 11 号指令 83 | 4 8 21 Class java/lang/Exception 84 | 4 8 31 any 85 | 11 16 31 any 86 | 21 26 31 any 87 | 31 33 31 any 88 | } 89 | ``` 90 | 91 | try 的范围就是体现在异常表行记录的起点和终点。JVM 在 try 住的代码区间内如有异常抛出的话,就会在当前栈桢的异常表中,找到匹配类型的异常记录的入口指令号,然后跳到该指令处执行。异常指令块执行完后,再回来继续执行后面的代码。JVM 按照每个入口在表中出现的顺序进行检索,如果没有发现匹配的项,JVM 将当前栈帧从栈中弹出,再次抛出同样的异常。当 JVM 弹出当前栈帧时,JVM 马上终止当前方法的执行,并且返回到调用本方法的方法中,但是并非继续正常执行该方法,而是在该方法中抛出同样的异常,这就使得 JVM 在该方法中再次执行同样的搜寻异常表的操作。 92 | 93 | ![JVM异常处理](JVM异常处理.png) -------------------------------------------------------------------------------- /Java/基础/select&poll&epoll.md: -------------------------------------------------------------------------------- 1 | ## 背景知识 2 | 3 | ### 文件描述符fd 4 | 5 | 文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述**指向文件的引用**的抽象化概念。 6 | 7 | 文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。在Linux系统中,流在内核中可以表示成文件的形式。 8 | 9 | ### IO模型 10 | 11 | IO可以理解成对流的操作。 12 | 13 | 一般对于一个read操作发生时,它会经历两个阶段。 14 | 15 | - 第一个阶段是等待数据准备。 16 | - 第二个阶段是真正读取的过程,将数据从内核缓冲区拷贝到用户进程缓冲区中, 17 | 18 | 而五种常见的IO模型也是围绕这两个阶段来区分的。 19 | 20 | - **同步模型(synchronous IO)** 21 | - 阻塞IO(bloking IO) 22 | - 非阻塞IO(non-blocking IO) 23 | - 多路复用IO(multiplexing IO) 24 | - 信号驱动式IO(signal-driven IO) 25 | - **异步IO(asynchronous IO)** 26 | 27 | 其中,IO多路复用就是一种机制,实现一个进程可以监视多个描述符,一旦某个描述符就绪,就能够通知程序进行相应的读写操作。IO多路复用相比于多线程的优势在于系统的开销小,系统不必创建和维护进程或线程,免去了线程或进程的切换带来的开销。而操作系统支持IO多路复用的系统调用有select,poll和epoll。 28 | 29 | ## select 30 | 31 | 先来看看select的函数声明: 32 | 33 | ```c 34 | int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); 35 | ``` 36 | 37 | `fd_set`是表示文件描述符集合的数据结构。readfds,writefds和exceptfds分别对应三类文件描述符集。当select被调用时,内部逻辑如下: 38 | 39 | 1. 将3个fd集copy到内核,这里限制了fd最大数量为1024 40 | 2. 线程阻塞,直到超时或内核检测到有fd可读或可写,内核会通知监控者select,select返回可读或可写的fd总数 41 | 3. 那么用户进程如何找到可读可写的fd呢?select会将之前传递给内核的fd集从内核copy到用户进程。用户进程通过遍历的方式找到可读可写的fd。 42 | 43 | 缺点: 44 | 45 | 1. copy次数过多,而且每次调用select方法都要进行fd集的copy操作 46 | 2. select监控fd数量有限 47 | 3. 用户进程通过遍历的方式找到可读写的fd,时间复杂度为o(n),IO效率随着fd数量增多而线性下降 48 | 49 | ## poll 50 | 51 | 先来看看poll的函数声明: 52 | 53 | ```c 54 | int poll (struct pollfd *fds, unsigned int nfds, int timeout); 55 | ``` 56 | 57 | pollfd是表示文件描述符集合的数据结构。 58 | 59 | ```c 60 | struct pollfd { 61 | int fd; //文件描述符 62 | short events; //监视的请求事件 63 | short revents; //已发生的事件 64 | }; 65 | ``` 66 | 67 | poll与select差不多,但poll的pollfd没有最大数量的限制,可是IO效率依旧没有提升orz。 68 | 69 | ## epoll 70 | 71 | select/poll都只有一个方法,而epoll的操作过程有3个方法,分别是`epoll_create()`, `epoll_ctl()`,`epoll_wait()`。 72 | 73 | ### epoll_create() 74 | 75 | ```c 76 | int epoll_create(int size);//用于创建一个epoll的句柄,size是指监听的描述符个数。 77 | ``` 78 | 79 | 该方法会在内核创建专属于epoll的高速cache区,并在该缓冲区建立红黑树和就绪链表,用户态传入的文件句柄将被放到红黑树中。 80 | 81 | ### epoll_ctl() 82 | 83 | ```c 84 | int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 85 | ``` 86 | 87 | 该方法对`epoll_create()`所创建的内核cache区进行操作的,操作对象是需要监听的fd。 88 | 89 | 比如,把要监听的fd注册到cache内,那么`epoll_ctl()`会将fd插入到红黑树中,并向内核注册了该fd的回调函数。内核在检测到某fd可读可写时则调用该回调函数,而回调函数的工作是将fd放到就绪链表。 90 | 91 | ### epoll_wait() 92 | 93 | ```c 94 | int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout); 95 | ``` 96 | 97 | `epoll_wait`只需监控就绪链表,如果就绪链表有fd,则表示该fd可读可写,并返回给用户态(少量的copy); 98 | 99 | 该函数返回需要处理的事件数目,如返回0表示已超时。 100 | 101 | ### 小结 102 | 103 | 执行epoll_create时,在创建了红黑树和就绪链表。执行epoll_ctl时,如果增加fd,则检查在红黑树中是否存在,存在立即返回,不存在则添加到树上,然后向内核注册回调函数,用于当中断事件到来时向准备就绪链表中插入数据。执行epoll_wait时返回就绪链表里的数据即可。 104 | 105 | 因此,epoll比select和poll高效的原因是: 106 | 107 | 1. 减少了用户态和内核态之间文件句柄的copy 108 | 2. 降低了在文件句柄集中查找的时间复杂度。用红黑树维护fd集,可以将查找fd的时间复杂度降为o(logn)。 109 | 110 | ## 参考 111 | 112 | - https://www.zhihu.com/question/20122137 113 | - http://www.jianshu.com/p/dfd940e7fca2# 114 | - http://gityuan.com/2015/12/06/linux_epoll/ 115 | 116 | -------------------------------------------------------------------------------- /Java/基础/Java反射实现原理.md: -------------------------------------------------------------------------------- 1 | ## 从一段示例代码开始 2 | 3 | ```java 4 | Class clz = Class.forName("ClassA"); 5 | Object instance = clz.newInstance(); 6 | Method method = clz.getMethod("myMethod", String.class); 7 | method.invoke(instance, "abc","efg"); 8 | ``` 9 | 10 | 前两行实现了类的装载、链接和初始化(newInstance方法实际上也是使用反射调用了``方法),后两行实现了从class对象中获取到method对象然后执行反射调用。试想一下,如果`Method.invoke`方法内,动态拼接成如下代码,转化成JVM能运行的字节码,就可以实现反射调用了。 11 | 12 | ```java 13 | public Object invoke(Object obj, Object[] param){ 14 | MyClass instance=(MyClass)obj; 15 | return instance.myMethod(param[0],param[1],...); 16 | } 17 | ``` 18 | 19 | ## Class和Method对象 20 | 21 | Class对象里维护着该类的所有Method,Field,Constructor的cache,这份cache也可以被称作根对象。每次getMethod获取到的Method对象都持有对根对象的引用,因为一些重量级的Method的成员变量(主要是MethodAccessor),我们不希望每次创建Method对象都要重新初始化,于是所有代表同一个方法的Method对象都共享着根对象的MethodAccessor,每一次创建都会调用根对象的copy方法复制一份: 22 | 23 | ```java 24 | Method copy() { 25 | Method res = new Method(clazz, name, parameterTypes, returnType, 26 | exceptionTypes, modifiers, slot, signature, 27 | annotations, parameterAnnotations, annotationDefault); 28 | res.root = this; 29 | res.methodAccessor = methodAccessor; 30 | return res; 31 | } 32 | ``` 33 | 34 | 35 | 36 | ## 反射调用 37 | 38 | ```java 39 | public Object invoke(Object obj, Object... args) 40 | throws IllegalAccessException, IllegalArgumentException, 41 | InvocationTargetException 42 | { 43 | if (!override) { 44 | if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { 45 | Class caller = Reflection.getCallerClass(); 46 | checkAccess(caller, clazz, obj, modifiers); 47 | } 48 | } 49 | MethodAccessor ma = methodAccessor; // read volatile 50 | if (ma == null) { 51 | ma = acquireMethodAccessor(); 52 | } 53 | return ma.invoke(obj, args); 54 | } 55 | ``` 56 | 57 | 调用Method.invoke之后,先进行访问权限检查,再获取MethodAccessor对象,并调用MethodAccessor.invoke方法。MethodAccessor被同名Method对象所共享,由ReflectionFactory创建。创建机制采用了一种名为inflation的方式(JDK1.4之后):如果该方法的累计调用次数<=15,会创建出NativeMethodAccessorImpl,它的实现就是直接调用native方法实现反射;如果该方法的累计调用次数>15,会创建出由字节码组装而成的MethodAccessorImpl。(是否采用inflation和15这个数字都可以在jvm参数中调整) 58 | 59 | 那么以示例的反射调用`ClassA.myMethod(String,String)`为例,生成MethodAccessorImpl类的字节码对应成Java代码如下: 60 | 61 | ```java 62 | public class GeneratedMethodAccessor1 extends MethodAccessorImpl { 63 | public Object invoke(Object obj, Object[] args) throws Exception { 64 | try { 65 | MyClass target = (ClassA) obj; 66 | String arg0 = (String) args[0]; 67 | String arg1 = (String) args[1]; 68 | target.myMethod(arg0,arg1); 69 | } catch (Throwable t) { 70 | throw new InvocationTargetException(t); 71 | } 72 | } 73 | } 74 | ``` 75 | 76 | ## 性能 77 | 78 | 通过JNI调用native方法初始化更快,但对优化有阻碍作用。随着调用次数的增多,使用拼装出的字节码可以直接以Java调用的方式来实现反射,发挥了JIT的优化作用。 79 | 80 | 那么为什么Java反射调用被普通的方法调用慢很多呢?我认为主要有以下三点原因: 81 | 82 | 1. 因为接口的通用性,Java的invoke方法是传object和object[]数组的。基本类型参数需要装箱和拆箱,产生大量额外的对象和内存开销,频繁促发GC。 83 | 2. 编译器难以对动态调用的代码提前做优化,比如方法内联。 84 | 3. 反射需要按名检索类和方法,有一定的时间开销。 85 | 86 | 87 | 88 | 参考: 89 | 90 | - http://www.fanyilun.me/2015/10/29/Java%E5%8F%8D%E5%B0%84%E5%8E%9F%E7%90%86/ -------------------------------------------------------------------------------- /C&C++/C++ Primer.md: -------------------------------------------------------------------------------- 1 | ## struct 2 | 3 | C里面的struct结构体是用来定义复杂的数据类型;而C++里也保留了struct关键字,但C++里的struct关键字相当于用来声明类类型,而class和struct关键字定义类的唯一差别在于默认访问级别:默认情况下struct的成员为public,class的成员为private。 4 | 5 | ## 标准库类型 6 | 7 | ### string 8 | 9 | ![string的操作](string的操作.png) 10 | 11 | 任何存储string的size操作结果的变量必须为string::size_type类型。不要把size的返回值赋值给一个int变量。 12 | 13 | C风格的字符串是指那种以'\0'结尾的字符串,比如字符串字面量。而string.h是C语言提供的标准库来处理C风格字符串,cstring是C++的版本。 14 | 15 | | API | 功能 | 16 | | :--------------: | :------------------------------------ | 17 | | strlen(s) | 返回s的长度,不包括'\0' | 18 | | strcmp(s1,s2) | 标胶s1和s2是否相同。若相等,返回0;若大于,返回正数;若小于,返回负数 | 19 | | strcat(s1,s2) | 将s2连接到s1后,返回s1 | 20 | | strcpy(s1,s2) | 将s2复制给s1,返回s1 | 21 | | strncat(s1,s2,n) | 将s2的前n个字符连接到s1后面,返回s | 22 | | strncpy(s1,s2,n) | 将s2的前n个字符复制给s1,返回s1 | 23 | 24 | 建议使用标准库类型string,尽量避免使用C风格字符串。 25 | 26 | 27 | 28 | ### vector 29 | 30 | ![vector的操作](vector的操作.png) 31 | 32 | ```c++ 33 | for (vector::size_type i = 0; i != text.size(); ++i) { 34 | cout << text[i] << endl; 35 | } 36 | ``` 37 | 38 | ## 类 39 | 40 | ```c++ 41 | #include 42 | class Sales_item { 43 | public: 44 | double avg_price(); 45 | bool is_same_item(const Sales_item &item); 46 | Sales_item(std::string name) : units_sold(0), revenue(0), name(name) {} 47 | private: 48 | std::string name; 49 | unsigned units_sold; 50 | double revenue; 51 | }; 52 | double Sales_item::avg_price() { 53 | return units_sold == 0 ? 0 : revenue / units_sold; 54 | } 55 | bool Sales_item::is_same_item(const Sales_item &item) { 56 | return item.name == name; 57 | } 58 | ``` 59 | 60 | ### 构造函数 61 | 62 | **成员初始化次序** 63 | 64 | 构造函数初始化列表仅指定用于初始化成员的值,并不指定这些初始化执行的次序。成员被初始化的次序就是定义成员的次序。 65 | 66 | **类类型的数据成员的初始化** 67 | 68 | 初始化类类型的成员时,要指定实参并传递给成员类型的一个构造函数。可以使用该类型的任意构造函数。 69 | 70 | **默认构造函数** 71 | 72 | 如果一个类定义了一个构造函数,编译器就不会再生成默认构造函数。只有当一个类没有定义构造函数时,编译器才会自动生成一个默认构造函数。 73 | 74 | **抑制由构造函数定义的隐式转换** 75 | 76 | 将构造函数声明为explicit,来防止隐式转换时使用构造函数。单形参构造函数应该声明为explicit。 77 | 78 | **复制构造函数** 79 | 80 | 复制构造函数是一种特殊构造函数,具有单个形参,该形参是对该类类型的引用。当定义一个新对象并用一个同类型的对象对它进行初始化时,将显示使用复制构造函数。当将该类型的对象传递给函数或从函数返回该类型的对象时,将隐式使用复制构造函数。 81 | 82 | ```c++ 83 | Sales_item(const Sales_item &item); 84 | ``` 85 | 86 | - 当形参非引用类型的时候,将复制实参的值。以非引用类型作返回值时,将返回return语句中的值的副本。当形参或返回值为类类型时,由复制构造函数进行复制。 87 | - 如果将复制构造函数定义成private,那么类将不允许复制。 88 | 89 | ### 赋值运算符函数 90 | 91 | ```c++ 92 | Sales_item &Sales_item::operator=(const Sales_item &item) { 93 | cout << "operator=" << endl; 94 | if (this != &item) { 95 | // 执行深拷贝 96 | } 97 | return *this; 98 | } 99 | ``` 100 | 101 | 调用时机: 102 | 103 | ```c++ 104 | Sales_item item; 105 | Sales_item item1 = item; // 不会调用赋值运算符函数 106 | Sales_item item2; 107 | item2 = item; // 会调用赋值运算符函数 108 | ``` 109 | 110 | 原则:当一个类需要深拷贝时,需要定义复制构造函数,重载赋值运算符和自定义析构函数。 111 | 112 | 参考:[一文说尽C++赋值运算符重载函数(operator=)](http://www.cnblogs.com/zpcdbky/p/5027481.html) 113 | 114 | ### 析构函数 115 | 116 | ```c++ 117 | ~Sales_item(); 118 | Sales_item::~Sales_item() {s 119 | } 120 | ``` 121 | 122 | 局部变量在超过作用域时会自动调用析构函数;当对象的引用或指针超出作用域时,不会运行析构函数,只有删除指向动态分配对象的指针才会运行析构函数。 123 | 124 | ### 友元 125 | 126 | 友元机制允许一个类将其非公有成员的访问权授予指定的函数或类。友元的声明以关键字friend开始。 127 | 128 | ```C++ 129 | class Screen { 130 | friend class Window_Mgr; // 友元类 131 | friend Window_Mgr& Window_Mgr::relocate(Window_Mgr::index,Window_Mgr::index,Screen&); // 友元函数 132 | } 133 | ``` 134 | 135 | -------------------------------------------------------------------------------- /网络基础/Http缓存策略总结.md: -------------------------------------------------------------------------------- 1 | ## 缓存的拓扑结构 2 | 3 | ### 私有缓存 4 | 5 | 私有缓存是个人缓存,一般是指浏览器中内建的缓存。 6 | 7 | ### 公有代理缓存 8 | 9 | 公有缓存是缓存代理服务器,接受来自多个用户的访问。 10 | 11 | ## 缓存处理步骤 12 | 13 | 对于浏览器端的缓存来说,这些规则是在HTTP报文头和HTML页面的META标签中定义的。他们分别从新鲜度和校验值两个维度来规定浏览器是否可以直接使用缓存中的副本,还是需要去源服务器获取更新的版本。 14 | 15 | - **新鲜度**:缓存副本的有效期。 16 | - **校验值**:服务器返回资源时有时在控制头中带上这个资源实体标签ETag,它可以用来作为浏览器再次请求过程的校验标识。如果发现校验标识不匹配,说明资源已经被修改或过期,浏览器需要重新获取资源内容。 17 | 18 | 对一条报文的基本缓存处理过程: 19 | 20 | 1. 接收:缓存从网络中读取抵达的请求报文 21 | 2. 解析:缓存对报文进行解析,提取出URL和各种首部 22 | 3. 查询:缓存查看是否有本地副本可用 23 | 4. 新鲜度检测:检查缓存是否足够新鲜 24 | 5. 创建响应 25 | 6. 发送 26 | 7. 日志 27 | 28 | ## 浏览器端的缓存规则 29 | 30 | ### 使用HTML META标签 31 | 32 | ```html 33 | 34 | ``` 35 | 36 | 上面代码是告诉浏览器当前页面不被缓存,每次访问都需要去服务器拉取。这种方式只有部分浏览器可以支持,而且所有缓存代理服务器都不支持,因为代理不解析HTML内容本身。 37 | 38 | ### 使用HTTP Header 39 | 40 | #### 1. Pragma 41 | 42 | ```http 43 | Pragma: no-cache 44 | ``` 45 | 46 | 这是服务器告知客户端不要对资源读缓存,每次都需要向服务器发一次请求才行。 47 | 48 | 这是http1.0定义的header,在http1.1被Cache-Control代替。 49 | 50 | #### 2. Expires 51 | 52 | ```http 53 | Expires: Fri, 11 Jun 2021 11:33:01 GMT 54 | ``` 55 | 56 | Pragma用来禁用缓存,那么Expires就是用来告诉浏览器资源缓存过期的时间,告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。 57 | 58 | #### 3. Cache-Control 59 | 60 | http1.1新增了 Cache-Control 来定义缓存过期时间。注意:若报文中同时出现了 Expires 和 Cache-Control,则以 Cache-Control 为准。 61 | 也就是说优先级从高到低分别是 **Pragma -> Cache-Control -> Expires** 。 62 | 63 | 作为请求首部时,cache-directive 的可选值有: 64 | 65 | ![Cache-Control请求首部取值](Cache-Control请求首部取值.png) 66 | 67 | 作为响应首部时,cache-directive 的可选值有: 68 | 69 | ![Cache-Control响应首部取值](Cache-Control响应首部取值.png) 70 | 71 | **no-Store与no-Cache的区别** 72 | 73 | no-Store要求接收方不要将响应保存下来,而on-Cache允许接收方可以把响应缓存下来,但这些缓存不能直接用,必须经过向服务器发送校验请求后才能使用。 74 | 75 | #### 4. 缓存校验字段 76 | 77 | 客户端上某个资源保存的缓存时间过期了,但这时候其实服务器并没有更新过这个资源,那么直接告诉客户端直接使用缓存就好了。 78 | 79 | 就像这样一个场景: 80 | 81 | ``` 82 | C:小服,你几岁了? 83 | S:小客,我18岁了。 84 | ================================= 85 | C:小服 ,你几岁了?我猜你18岁了。 86 | S:靠,你知道还问我?(304) 87 | ================================= 88 | C:小服 ,你几岁了?我猜你18岁了。 89 | S:小客 ,我19岁了。(200) 90 | ``` 91 | 92 | ##### 1. Last-Modified/If-Modified-Since 93 | 94 | Last-Modified/If-Modified-Since要配合Cache-Control使用。 95 | 96 | 服务器将资源传递给客户端时,会将资源最后更改的时间以“Last-Modified: GMT”的形式加在实体首部上一起返回给客户端。 97 | 98 | ```http 99 | Last-Modified: Fri, 22 Jul 2016 01:47:00 GMT 100 | ``` 101 | 102 | 客户端会为资源标记上该信息,下次再次请求时,会把该信息附带在请求报文中一并带给服务器去做检查。 103 | 104 | ```http 105 | If-Modified-Since: Fri, 22 Jul 2016 01:47:00 GMT 106 | ``` 107 | 108 | 若传递的时间值与服务器上该资源最终修改时间是一致的,则说明该资源没有被修改过,直接返回`304`状态码,**内容为空**,这样就节省了传输数据量 。如果两个时间不一致,则服务器会发回该资源并返回`200`状态码,和第一次请求时类似。 109 | 110 | ##### 2. ETag/If-None-Match 111 | 112 | Last-Modified标注的最后修改只能精确到秒级,因此Http1.1还推出了 **ETag 实体首部**字段。Etag/If-None-Match也要配合Cache-Control使用。服务器会通过某种算法,给资源计算得出一个唯一标志符(比如md5标志),在把资源响应给客户端的时候,会在实体首部加上“ETag: 唯一标识符”一起返回给客户端。 113 | 114 | ```http 115 | Etag: "5d8c72a5edda8d6a:3239" 116 | ``` 117 | 118 | 客户端会保留该 ETag 字段,并在下一次请求时将其一并带过去给服务器。 119 | 120 | ```http 121 | If-None-Match: "5d8c72a5edda8d6a:3239" 122 | ``` 123 | 124 | 服务器只需要比较客户端传来的ETag跟自己服务器上该资源的ETag是否一致,就能很好地判断资源相对客户端而言是否被修改过了。 125 | 126 | 127 | 128 | ## 总结 129 | 130 | 浏览器第一次请求: 131 | 132 | ![缓存机制-浏览器第一次请求](缓存机制-浏览器第一次请求.png) 133 | 134 | 浏览器再次请求时: 135 | 136 | ![缓存机制-浏览器第二次请求](缓存机制-浏览器第二次请求.png) 137 | 138 | ![Http cache总结](Http cache总结.png) 139 | 140 | ## 参考与拓展 141 | 142 | - [HTTP缓存控制小结](http://imweb.io/topic/5795dcb6fb312541492eda8c) 143 | - [HTTP 缓存](https://developers.google.cn/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=zh-cn#etag) 144 | - [浅谈浏览器http的缓存机制](http://www.cnblogs.com/vajoy/p/5341664.html) 145 | - [【Web缓存机制系列】1 – Web缓存的作用与类型](http://alloyteam.com/2012/03/web-cache-1-web-cache-overview/) 146 | - [浅谈Web缓存](http://www.alloyteam.com/2016/03/discussion-on-web-caching/) -------------------------------------------------------------------------------- /Java/JVM/摘要笔记.md: -------------------------------------------------------------------------------- 1 | The symbolic reference is a bundle of information that uniquely identifies a method,including the class name**,** method name**,** and method descriptor. **(**A method descriptor is the method's return type and the number and types of its arguments.) 2 | 3 | 方法的符号引用是唯一标识一个方法的信息结构体,包含类名,方法名和方法描述符,方法描述符又包含返回值和参数类型。 4 | 5 | To resolve a symbolic reference, the JVM locates the method being referred to symbolically and replaces the symbolic reference with a direct reference. A direct reference**,** such as a pointer or offset**,** allows the virtual machine to invoke the method more quickly if the reference is ever used again in the future. 6 | 7 | 在JVM加载类的过程中,会将符号信用替换成直接引用。直接引用就是一个指针或偏移量,可以让JVM快速定位到具体要调用的方法。 8 | 9 | For every instance method invocation, the virtual machine expects a reference to the object (objectref) to be on the stack. 10 | 11 | 除非被调用的方法是类方法,每一次方法调用,JVM都会把方法被调用的对象引用压入栈中。 12 | 13 | In addition to objectref, the virtual machine expects the arguments (args) required by the method**,** if any**,** to be on the stack. If the method is a class method**,** only the args are on the stack. 14 | 15 | 除了对象的引用之外,JVM还会把方法的参数依次压入栈。 16 | 17 | To invoke a method, the Java virtual machine creates a new stack frame for the method. The stack frame contains space for the method's local variables,its operand stack**,** and any other information required by a particular virtual machine implementation. The size of the local variables and operand stack are calculated at compile-time and placed into the class file**,** so the virtual machine knows just how much memory will be needed by the method's stack frame. When the JVM invokes a method**,** it creates a stack frame of the proper size for that method. 18 | 19 | 在方法调用时,JVM会为方法新建一个栈帧,栈帧包含方法的局部变量表和操作数栈。栈帧的大小是在编译期就决定了。 20 | 21 | The JVM creates a new stack frame and places the objectref on the new stack frame as local variable 0,and all the args as local variable 1**,** 2**,** and so on. The objectref is the implicit this pointer that is passed to any instance method. 22 | 23 | 24 | 25 | The invokeinterface opcode performs the same function as invokevirtual. The only difference is that invokeinterface is used when the reference is of an interface type. 26 | 27 | invokeinterface 和 invokevirtual 的调用机制差不多,唯一的差别是invokeinterface用在接口类型。 28 | 29 | The JVM uses a different opcode to invoke a method given an interface reference because it can't make as many assumptions about the method table offset as it can given a class reference. If the JVM has a class reference, it knows each method will always occupy the same position in the method table, independent of the actual class of the object. This is not true with an interface reference: The method could occupy different locations for different classes that implement the same interface. 30 | 31 | 为什么要分接口方法调用和虚方法调用呢?因为对于一个类类型引用,不论它指向哪个种兼容类型的对象,它调用的方法在虚函数表的offset是一样的。然而对于接口类型引用,由于一个接口可以被不同的Class来实现,所以接口方法在不同类的方法表的offset当然就(很可能)不一样了。 32 | 33 | For aninvoke interface instruction, however, the virtual machine will have to search through the method table every single time the instruction is encountered, because it can't assume the offset is the same as in previous invocations. 34 | 35 | 每次接口方法的调用,JVM都会搜寻一遍虚函数表。但invokevirtual可以把首次调用时把offset缓存起来,后边再次调用时找到虚函数表,然后根据之前缓存的offset就可以快速定位到具体的方法了。 36 | 37 | 这意味着在vtable中,方法的位置在编译期是可知的。因此,javac会针对这个精确的vtable条目生成一个invokevirtual指令。不过,最终的方法选择依然是在运行期确定的,因为这里还有方法重写(overriding)的可能性,但是vtable slot在编译期就已经确定了。 38 | 39 | The fastest instructions will most likely be invokespecial and invokestatic, because methods invoked by these instructions are statically bound. When the JVM resolves the symbolic reference for these instructions and replaces it with a direct reference, that direct reference probably will include a pointer to the actual bytecodes. 40 | 41 | 综合这几种方法调用指令,invokespecial和invokestatic是最快的,因为它们的方法地址是编译期静态绑定的,当类加载时符号引用会被替换成直接引用,而直接引用可能就包含方法字节码的地址指针。 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /网络基础/Https总结.md: -------------------------------------------------------------------------------- 1 | ## 什么是HTTPS 2 | 3 | **HTTP+数据加密+身份认证+数据完整性保护=HTTPS** 4 | 5 | - 数据加密;传输内容进行加密 6 | - 身份验证:通信双方验证对方的身份真实性 7 | - 数据完整性保护:检测传统的内容是否被篡改或伪造 8 | 9 | HTTPS由HTTP进行通信,但利用SSL/TLS来加密数据包,默认使用端口443。 10 | 11 | ![HTTP&HTTPS](HTTP&HTTPS.png) 12 | 13 | ### 加密方式 14 | 15 | HTTPS采用混合加密机制: 16 | 17 | 1. 使用非对称加密技术对密钥进行加密 18 | 2. 再确保密钥安全的前提下,使用对称加密的方式进行通信 19 | 20 | 21 | 加密的过程中涉及到公钥和私钥。公钥就相当于锁头,任何人都能知道,私钥就相当于与锁头配对的唯一钥匙,不公开。 22 | 23 | ### 身份认证 24 | 25 | 数字签名是附加在报文上的特殊加密校验码,可以证明是作者编写了这条报文,前提是作者才会有私钥,才能算出这些校验码。如果传输的报文被篡改,则校验码不会匹配,因为校验码只有作者保存的私钥才能产生,所以前面可以保证报文的完整性。 26 | 27 | 证书信任的方式 28 | 29 | - 操作系统和浏览器内置 30 | - 权威的证书颁发机构 31 | - 手动指定证书 32 | 33 | **数字证书**包含以下内容; 34 | 35 | - 对象名称 36 | - 过期时间 37 | - 证书发布者(担保者) 38 | - 来自证书发布者的数字签名 39 | - 对象的公开密钥 40 | 41 | ### 数据完整性 42 | 43 | 检测传输的内容是否被篡改或伪造。 44 | 45 | **数字签名**是只有信息发送者才能产生的别人无法伪造的一段文本,这段文本是对信息发送者发送信息真实性的一个有效证明,具有不可抵赖性。 46 | 47 | **数字签名的生成过程:** 48 | 49 | 报文的发送方从报文文本生成一个128位的散列值(或称为报文摘要活哈希值),发送方使用自己的私钥对这个摘要值进行加密来形成发送方的数字签名。然后这个数字签名将作为报文的附件一起发送给报文的接收方。报文的接收方首先从接收到的原始报文中计算出128位的散列值,再用发送方的公钥来对报文附加的数字签名进行解密。如果两次得到的结果是一致的那么接收方可以确认信息是真实的 ,否则说明发送方是假冒的或者报文被篡改了。 50 | 51 | 52 | 53 | ## SSL协议和握手过程 54 | 55 | HTTP中没有加密机制,可以通过SSL(Secure Socket Layer 安全套接层)或TLS(Transport Layer Security 安全层传输协议)的组合使用,加密HTTP的通信内容。 56 | 57 | ![SSL-TLS握手过程](SSL-TLS握手过程.png) 58 | 59 | **1. 客户端发出请求 ClientHello** 60 | 61 | 在这一步,客户端需要协商协议版本号和加密算法。 62 | 63 | ``` 64 | 1. 服务端你好,我能理解的密码套件有RSA/3DES或DSS/AES,请问我们使用哪一种密码套件进行通信呢? 65 | ``` 66 | 67 | 客户端主要向服务器提供以下信息: 68 | 69 | (1) 支持的协议版本,比如TLS 1.0版。 70 | 71 | (2) 一个客户端生成的随机数,稍后用于生成"对话密钥"。 72 | 73 | (3) 支持的加密方法,比如RSA公钥加密。 74 | 75 | (4) 支持的压缩方法。 76 | 77 | **2. 服务端回应 ServerHello** 78 | 79 | 服务器收到客户端请求后,向客户端发出回应。 80 | 81 | ``` 82 | 2.你好,我们就用RSA/3DES来进行通信吧。 83 | 3.这是我的证书(非匿名通信时) 84 | 4.我们用这些信息来进行密钥交换吧。(当证书的信息不足时) 85 | 5.对了,请给我看一下你的证书吧 86 | 6.问候到此结束 87 | ``` 88 | 89 | 服务器的回应包含以下内容: 90 | 91 | (1) 确认使用的加密通信协议版本,比如TLS 1.0版本。 92 | 93 | (2) 一个服务器生成的随机数,稍后用于生成"对话密钥"。 94 | 95 | (3) 确认使用的加密方法,比如RSA公钥加密。 96 | 97 | (4) 服务器证书。 98 | 99 | **3. 客户端回应** 100 | 101 | 客户端收到服务器回应以后,首先验证服务器证书。如果证书没有问题,客户端就会从证书中取出服务器的公钥。 102 | 103 | ``` 104 | 7.这是我的证书 105 | 8.这是经过加密的预备主密钥(pre-master key) 106 | 9.我就是持有客户端证书的本人 107 | 10.好,现在我要切换密码了 108 | 11.握手协议到此结束 109 | ``` 110 | 111 | 客户端的回应包含以下内容: 112 | 113 | (1) 一个随机数。该随机数用服务器公钥加密,防止被窃听。 114 | 115 | (2) 编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。 116 | 117 | (3) 客户端握手结束通知,表示客户端的握手阶段已经结束。 118 | 119 | 此时客户端已经获取全部的计算协商密钥需要的信息:两个明文随机数 random_C 和 random_S 与自己计算产生的 Pre-master,计算得到协商密钥:enc_key=Fuc(random_C, random_S, Pre-Master) 120 | 121 | **4. 服务端最后回应** 122 | 123 | 服务器收到客户端的第三个随机数pre-master key之后,计算生成本次会话所用的"会话密钥"。 124 | 125 | ``` 126 | 12.好,现在我要切换密码了 127 | 13.握手协议到此结束 128 | ``` 129 | 130 | 小结握手过程: 131 | 132 | 1. 客户端给出协议版本号、一个客户端生成的随机数(Client random),以及客户端支持的加密方法。 133 | 2. 服务器确认双方使用的加密方法,并给出数字证书、以及一个服务器生成的随机数(Server random)。 134 | 3. 客户端确认数字证书有效,然后生成一个新的随机数(Premaster secret),并使用数字证书中的公钥,加密这个随机数,发给服务器。 135 | 4. 服务器使用自己的私钥,获取客户端发来的随机数(即Premaster secret)。 136 | 5. 这样,客户端和服务器都可以根据约定的加密方法,使用前面的三个随机数,生成"对话密钥"(session key),用来加密接下来的整个对话过程。 137 | 138 | 139 | ## Android客户端与HTTPS 140 | 141 | okhttp支持的https的网站基本都是CA机构颁发的证书,默认情况下是可以信任的。app客户端与自己服务端交互的时候可能也会遇到自签名证书。甚至在开发安全级别很高的app时,需要用到双向证书的验证。客户端需要预先把自签名证书注册为可信任证书。 142 | 143 | **参考:**https://developer.android.google.cn/training/articles/security-ssl.html?hl=zh-cn 144 | 145 | 146 | ## 参考&拓展 147 | 148 | - http://www.wosign.com/faq/faq2016-0309-04.htm 149 | - http://mouxuejie.com/blog/2017-03-16/https-ssl-tls-introduction/ 150 | - http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html 151 | - http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html 152 | - [深入理解HTTPS原理、过程与实践](https://zhuanlan.zhihu.com/p/26682342) 153 | - [图解HTTPS](http://www.cnblogs.com/zhuqil/archive/2012/07/23/2604572.html) 154 | - [一篇文章为你深度解析 HTTPS 协议](http://blog.csdn.net/qcloudcommunity/article/details/60964718) 155 | 156 | 157 | -------------------------------------------------------------------------------- /Java/JDK/HashMap.md: -------------------------------------------------------------------------------- 1 | # HashMap 2 | 1. 有两个参数可以影响*HashMap*的性能:初始容量(inital capacity)和负载系数(load factor)。初始容量指定了初始`table`的大小,负载系数用来指定自动扩容的临界值。当`entry`的数量超过`capacity*load_factor`时,容器将自动扩容并重新哈希。|Capacity就是bucket的大小,Load factor就是bucket填满程度的最大比例。如果对迭代性能要求很高的话不要把`capacity`设置过大,也不要把`load_factor`设置过小。当bucket中的entries的数目大于`capacity*load factor`时就需要调整bucket的大小为当前的2倍。| 3 | 2. 将对向放入到*HashMap*或*HashSet*中时,有两个方法需要特别关心:`hashCode()`和`equals()`。**hashCode()方法决定了对象会被放到哪个bucket里,当多个对象的哈希值冲突时,equals()方法决定了这些对象是否是“同一个对象”**。所以,如果要将自定义的对象放入到`HashMap`或`HashSet`中,需要@Override `hashCode()`和`equals()`方法。 4 | 3. 方法剖析 5 | 6 | - get() 7 | 8 | `get(Object key)`方法根据指定的`key`值返回对应的`value`,该方法调用了`getEntry(Object key)`得到相应的`entry`,然后返回`entry.getValue()`。因此`getEntry()`是算法的核心。 9 | 10 | 算法思想是首先通过`hash()`函数得到对应`bucket`的下标,然后依次遍历冲突链表,通过`key.equals(k)`方法来判断是否是要找的那个`entry`。 11 | 12 | ![getEntry](getEntry.png) 13 | 14 | 上图中`hash(k)&(table.length-1)`等价于`hash(k)%table.length`,原因是*HashMap*要求`table.length`必须是2的指数,因此`table.length-1`就是二进制低位全是1,跟`hash(k)`相与会将哈希值的高位全抹掉,剩下的就是余数了。 15 | 16 | ```java 17 | //getEntry()方法 18 | final Entry getEntry(Object key) { 19 | ...... 20 | int hash = (key == null) ? 0 : hash(key); 21 | for (Entry e = table[hash&(table.length-1)];//得到冲突链表 22 | e != null; e = e.next) {//依次遍历冲突链表中的每个entry 23 | Object k; 24 | //依据equals()方法判断是否相等 25 | if (e.hash == hash && 26 | ((k = e.key) == key || (key != null && key.equals(k)))) 27 | return e; 28 | } 29 | return null; 30 | } 31 | ``` 32 | 33 | - put() 34 | 35 | `put(K key, V value)`方法是将指定的`key, value`对添加到`map`里。该方法首先会对`map`做一次查找,看是否包含该元组,如果已经包含则直接返回,查找过程类似于`getEntry()`方法;如果没有找到,则会通过`addEntry(int hash, K key, V value, int bucketIndex)`方法插入新的`entry`,插入方式为**头插法**。 36 | 37 | ```java 38 | //addEntry() 39 | void addEntry(int hash, K key, V value, int bucketIndex) { 40 | if ((size >= threshold) && (null != table[bucketIndex])) { 41 | resize(2 * table.length);//自动扩容,并重新哈希 42 | hash = (null != key) ? hash(key) : 0; 43 | bucketIndex = hash & (table.length-1);//hash%table.length 44 | } 45 | //在冲突链表头部插入新的entry 46 | Entry e = table[bucketIndex]; 47 | table[bucketIndex] = new Entry<>(hash, key, value, e); 48 | size++; 49 | } 50 | ``` 51 | - resize 52 | 53 | 当超过限制的时候会resize,然而又因为我们使用的是2次幂的扩展(指长度扩为原来2倍),所以,元素的位置要么是在原位置,要么是在原位置再移动2次幂的位置。因此元素在重新计算hash之后,因为n变为2倍,那么n-1的mask范围在高位多1bit(红色),我们在扩充HashMap的时候,不需要重新计算hash,只需要看看原来的hash值新增的那个bit是1还是0就好了,是0的话索引没变,是1的话索引变成“原索引+oldCap”。 54 | 55 | ![resize](resize.png) 56 | 57 | 既省去了重新计算hash值的时间,而且同时,由于新增的1bit是0还是1可以认为是随机的,因此resize的过程,均匀的把之前的冲突的节点分散到新的bucket了。 58 | 59 | 4. hash函数 60 | 61 | 62 | ```java 63 | static final int hash(Object key) { 64 | int h; 65 | // 扰动函数:把hashCode的高16位移动到低16位后与自身进行异或 66 | return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); 67 | } 68 | ``` 69 | 70 | 如果传过来的key是null,则插入的数组下标是0。 71 | 72 | 5. Entry对象里缓存了对应key的再hash值。如果散列值不匹配,也不必检验key对象的等同性了。 73 | 74 | 6. 参考 75 | 76 | - http://blog.csdn.net/ns_code/article/details/36034955 77 | - http://blog.jobbole.com/101493/ 78 | - http://yikun.github.io/2015/04/01/Java-HashMap%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%8F%8A%E5%AE%9E%E7%8E%B0/ 79 | - [Java进阶(六)从ConcurrentHashMap的演进看Java多线程核心技术](http://www.jasongj.com/java/concurrenthashmap/) 80 | 81 | 82 | 83 | 84 | 85 | # HashMap和HashTable的区别 86 | 87 | http://www.importnew.com/7010.html 88 | 89 | - HashMap可以接收null的key,HashTable存入null key后,以后可能会抛NullPointException。 90 | - HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException。 91 | - HashTable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。 92 | - HashTable直接使用key的hashCode方法返回的hash值;HashMap需要调用hash方法对key的hashCode再hash。 93 | - HashTable在不指定容量的情况下的默认容量为11,而HashMap为16,Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。因此它们求出table的index的方式是不一样的,hashtable是与table的容量取模,HashMap是通过与table的容量减一做与&操作。 94 | - hashtable的默认容量为11,hashmap的默认容量为16. 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /Android/Android新进程的启动过程.md: -------------------------------------------------------------------------------- 1 | ## 大致分析 2 | ![start_app_process](start_app_process.jpg) 3 | 4 | 1. **App发起进程**:当从桌面启动应用,则发起进程便是Launcher所在进程;当从某App内启动远程进程,则发送进程便是该App所在进程。发起进程先通过binder发送消息给system_server进程; 5 | 2. **system_server进程**:调用Process.start()方法,通过socket向zygote进程发送创建新进程的请求; 6 | 3. **zygote进程**:在执行`ZygoteInit.main()`后便进入`runSelectLoop()`循环体内,当有客户端连接时便会执行ZygoteConnection.runOnce()方法,再经过层层调用后fork出新的应用进程; 7 | 4. **新进程**:执行handleChildProc方法,最后调用ActivityThread.main()方法。 8 | 9 | 10 | 11 | 12 | ## 源码的角度 13 | 14 | ![start_process时序图](start_process时序图.jpg) 15 | 16 | - Process.start方法完成一个新进程的启动 17 | - 新进程的入口方法为ActivityThread.main方法。 18 | 19 | 20 | ```java 21 | public static void main(String[] args) { 22 | ... 23 | Looper.prepareMainLooper(); 24 | ActivityThread thread = new ActivityThread(); 25 | thread.attach(false); 26 | ... 27 | Looper.loop(); 28 | } 29 | ``` 30 | 31 | - attach方法内会调用attachApplication,将**ApplicationThead与AMS**绑定起来 32 | 33 | - 接着AMS通过ApplicationThead的代理对象和ActivityThread的H对象通知ActivityThread调用handleBindApplication,着手以下工作: 34 | 35 | - 创建ContextImpl和Instrumentation 36 | - 创建**Application**对象 37 | - 调用installContentProvider,启动当前进程的**ContentProvider**并调用其onCreate方法。 38 | - 调用Application对象的onCreate方法。(mInstrumentation.callApplicationOnCreate) 39 | 40 | ​ 41 | 42 | 43 | 44 | 当点击`桌面App`的时候,发起进程就是`Launcher`所在的进程,启动远程进程,利用`Binder`发送消息给`system_server进程`; 45 | 46 | 在`system_server进程`中启动了N多服务,例如`ActiivityManagerService,WindowManagerService`等。启动进程的操作会先调用`AMS.startProcessLocked`方法,内部调用 `Process.start(android.app.ActivityThread);`而后通过`socket`通信告知`Zygote进程fork子进程`,即app进程。进程创建后将`ActivityThread`加载进去,执行`ActivityThread.main()`方法。 47 | 48 | 在`app进程`中,`main方法`会实例化`ActivityThread`,同时创建`ApplicationThread,Looper,Hander对象`,调用`attach方法`进行`Binder`通信,`looper`启动循环。`attach`方法内部获取`ActivityManagerProxy`对象,其实现了`IActivityManager`接口,作为客户端调用`attachApplication(mAppThread)`方法,将`thread`信息告知`AMS`。 49 | 50 | 在`system_server进程`中,`AMS`中会调用`ActivityManagerNative.onTransact`方法,真正的逻辑在服务端`AMS.attachApplication`方法中,内部调用`AMS.attachApplicationLocked`方法,方法的参数是`IApplicationThread`,在此处是`ApplicationThreadProxy`对象,用于跟前面通过`Process.start()`所创建的进程中`ApplicationThread`对象进行通信。 51 | `attachApplicationLocked`方法会处理`Provider, Activity, Service, Broadcast`相应流程,调用`ApplicationThreadProxy.bindApplication`方法,通过`Binder`通信,传递给`ApplicationThreadNative.onTransact`方法。 52 | 53 | 在`app进程`中,真正的逻辑在`ActivityThread.bindApplication`方法中。`bindApplication`方法的主要功能是依次向主线程发送消息`H.SET_CORE_SETTINGS 和H.BIND_APPLICATION`。后续创建`Application,Context`等。`Activity`的回调也会是通过Binder通信,然后发送不同消息处理。 54 | 55 | ## Android启动流程 56 | 57 | 参考:[Android Application启动流程分析](http://www.jianshu.com/p/a5532ecc8377) 58 | 59 | 首先, 让我们快速看下Android启动流程. 与众多基于Linux内核的系统类似, 启动系统时, bootloader启动内核和init进程. init进程分裂出更多名为"daemons(守护进程)"的底层的Linux进程, 诸如android debug deamon, USB deamon等. 这些守护进程处理底层硬件相关的接口. 60 | 61 | 随后, init进程会启动一个非常有意思的进程---"Zygote". 顾名思义, 这是一个Android平台的非常基础的进程. 这个进程初始化了第一个VM, 并且预加载了framework和众多App所需要的通用资源. 然后它开启一个Socket接口来监听请求, 根据请求孵化出新的VM来管理新的App进程. 一旦收到新的请求, Zygote会基于自身预先加载的VM来孵化出一个新的VM创建一个新的进程。 62 | 63 | 启动Zygote之后, init进程会启动runtime进程. Zygote会孵化出一个超级管理进程---System Server. SystemServer会启动所有系统核心服务, 例如Activity Manager Service, 硬件相关的Service等. 到此, 系统准备好启动它的第一个App进程---Home进程了. 64 | 65 | 66 | 67 | **摘自《深入理解Android》**http://wiki.jikexueyuan.com/project/deep-android-v1/zygote.html 68 | 69 | Zygote是创建Android系统中Java世界的盘古,它创建了第一个Java虚拟机,同时它又是女娲,它成功地繁殖了framework的核心system_server进程。做为Java语言的受益者,我们理应回顾一下Zygote创建Java世界的步骤: 70 | 71 | · 第一天:创建AppRuntime对象,并调用它的start。此后的活动则由AppRuntime来控制。 72 | 73 | · 第二天:调用startVm创建Java虚拟机,然后调用startReg来注册JNI函数。 74 | 75 | · 第三天:通过JNI调用com.android.internal.os.ZygoteInit类的main函数,从此进入了Java世界。然而在这个世界刚开创的时候,什么东西都没有。 76 | 77 | · 第四天:调用registerZygoteSocket。通过这个函数,它可以响应子孙后代的请求。同时Zygote调用preloadClasses和preloadResources,为Java世界添砖加瓦。 78 | 79 | · 第五天:Zygote觉得自己工作压力太大,便通过调用startSystemServer分裂一个子进程system_server来为Java世界服务。 80 | 81 | · 第六天:Zygote完成了Java世界的初创工作,它已经很满足了。下一步该做的就是调用runSelectLoopMode后,便沉沉地睡去了。 82 | 83 | · 以后的日子:Zygote随时守护在我们的周围,当接收到子孙后代的请求时,它会随时醒来,为它们工作。 84 | 85 | 如果支持中文编码的话,我一定要为Zygote取名为盘古_女娲。 -------------------------------------------------------------------------------- /Android/RecyclerView.md: -------------------------------------------------------------------------------- 1 | ## ListView vs RecyclerView 2 | 3 | ListView相比RecyclerView,有一些优点: 4 | 5 | - `addHeaderView()`, `addFooterView()`添加头视图和尾视图。 6 | - 通过”android:divider”设置自定义分割线。 7 | - `setOnItemClickListener()`和`setOnItemLongClickListener()`设置点击事件和长按事件。 8 | 9 | 这些功能在RecyclerView中都没有直接的接口,要自己实现(虽然实现起来很简单),因此如果只是实现简单的显示功能,ListView无疑更简单。 10 | 11 | RecyclerView相比ListView,有一些明显的优点: 12 | 13 | - 默认已经实现了View的复用,不需要类似`if(convertView == null)`的实现,而且回收机制更加完善。 14 | - 默认支持局部刷新。 15 | - 容易实现添加item、删除item的动画效果。 16 | - 容易实现拖拽、侧滑删除等功能。 17 | 18 | RecyclerView是一个插件式的实现,对各个功能进行解耦,从而扩展性比较好。 19 | 20 | 21 | | 角色 | 功能 | 22 | | --------------------------- | ------------------------------------ | 23 | | RecyclerView.ItemDecoration | 给每一项Item视图添加子View,例如可以进行画分隔线之类 | 24 | | RecyclerView.ItemAnimator | 负责处理数据添加或者删除时候的动画效果 | 25 | | RecyclerView.Adapter | 为每一项Item创建视图 | 26 | | RecyclerView.ViewHolder | 承载Item视图的子布局 | 27 | | RecyclerView.LayoutManager | 负责Item视图的布局的显示管理,items的滚动, 管理items动画 | 28 | 29 | 30 | ## RecyclerView 31 | 32 | ### Adapter工作原理 33 | 34 | 用于提供每个 item view 视图,并返回给 **RecyclerView** 作为其子布局添加到内部。 35 | 但是,与 **ListView** 不同的是,ListView 的适配器是直接返回一个 View,将这个 View 加入到 ListView 内部。而 RecyclerView 是返回一个 ViewHolder 并且不是直接将这个 holder 加入到视图内部,而是加入到一个缓存区域,在视图需要的时候去缓存区域找到 holder 再间接的找到 holder 包裹的 View。 36 | 37 | ### ViewHolder 38 | 39 | 每个 **ViewHolder** 的内部是一个 View,并且 **ViewHolder** 必须继承自`RecyclerView.ViewHolder`类。 这主要是因为 RecyclerView 内部的缓存结构并不是像 ListView 那样去缓存一个 View,而是直接缓存一个 ViewHolder ,在 ViewHolder 的内部又持有了一个 View。既然是缓存一个 ViewHolder,那么当然就必须所有的 ViewHolder 都继承同一个类才能做到了。 40 | 41 | ### Recycler 42 | 43 | ```java 44 | public final class Recycler { 45 | final ArrayList mAttachedScrap = new ArrayList<>(); // 屏幕上的ViewHolder 46 | private ArrayList mChangedScrap = null; // notify...方法需要改变的ViewHolder 47 | 48 | final ArrayList mCachedViews = new ArrayList(); // 缓存屏幕外的ViewHolder,默认大小为2 49 | 50 | private final List mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap); 51 | 52 | private RecycledViewPool mRecyclerPool; // 允许共享的ViweHolder缓存池,依据ItemType来缓存ViewHolder 53 | 54 | private ViewCacheExtension mViewCacheExtension; 55 | } 56 | public static class RecycledViewPool { 57 | // 根据 viewType 保存的被废弃的 ViewHolder 集合 58 | private SparseArray> mScrap = new SparseArray>(); 59 | } 60 | public void setRecycledViewPool(RecycledViewPool pool) { 61 | mRecycler.setRecycledViewPool(pool); 62 | } 63 | ``` 64 | 65 | 66 | 67 | ## Layout过程 68 | 69 | ![recyclerview_layout](recyclerview_layout.jpg) 70 | 71 | ## 滑动过程 72 | 73 | list的滑动最终会交由LayoutManager来处理,比如LinearLayoutManager的scrollBy方法: 74 | 75 | ```java 76 | int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { 77 | ... 78 | final int absDy = Math.abs(dy); 79 | updateLayoutState(layoutDirection, absDy, true, state); 80 | final int consumed = mLayoutState.mScrollingOffset 81 | + fill(recycler, mLayoutState, state, false); 82 | ... 83 | final int scrolled = absDy > consumed ? layoutDirection * consumed : dy; 84 | mOrientationHelper.offsetChildren(-scrolled); 85 | ... 86 | } 87 | ``` 88 | 89 | fill()方法,作用就是向可绘制区间填充ItemView,那么在这里,可绘制区间就是滑动偏移量!再看方法`mOrientationHelper.offsetChildren()`作用就是平移ItemView。 90 | 91 | 根据列表位置获取ItemView,先后从scrapped、cached、exCached、recycled集合中查找相应的ItemView,如果没有找到,就创建(Adapter.createViewHolder()),最后与数据集绑定。其中scrapped、cached和exCached集合定义在RecyclerView.Recycler中,分别表示将要在RecyclerView中删除的ItemView、一级缓存ItemView和二级缓存ItemView,cached集合的大小默认为2,exCached是需要我们通过RecyclerView.ViewCacheExtension自己实现的,默认没有;recycled集合其实是一个Map,定义在RecyclerView.RecycledViewPool中,将ItemView以ItemType分类保存了下来,这里算是设计的一个亮点,因为这样可以让不同的RecyclerView共享要给RecycledViewPool,而且它们之间的ViewHolder不会相互影响。 92 | 93 | 94 | 95 | ## 参考 96 | 97 | - http://blog.csdn.net/qq_23012315/article/details/50807224 98 | - https://kymjs.com/code/2016/07/10/01/ 99 | - http://chuansong.me/n/1453225651817 100 | - https://www.youtube.com/watch?v=LqBlYJTfLP4 101 | 102 | -------------------------------------------------------------------------------- /设计模式/设计模式.md: -------------------------------------------------------------------------------- 1 | ## [UML类图](https://design-patterns.readthedocs.io/zh_CN/latest/read_uml.html) 2 | 3 | ![uml](uml.jpg) 4 | 5 | - 车的类图结构为<>,表示车是一个抽象类; 6 | - 它有两个继承类:小汽车和自行车;它们之间的关系为实现关系,使用带空心箭头的虚线表示; 7 | - 小汽车为与SUV之间也是继承关系,它们之间的关系为泛化关系,使用带空心箭头的实线表示; 8 | - 小汽车与发动机之间是组合关系,使用带实心箭头的实线表示; 9 | - 学生与班级之间是聚合关系,使用带空心箭头的实线表示; 10 | - 学生与身份证之间为关联关系,使用一根实线表示; 11 | - 学生上学需要用到自行车,与自行车是一种依赖关系,使用带箭头的虚线表示; 12 | 13 | 总结: 14 | 15 | 1. 类的继承与接口的实现都是用箭头 16 | 2. 类的聚合和组合都是用菱形 17 | 18 | 19 | 20 | ## 静态工厂模式 21 | 22 | 又称为简单工厂模式,可以根据传入参数的不同,返回不同的实例。静态工厂模式定义一个类负责创建其他类的实例,被创建类的实例通常都具有共同的父类。 23 | 24 | ![StaticFactory](StaticFactory.jpg) 25 | 26 | 简单工厂模式最大的优点在于**实现对象的创建和对象的使用分离**,将对象的创建交给专门的工厂类负责。当你需要某个对象,只需要调用工厂类的方法并传入正确的参数就可以获得对象,而无需知道具体的创建细节。但是其最大的缺点在于工厂类不够灵活,增加新的具体产品需要修改工厂类的判断逻辑代码,而且产品较多时,工厂方法代码将会非常复杂。 27 | 28 | 场景: 29 | 30 | 1. PolicyManager.makeNewWindow 31 | 2. Volley.newRequestQueue 32 | 3. RxJava Observerable的创建 33 | 34 | ```java 35 | public static Observable create(OnSubscribe f) { 36 | return new Observable(RxJavaHooks.onCreate(f)); 37 | } 38 | ``` 39 | 40 | 41 | 42 | ## 工厂方法模式 43 | 44 | 工厂方法模式是静态工厂模式的一种抽象和推广。将工厂类的方法抽象成具体的接口,由各个子类工厂去实现并生产特定的类实例。 45 | 46 | ![FactoryMethod](FactoryMethod.jpg)、 47 | 48 | 49 | 50 | ## 抽象工厂模式 51 | 52 | 一个工厂可以生产多种不同的产品。工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。 53 | 54 | ![FactoryMethod](FactoryMethod.jpg) 55 | 56 | ## [Builder模式](https://github.com/simple-android-framework-exchange/android_design_patterns_analysis/tree/master/builder/mr.simple) 57 | 58 | 角色: 59 | 60 | - Product 产品类 : 产品的抽象类。 61 | - Builder : 抽象类, 规范产品的组建,一般是由子类实现具体的组件过程。 62 | - ConcreteBuilder : 具体的构建器. 63 | - Director : 统一组装过程(可省略)。 64 | 65 | ![builder](builder.png) 66 | 67 | 场景: 68 | 69 | 1. AlertDialog的创建过程 70 | 2. OkHttpClient(CallFactory),Retrofit的创建过程 71 | 72 | 73 | 74 | ## 单例模式 75 | 76 | ```java 77 | public class Singleton { 78 | private volatile static Singleton uniqueInstance; 79 | public static Singleton getInstance() { 80 | if (uniqueInstance==null){ 81 | synchronized (Singleton.class){ 82 | if (uniqueInstance!=null) 83 | uniqueInstance=new Singleton(); 84 | } 85 | } 86 | return uniqueInstance; 87 | } 88 | private Singleton() {} 89 | } 90 | ``` 91 | 92 | 93 | 94 | ## 适配器模式 95 | 96 | ![Adapter](Adapter.jpg) 97 | 98 | 场景: 99 | 100 | ListView的Adapter场景中,数据源就是Adaptee,Client就是ListView。 101 | 102 | 103 | 104 | ## 桥接模式 105 | 106 | 将抽象化部分(Abstraction)与实现部分(Implementation)分离,使它们可以独立变化。 107 | 108 | - 抽象化:抽象化就是忽略一些信息,把不同的实体当作同样的实体对待。在面向对象中,将对象的共同性质抽取出来形成类的过程即为抽象化的过程。它持有Implementation的引用。 109 | - 实现化:针对抽象化给出的具体实现,就是实现化,抽象化与实现化是一对互逆的概念,实现化产生的对象比抽象化更具体,是对抽象化事物的进一步具体化的产物。 110 | - 脱耦:脱耦就是将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改换成弱关联,将两个角色之间的继承关系改为关联关系。桥接模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用关联关系(组合或者聚合关系)而不是继承关系,从而使两者可以相对独立地变化,这就是桥接模式的用意。 111 | 112 | ![Bridge](Bridge.jpg) 113 | 114 | 场景: 115 | 116 | 1. ListView的ItemView的布局和创建被抽象出来,ItemView的创建被抽取到Adapter来实现,ItemView的布局被抽象成一个抽象类AbsListView。 117 | 2. JVM的跨平台特性 118 | 3. View与Drawable 119 | 120 | ## 装饰者模式 121 | 122 | 动态地增强一个对象。 123 | 124 | ![Decorator](Decorator.jpg) 125 | 126 | 127 | 128 | 场景: 129 | 130 | 1. Activity等类是ContextImpl的包装类。 131 | 132 | 133 | 134 | ## 代理模式 135 | 136 | 某个对象提供一个代理对象,并由代理对象控制对原对象的引用。 137 | 138 | ![Proxy](Proxy.jpg) 139 | 140 | [静态代理和动态代理的区别](http://blog.csdn.net/giserstone/article/details/17199755): 141 | 142 | - 静态代理的代理类和委托类的关系在运行前就已经确定了 143 | - 动态代理的代理类和委托类(或接口)的调用关系是运行时确定的 144 | 145 | 场景: 146 | 147 | 1. AMS与ActivityThread交互 148 | 2. AIDL 149 | 150 | 151 | 152 | ## 命令模式 153 | 154 | 将命令执行者与命令请求者的职责分离,将命令抽象成接口并可对应多种实现,命令的执行者就可以通过命令接口来对命令交互了。 155 | 156 | ![command](command.png) 157 | 158 | 场景: 159 | 160 | 1. Runnable与Executor 161 | 2. RxJava里面,Observable里的OnSubscribe对象。OnSubscribe是一个命令对象,当Observable的subscribe方法被调用时,OnSubscribe的call方法会被调用。 162 | 163 | 164 | 165 | ## 观察者模式 166 | 167 | 实在是用得太多了! 168 | 169 | 170 | 171 | ## 策略模式 172 | 173 | 定义一些独立的类来封装不同的算法,每一个类封装一个具体的算法,在这里,每一个封装算法的类我们都可以称之为策略(Strategy),为了保证这些策略的一致性,一般会用一个抽象的策略类来做算法的定义,而具体每种算法则对应于一个具体策略类。 174 | 175 | ![StaticFactory](StaticFactory.jpg) 176 | 177 | 场景: 178 | 179 | 1. Android动画中的时间插值器和估值器。 -------------------------------------------------------------------------------- /Android/Activity-LifeCircle&Launchmode.md: -------------------------------------------------------------------------------- 1 | # Activity生命周期 2 | 3 | ![activity_lifecycle](activity_lifecycle.png) 4 | 5 | 6 | 7 | ## 有关生命周期的几个回调方法 8 | 9 | - ### onCreate() 10 | 11 | 系统第一次创建该Activity实例时会回调该方法。该方法一般承担着一些初始化工作,比如调用setContentView去加载界面布局,初始化Activity所需的数据并绑定到对应的View上。 12 | 13 | - ### onRestart() 14 | 15 | Activity从onStop状态,将要重新进入前台(由不可见到可见)。 16 | 17 | - ### onStart() 18 | 19 | Activity正在启动,已经时可见的了,但还没到前台,无法和用户交互。 20 | 21 | - ### onResume() 22 | 23 | Activity已经在前台,可以与用户交互。 24 | 25 | - ### onPause() 26 | 27 | Activity离开前台,不能与用户交互。这也是Activity接收到用户要离开该Activity指令时所回调的第一个方法。比如: 28 | 29 | 1. 有电话打进来 30 | 2. 手机息屏 31 | 3. 进入其他的Activity 32 | 4. 打开一个新的半透明的Activity 33 | 5. 在Android 7.0之后的multi-window模式下,失去焦点的Activity会调用onPause 34 | 35 | onPause适合做轻量级的回收工作,比如停止动画等,而不适合进行太耗时的操作,因为旧的onPause执行完后,新的Activity的onResume才会执行。 36 | 37 | 执行完onPause后并不意味着activity就离开了Paused 状态了,因为此时旧的activity仍然可见且新的activity还未进入resume状态。如果activity变得完全不可见了,回调onStop。 38 | 39 | - ### onStop() 40 | 41 | Activity即将停止,不再可见。 42 | 43 | onStop适合做稍微重量级的回收工作,比如释放导致内存泄漏的资源,保存数据到数据库中。 44 | 45 | 有时候由于高优先级的应用需要内存,系统不足以分配,就把拥有该Activity的进程杀死来释放内存,这样就不会调用onDestory。(It is possible for the system to kill the process hosting your activity without calling the activity's final `onDestroy()` callback.) 46 | 47 | - ### onDestroy() 48 | 49 | Activity即将被销毁,Activity生命周期中最后一个回调,适合做最终的资源释放与回收工作。 50 | 51 | 有两种情况下,系统回调此方法。一是Activity的finish()被调用,二是系统为了节约内存而把activity所在的进程杀死(the system is temporarily destroying the process containing the activity to save space)。这两种方式可以用 `isFinishing()` 来区分,前者 `isFinishing()` 返回true。 52 | 53 | 54 | 55 | **小结** 56 | 57 | onCreate与onDestroy是一对回调,标志一个Activity的诞生与消亡; 58 | 59 | onStart与onStop是一对回调,标志一个Activity是否可见; 60 | 61 | onResume与onPause是一对回调,标志一个Activity是否能与用户交互,是位于前台还是后台。 62 | 63 | 64 | 65 | ## 典型场景 66 | 67 | - 由ActivityA跳转到ActivityB 68 | 69 | 回调如下: 70 | 71 | -> A的onPause 72 | 73 | -> B的onCreate,onStart,onResume 74 | 75 | -> 如果A不可见则还会调用A的onStop 76 | 77 | 78 | 79 | - Android7.0的Muti-Window分屏模式 80 | 81 | 当Activity获得焦点,回调onResume; 82 | 83 | 当Activity失去焦点,回调onPause,但由于此时Activity仍处于可见状态,所以不会回调onStop 84 | 85 | - 点击home键并重回Activity 86 | 87 | -> onPause-> onSaveInstanceState -> onStop -> onRestart -> onStart -> onResume 88 | 89 | - 异常情况(如Activity被杀死,Activity由竖屏变为横屏) 90 | 91 | 当系统配置发生改变后,Activity会被销毁再重建,系统会在onStop之前调用onSaveInstanceState来保存当前Activity的状态,与onPause没有既定的时序关系。onSaveInstanceState只是为将来Activity可能被重建保存快照,如果Activity果真被重建了,系统把onSaveInstanceState所保存的Bundle对象作为参数传递给onCreate与onRestoreInstanceState,onRestoreInstanceState在onStart调用之后。 92 | 93 | 如果想让系统配置发生改变后,不想系统重新创建Activity可以给Activity指定configChanges属性。比如不想让Activity在屏幕旋转的时候重新创建可以给Activity添加属性:`android:configChanges="orientation"`。 94 | 95 | - Activity在前台,设置了启动模式为singleTop 或者 singleTask,再次启动 96 | 97 | -> onPause -> onNewIntent -> onResume 98 | 99 | 这是我发现唯二能够由onPause到onResume的情况。 100 | 101 | 102 | 103 | # Activity的启动模式 104 | 105 | 1. Standard:标准模式,每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否已经存在。Standard模式的Activity默认会进入启动它的Activity所属的任务栈中。 106 | 2. SingleTop:栈顶复用模式。如果新的Activity已经位于任务栈的栈顶,此Activity不会被重新创建,同时它的onNewIntent方法会被回调。 107 | 3. singleTask:栈内复用模式。只要栈内存在该Activity,那么此Activity不会重新创建实例,和singleTop一样,系统也会回调其onNewIntent方法。 108 | 4. singleInstance:单例模式。每次启动Activity都需要新开辟一个栈,并且此栈不能有第二个Activity。 109 | 110 | ## TaskAffinity与singleTask 111 | 112 | Task其实是Activity集合的概念。Affinity意为亲和力,标识了一个Activity任务栈的名字,默认所有的Activity所需的任务栈名字为应用的包名。 113 | 114 | TaskAffinity属性要和singleTask启动模式或allowTaskRepresenting属性配对使用,在其他情况下没有意义。 115 | 116 | - 如果待启动的Activity是以singleTask模式来启动的,它会运行在名字和自身TaskAffinity相同的任务栈中 117 | - 如果应用A启动了应用B的某个Activity,这个Activity的allowTaskRepresenting属性为true,那么当应用B被启动后,此Activity会直接从应用A的任务栈转移到应用B的任务栈中。 118 | 119 | 当我们用ApplicationContext去启动standard模式的Activity的时候会报错。因为standard模式的Activity默认会进入启动它的Activity所属的任务栈中,但非Activity类型的Context没有所谓的任务栈。 120 | 121 | ## IntentFilter的匹配规则 122 | 123 | 启动Activity分为两种,显式调用和隐式调用。显式调用需要明确地指定被启动对象的组件信息,包括包名和类名;而隐式启动需要明确指定组件的信息。 124 | 125 | 隐式启动需要同时匹配intent-filter中的action,category和data信息。一个intent-filter中的action,category和data可以有多个,所有的action,category和data分别构成不同类别。 126 | 127 | ### action 128 | 129 | action是一个字符串。一个过滤规则可以有多个action,那么Intent中的action必须存在且和过滤规则中的其中一个action相同才算匹配。 130 | 131 | ### category 132 | 133 | category是要给字符串,要求Intent中可以没有category,但如果存在,那么每个category都必须与过滤规则中的任何一个category相同。 134 | 135 | ### data 136 | 137 | data由两部份组成,mimeType和URI。data匹配规则与action类似。 -------------------------------------------------------------------------------- /Android/插件化/热修复和插件化小结.md: -------------------------------------------------------------------------------- 1 | ## 热修复 2 | 3 | ### QQ空间的方案 4 | 5 | QQ空间热修复的方案是基于dex分包方案的基础之上,简单来说就是把BUG方法修复以后,重新生成一个dex,从服务器下载之后,将其插入到dexElements数组的最前面,让虚拟机去优先加载修复后的类。 6 | 7 | 使用这种方案时,必须避免类被打上`CLASS_ISPREVERIFIED`标志,具体的做法就是在每一个类的构造函数中单独引用一个在另外dex中的类。 8 | 9 | 该方案存在如下的缺点: 10 | 11 | 1. 不支持即时生效,必须重启虚拟机才能生效 12 | 2. patch.dex是用来存储修复的类,启动应用时,就要加载patch.dex,当修复的类到了一定数量,加载时间会变得很长,造成应用启动卡顿 13 | 14 | **优化方案**:[QFix](https://mp.weixin.qq.com/s?__biz=MzA3NTYzODYzMg==&mid=2653577964&idx=1&sn=bac5c8883b7aaaf7d7d9ea227f200412&chksm=84b3b0ebb3c439fd56a502a27e1adc18f600b875718e537191ef109e2d18dae1c52e5e36f2d9&scene=0#wechat_redirect) 15 | 16 | QFix官方还没开源,但有开源实现:https://github.com/lizhangqu/QFix 17 | 18 | ### 微信Tinker的方案 19 | 20 | 通过生成dex差量包的方式,与QQ空间超级补丁技术基本相同,区别在于不再将patch.dex增加到elements数组中,而是差量的方式给出patch.dex,然后将patch.dex与应用的classes.dex合并,然后整体替换掉旧的dex文件,以达到修复的目的。 21 | 22 | ![Tinker热修复方案](Tinker热修复方案.png) 23 | 24 | 缺点: 25 | 26 | 1. 不支持即时生效 27 | 2. 合并算法需要比较大的计算资源,需要单独开一个进程,系统可能会把进程杀掉,降低成功率 28 | 29 | ### 阿里AndFix方案 30 | 31 | 以上两种热修复方案都是针对Java层的,而AndFix的原理是基于native层的,修复单位是方法级别的。它是通过运行时在Native修改Filed指针,实现方法的替换。 32 | 33 | 优点: 34 | 35 | 1. 不需要重启虚拟机,即时生效 36 | 2. patch包体积小 37 | 38 | 不足: 39 | 40 | 1. 不支持新增字段,以及修改方法,也不支持对资源的替换。 41 | 2. 需要针对dalvik虚拟机和art虚拟机做适配,需要考虑指令集的兼容问题 42 | 43 | ### 美团Robust方案 44 | 45 | Robust插件对每个产品代码的每个函数都在编译打包阶段自动的插入了一段代码和一个静态变量。 46 | 47 | ```java 48 | public static ChangeQuickRedirect changeQuickRedirect; 49 | public long getCount() { 50 | if(changeQuickRedirect != null) { 51 | //PatchProxy中封装了获取当前className和methodName的逻辑,并在其内部最终调用了changeQuickRedirect的对应函数 52 | if(PatchProxy.isSupport(new Object[0], this, changeQuickRedirect, false)) { 53 | return ((Long)PatchProxy.accessDispatch(new Object[0], this, changeQuickRedirect, false)).longValue(); 54 | } 55 | } 56 | return 100L; 57 | } 58 | ``` 59 | 60 | 补丁包patch.dex包含有修复好的类。当客户端拿到补丁包后,用DexClassLoader加载patch.dex,拿到修复好的类的信息。通过反射找到有bug的类,并给它的成员变量changeQuickRedirect赋值。当这个类的有bug的方法被调用时就会被重定向到执行修复好的方法。 61 | ![美团Robust热修复原理图](美团Robust热修复原理图.png) 62 | 63 | 64 | 65 | 优点: 66 | 67 | - 只用到了ClassLoader,兼容性好 68 | - 补丁下发立即生效,不需要重新启动 69 | 70 | 缺点: 71 | 72 | - 编译阶段有插件侵入了产品代码,给每个方法插桩,对方法运行时间,包体积和方法数会有一点的影响 73 | - 不支持资源和so的替换 74 | 75 | ### 参考 76 | 77 | - [Android热修复学习之旅开篇——热修复概述](http://blog.csdn.net/u012124438/article/details/62107035) 78 | - [安卓App热补丁动态修复技术介绍](https://mp.weixin.qq.com/s?__biz=MzI1MTA1MzM2Nw==&mid=400118620&idx=1&sn=b4fdd5055731290eef12ad0d17f39d4a&scene=1&srcid=1106Imu9ZgwybID13e7y2nEi#wechat_redirect) 79 | - [Android热修复学习之旅开篇——热修复概述](http://blog.csdn.net/u012124438/article/details/62107035) 80 | - [Android热更新方案Robust](https://tech.meituan.com/android_robust.html) 81 | 82 | 83 | 84 | ## 插件化 85 | 86 | ### Hook组件的启动 87 | 88 | Android的四大组件都需要在AndroidManifest.xml中声明,而插件化要实现的目标是启动没有在AndroidManifest.xml中声明的组件。要做到这一点就必须要欺骗AMS的验证过程和让系统回调我们启动的组件的生命周期。 89 | 90 | 以Activity的启动为例。 91 | 92 | ![Activity启动的简要过程](/Users/lehua.tlh/Desktop/Code/Tech-Note/技术/Android/插件化/Activity启动的简要过程.png) 93 | 94 | Activity的栈管理,启动模式和校验等工作都是由AMS来做的,然后AMS通过ApplicationThread来通知ActivityThread来实例化和初始化Activity,并回调Activity的生命周期方法。至此,我们可以这样做。 95 | 96 | 1. 我们可以事先在AndroidManifest.xml里面声明一个StubActivity(龙套)。通过反射和动态代理Hook掉ActivityManagerNative的startActivity方法。把startActivity传过来的Intent对象里面的Activity替换成龙套Activity。 97 | 2. ActivityThread里面有一个Handler,专门来处理ApplicationThread传过来的通知。这些通知是由AMS通过Binder发过来的,当AMS处理完Activity校验和栈管理等手续后就会通知ActivityThread来实例化Activity。Activity在App端的启动工作是放在Handler.Callback里面。我们可以把这个Callback Hook掉,把Intent替换为原来真正的Intent。后面,ActivityThread会从这个Intent里的Activity的Class把Activity实例化。 98 | 99 | ### Hook类加载器 100 | 101 | Android系统使用了ClassLoader机制来进行Activity等组件的加载;apk被安装之后,APK文件的代码以及资源会被系统存放在固定的目录(比如/data/app/package_name/base-1.apk )系统在进行类加载的时候,会自动去这一个或者几个特定的路径来寻找这个类;但是系统并不知道存在于插件中的Activity组件的信息(插件可以是任意位置,甚至是网络,系统无法提前预知),因此正常情况下系统无法加载我们插件中的类。如果我们能把这个ClassLoader替换成我们自己的代理ClassLoader,就能操控Android的类加载过程。 102 | 103 | ```Java 104 | java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); 105 | activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent); 106 | StrictMode.incrementExpectedActivityCount(activity.getClass()); 107 | r.intent.setExtrasClassLoader(cl); 108 | ``` 109 | 110 | r.packageInfo是一个LoadedApk类的对象,**LoadedApk对象是APK文件在内存中的表示。** Apk文件的相关信息,诸如Apk文件的代码和资源,甚至代码里面的Activity,Service等组件的信息我们都可以通过此对象获取。而packageInfo被缓存到ActivityThread的mPackages的字段中。 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /Android/Android-Drawable.md: -------------------------------------------------------------------------------- 1 | Drawable 是一个抽象类,提供了一些 API 方法去处理各种资源的绘制,但是又不具备 View 的事件与交互处理能力。 2 | 3 | Drawable表示一种图像的概念,通常被用来作为VIew的背景来使用,Drawable会被拉伸至与VIew同等大小。Drawable的内部宽高通过getIntrinsicWidth和getIntrinsicHeight这两个方法获取,并不是所有的Drawable都有内部的宽高。 4 | 5 | Drawable的实际区域大小可以通过getBounds方法来得到,一般跟View的大小相同。 6 | 7 | 官方文档: 8 | 9 | https://developer.android.google.cn/guide/topics/resources/drawable-resource.html 10 | 11 | ## BitmapDrawable 12 | 13 | 表示的是一张图片,对应于``标签。 14 | 15 | ```xml 16 | 17 | 28 | ``` 29 | 30 | antialias:开启抗锯齿功能,让图片变得更平滑。 31 | 32 | dither:当图片像素配置与手机屏幕的像素配置不一致时,可以让高质量图片在低质量的屏幕上还能保持较好的显示效果。 33 | 34 | filter:当图片尺寸被拉伸或压缩时,开启过滤功能可以获得更好的显示效果。 35 | 36 | titleMode:mirror在水平和竖直方向上的镜面投影效果;clamp图片四周的像素会扩展到周围区域。 37 | 38 | 39 | 40 | ## ShapeDrawable 41 | 42 | 通过颜色来构造图像,既可以是纯色的图形,可以是具有渐变效果的图形,对应与``标签。 43 | 44 | ```xml 45 | 46 | 49 | 55 | 65 | 70 | 73 | 75 | 80 | 81 | ``` 82 | 83 | 84 | 85 | ## LayerDrawable 86 | 87 | 对应于``标签,表示一种层次化的Drawable集合。 88 | 89 | ```xml 90 | 91 | 93 | 100 | 101 | ``` 102 | 103 | 104 | 105 | ## StateListDrawable 106 | 107 | 对应于``标签,常用于Button的Selector。 108 | 109 | ```xml 110 | 111 | 115 | 126 | 127 | ``` 128 | 129 | 130 | 131 | [Android 应用层开发 Drawable 的一些叨叨絮](http://blog.csdn.net/yanbober/article/details/56844869) 132 | 133 | -------------------------------------------------------------------------------- /操作系统/操作系统知识拾遗.md: -------------------------------------------------------------------------------- 1 | # 操作系统概述 2 | 3 | ## 计算机基本构成 4 | 5 | ![计算机部件:顶层视图](计算机部件:顶层视图.png) 6 | 7 | - MAR确定下一次读写的内存地址 8 | - MBR存放要写入存储器的数据或者从存储器中读取的数据 9 | - IO AR确定一个特定的输入输出设备 10 | - IO BR用于在IO模块和CPU之间交换数据 11 | 12 | ## 操作系统的基本功能 13 | 14 | 1. 作业管理 15 | 2. 进程管理 16 | 3. 存储管理 17 | 4. 文件管理 18 | 5. 设备管理 19 | 20 | Linux 内核组件: 21 | 22 | ![Linux内核组件](Linux内核组件.png) 23 | 24 | # 进程管理 25 | 26 | ## 进程控制块 27 | 28 | 进程是由程序代码和相关数据还有进程控制块组成。 29 | 30 | 进程控制块信息分为三类: 31 | 32 | - 进程标识信息 33 | - 处理器状态信息 34 | - 进程控制信息 35 | 36 | 具体如下; 37 | 38 | - 标识符:进程的ID 39 | - 状态:如果进程正在执行,那么进程处于运行态 40 | - 优先级 41 | - 程序计数器 42 | - 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享内存块的指针 43 | - 上下文数据:进程执行时CPU中寄存器中的数据 44 | - I/O状态信息:包括显示I/O请求,分配给进程的I/O设备和被进程使用的文件列表 45 | - 记账信息:使用时钟数总和,时间限制等待 46 | 47 | ## 进程状态 48 | 49 | ![五状态进程模型](五状态进程模型.png) 50 | 51 | ## 进程描述 52 | 53 | 一般操作系统维护着4种不同类型的表:内存,I/O,文件和进程。 54 | 55 | ![操作系统控制表的通用结构](操作系统控制表的通用结构.png) 56 | 57 | - 内存表用于跟踪内存和外存。包含分配给进程的内存和外存的信息,进程共享内存区域的信息,管理虚拟内存所需的任何信息。 58 | - I/O表管理计算机系统中I/O设备和通达。 59 | - 文件表提供关于文件是否存在,文件在外存中的位置,当前状态和其他属性的信息。 60 | - 进程表中每一个表项都含有一个指向进程映像的指针。 61 | 62 | 一个进程需要足够的内存空间来保存该进程的程序和数据。程序,数据,栈和属性的集合称为进程映像,属性的集合就是进程控制块。 63 | 64 | ![进程组成结构](进程组成结构.png) 65 | 66 | ## 进程创建 67 | 68 | 1. 给新进程分配一个唯一的进程标识符 69 | 2. 给进程分配空间。包括进程映像中的所有元素。 70 | 3. 初始化进程控制块PCB。进程ID,CPU状态信息,进程控制信息等信息的初始化 71 | 4. 设置新的连接。如将进程控制块链接到相应的调度队列的链表上。 72 | 73 | ## 进程和线程 74 | 75 | **进程包含两个独立的特点**: 76 | 77 | - 资源所有权。一个进程包括一个存放进程映像的虚拟地址空间。一个进程总是拥有对资源的控制或所有权,这些资源包括内存,I/O设备和文件。 78 | - 程序调度/执行。一个进程沿着通过一个或多个程序的一条执行路径执行。 79 | 80 | 操作系统为了独立区分这两个特点,分派的单位通常是线程或轻量级进程,而拥有资源所有权的单位是进程。 81 | 82 | 多线程是指操作系统在单个进程内只吃多个并发执行路径的能力。在多线程环境中,进程只有一个与之关联的进程控制块和用户地址空间。但每个线程都有一个独立的栈和一个独立的线程控制块用于包含寄存器值,优先级和其他与线程相关的状态信息。 83 | 84 | **内核线程与用户线程** 85 | 86 | 用户线程的优点: 87 | 88 | - 线程的切换不需要切换到内核态,节省了状态转换的开销 89 | - 线程调度算法的实现更加简单 90 | - 用户线程可以在任何操作系统中运行,兼容性更好 91 | 92 | 用户线程的缺点: 93 | 94 | - 当一个用户线程执行系统调用时,这个进程中的所有线程都会被阻塞 95 | - 在纯粹的用户级线程策略中,一个多线程程序不能利用多处理器技术。内核只为一个进程分配一个处理器。 96 | 97 | 内核线程: 98 | 99 | 内核可以同时把一个进程中多个线程调度到多个处理器中;而且,如果进程中的一个线程被阻塞,内核可以调度进程中的另一个线程。 100 | 101 | ## 并发机制 102 | 103 | 操作系统为进程间的通信和同步提供了各种机制。管道,消息和共享内存提供了进程间传递数据的方法,而信号量和信号则用于其他进程的触发行为。 104 | 105 | - 管道 106 | 107 | 管道是一个固定字节大小的缓冲区,允许两个进程以生产者/消费者的模型进行通信。因此,这是一个FIFO队列,一个进程写,另一个进程读。只有有亲缘关系的进程才可以共享匿名管道,而不相关的进程只能共享命名管道。 108 | 109 | - 消息队列 110 | 111 | 每个进程都有与之关联的消息队列,其功能类似于信箱。 112 | 113 | - 共享内存 114 | 115 | 共享内存是虚存中由多个进程共享的一个公共内存块。 116 | 117 | - 信号量 118 | 119 | 操作系统通过PV原语来操作信号量。 120 | 121 | - 信号 122 | 123 | 进程间可以互相发送信号,内核也可能在内部发送信号。信号的传递是通过修改信号要发送到的进程所对应的**进程表**中的一个域来完成。只有在进程被唤醒继续运行时,或者进程准备从系统调用中返回时,才能处理信号。 124 | 125 | 126 | 127 | # 内存管理 128 | 129 | 内存管理技术: 130 | 131 | | 技术 | 说明 | 优势 | 缺点 | 132 | | ------ | ---------------------------------------- | ----------------------------------- | ------------------- | 133 | | 固定分区 | 在系统生成阶段,内存被划分成许多静态分区。进程可以被装入到大于或等于自身大小的分区 | 实现简单,开销小 | 有内部碎片;活动进程的最大数目是固定的 | 134 | | 动态分区 | 分区是动态生成的,每个进程可以被装入与自身大小正好相等的分区中 | 没有内部碎片,可以更充分地使用内存 | 由于需要压缩外部碎片,处理器利用率低 | 135 | | 简单分页 | 内存被划分成许多大小相等的页块;要装入进程,需要把进程包含的所有页都装入到内存中不一定连续的某些页块中 | 没有外碎片 | 有少量内碎片 | 136 | | 简单分段 | 每个进程被划分成许多段;要装入进程,需要把进程包含的所有段都装入到内存中不一定连续的某些动态分区中。 | 没有内碎片 | 存在外碎片 | 137 | | 虚拟内存分页 | 非驻留页在以后需要时自动调入内存 | 没有外碎片;支持更高道数的多道程序设计;巨大的虚存空间 | 复杂的内存管理开销 | 138 | | 虚拟内存分段 | 非驻留段在以后需要时自动调入内存 | 没有内碎片;支持更高道数的多道程序设计;巨大的虚存空间;支持保护和共享 | 复杂的内存管理开销 | 139 | 140 | ## 分页 141 | 142 | 内存被划分成大小固定的页框,操作系统必须为每个进程维护一个页表,建立虚拟空间中的页块到物理内存中的页块的映射,CPU使用页号和页内位移来计算物理地址。 143 | 144 | ![分页系统中的地址转换](分页系统中的地址转换.png) 145 | 146 | ## 分段 147 | 148 | 分页对用户透明,分段对用户是可见的。 149 | 在段式存储管理中内存空间被动态的划分为若干长度不一的**物理段**,每个物理段在内存对应一个起始地址,称为**段首址**,在每一个物理段中由0开始编址,称为**段内地址**。每一个进程都有一个唯一的段表,每个段表项包含段号,相应段在内存中的起始地址和段的长度。 150 | 151 | ![分段系统中的地址转换](分段系统中的地址转换.png) 152 | 153 | ![分段系统中的地址转换1](分段系统中的地址转换1.png) 154 | 155 | ## 段页式 156 | 157 | ![段页式系统中的地址转换](段页式系统中的地址转换.png) 158 | 159 | # I/O管理和磁盘调度 160 | 161 | ## I/O技术发展 162 | 163 | 执行I/O的三种技术: 164 | 165 | - 可编程I/O:处理器代表一个进程给I/O模块发送一个I/O命令;该进程进入忙等待,直到操作完成才可以继续执行 166 | - 中断驱动I/O 167 | - DMA方式;一个DMA模块控制内存和I/O模块之间的数据交换。 168 | 169 | ## DMA 170 | 171 | 当处理器想读或写一块数据时,它通过向DMA模块发送一下信息: 172 | 173 | - 是否请求读操作或写操作,通过CPU与DMA模块之间的读写控制线发送 174 | - 相关I/O设备地址,通过数据线传送 175 | - 操作内存的起始地址,在数据线上传送并保存在DMA模块的地址寄存器中 176 | - 读或写的字数,通过数据线传送,保存在其数据计数寄存器中 177 | 178 | DMA模块直接从内存或向内存传送整块数据,一次传送一个字,并不再需要通过CPU。传送结束后DMA给CPU发送一个中断信号。因此,只有在传送开始和结束时才会用到CPU。 179 | 180 | ![DMA结构图](DMA结构图.png) -------------------------------------------------------------------------------- /数据结构与算法/排序算法.md: -------------------------------------------------------------------------------- 1 | ## 快速排序 2 | 3 | ```java 4 | private void quickSort(int[] a,int left, int right) { 5 | //if (a.length <= 1) return; 6 | if (right <= left) return; 7 | int base = a[left]; 8 | int i = left, j = right; 9 | while (true) { 10 | while (a[j] >= base && i < j) 11 | j--; 12 | while (a[i] <= base && i < j) 13 | i++; 14 | if (i != j)//当i==j时就没必要交换了 15 | swap(a, i, j); 16 | else break; 17 | } 18 | swap(a, left, i); 19 | quickSort(a, left, i - 1); 20 | quickSort(a, i + 1, right); 21 | } 22 | ``` 23 | 24 | ## 归并排序 25 | 26 | ```java 27 | private void mergeSort(int[] array, int start, int end) { 28 | if (start >= end) return; 29 | int mid = (start + end) >> 1; 30 | mergeSort(array, start, mid); 31 | mergeSort(array, mid + 1, end); 32 | merge(array, start, mid, end); 33 | } 34 | 35 | private void merge(int[] array, int start, int mid, int end) { 36 | System.arraycopy(array, start, bak, start, end - start + 1); 37 | int i = start, j = mid + 1; 38 | for (int k = start; k <= end; k++) { 39 | if (i > mid) { 40 | array[k] = bak[j++]; 41 | } else if (j > end) { 42 | array[k] = bak[i++]; 43 | } else if (bak[j] < bak[i]) { 44 | array[k] = bak[j++]; 45 | } else { 46 | array[k] = bak[i++]; 47 | } 48 | } 49 | } 50 | ``` 51 | 52 | ## 堆排序 53 | 54 | ```java 55 | public void sort(int[] a) { 56 | int last = a.length - 1; 57 | for (int i = (last - 1) / 2; i >= 0; i--) { 58 | swiftDown_(a, i, last); 59 | } 60 | while (last != 0) { 61 | swap(a, 0, last--); 62 | swiftDown_(a, 0, last); 63 | } 64 | } 65 | 66 | // 调整最小堆 67 | private void swiftDown(int[] a, int p, int last) { 68 | while (true) { 69 | int left = p * 2 + 1; 70 | int right = p * 2 + 2; 71 | int sankVex = p; 72 | if (left <= last && a[sankVex] > a[left]) sankVex = left; 73 | if (right <= last && a[sankVex] > a[right]) sankVex = right; 74 | if (sankVex != p) { 75 | swap(a, p, sankVex); 76 | p = sankVex; 77 | } else break; 78 | } 79 | } 80 | 81 | protected void swap(int i, int j, int[] a) { 82 | int t = a[i]; 83 | a[i] = a[j]; 84 | a[j] = t; 85 | } 86 | ``` 87 | 88 | ## 冒泡排序 89 | 90 | ```java 91 | public void sort(int[] a) { 92 | if (a == null) return; 93 | for (int i = 0; i < a.length; i++) { 94 | for (int j = 1; j < a.length - i; j++) { 95 | if (a[j] < a[j - 1]) { 96 | int temp = a[j]; 97 | a[j] = a[j - 1]; 98 | a[j - 1] = temp; 99 | } 100 | } 101 | } 102 | } 103 | ``` 104 | 105 | ## 选择排序 106 | 107 | ```java 108 | public void sort(int[] a) { 109 | if (a == null) return; 110 | for (int i = 0; i < a.length; i++) { 111 | for (int j = i; j < a.length; j++) { 112 | if (a[i] > a[j]) { 113 | int temp = a[i]; 114 | a[i] = a[j]; 115 | a[j] = temp; 116 | } 117 | } 118 | } 119 | } 120 | ``` 121 | 122 | ## 插入排序 123 | 124 | ```java 125 | public void sort(int[] a) { 126 | for (int i = 1; i < a.length; i++) { 127 | int insertNum = a[i]; 128 | int j = i - 1; 129 | while (j >= 0 && a[j] > insertNum) { 130 | a[j + 1] = a[j]; 131 | j--; 132 | } 133 | a[j + 1] = insertNum; 134 | } 135 | } 136 | ``` 137 | 138 | 139 | 140 | ## 总结 141 | 142 | ![排序算法](排序算法.png) 143 | 144 | 时间复杂度比较: 145 | 146 | | **排序方法** | **平均时间** | **最好时间** | **最坏时间** | 147 | | ---------- | --------- | -------- | -------- | 148 | | 桶排序(不稳定) | O(n) | O(n) | O(n) | 149 | | 基数排序(稳定) | O(n) | O(n) | O(n) | 150 | | 归并排序(稳定) | O(nlogn) | O(nlogn) | O(nlogn) | 151 | | 快速排序(不稳定) | O(nlogn) | O(nlogn) | O(n^2) | 152 | | 堆排序(不稳定) | O(nlogn) | O(nlogn) | O(nlogn) | 153 | | 希尔排序(不稳定) | O(n^1.25) | | | 154 | | 冒泡排序(稳定) | O(n^2) | O(n) | O(n^2) | 155 | | 选择排序(不稳定) | O(n^2) | O(n^2) | O(n^2) | 156 | | 直接插入排序(稳定) | O(n^2) | O(n) | O(n^2) | 157 | 158 | 159 | 160 | ## 问题 161 | 162 | - 为什么快速排序的时间复杂度是o(nlogn)? 163 | 164 | 每次quicksort过程中,有一个partition操作和两个递归分支。因此一个完整的quicksort相当于在构造一个颗满二叉树。树高是logn,而树中每一层加起来的时间复杂度都是o(n),因此整个快速排序的时间复杂度为o(nlogn)。 -------------------------------------------------------------------------------- /Java/基础/Java泛型.md: -------------------------------------------------------------------------------- 1 | 【整理成博客】 2 | http://blog.csdn.net/tellh/article/details/71308245 3 | 4 | 【草稿】 5 | 6 | ## 泛型的作用 7 | 8 | 泛型实现参数化类型的概念,使代码可以应用于多种类型,解耦类或方法与所使用的类型之间的约束。 9 | 10 | ## 泛型不支持协变 11 | 12 | ```java 13 | class Fruit{} 14 | class Apple extends Fruit{} 15 | Fruit[] fruit = new Apple[10]; // OK 16 | ArrayList flist = new ArrayList(); // Not OK! 17 | ArrayList flist = new ArrayList();// 使用通配符解决协变问题 18 | ``` 19 | 20 | ## 上界通配符 21 | 22 | ```java 23 | List flist = Arrays.asList(new Apple()); 24 | Apple a = (Apple)flist.get(0); // No warning 25 | flist.contains(new Apple()); // Argument is ‘Object’ 26 | flist.indexOf(new Apple()); // Argument is ‘Object’ 27 | //flist.add(new Apple()); 无法编译 28 | ``` 29 | 30 | 通配符 `List` 表示某种特定类型 ( `Fruit` 或者其子类 ) 的 List,但是并不关心(不知道)这个实际的具体类型到底是什么。注意,并不意味着这个List持有Fruit的任意类型。 31 | 32 | 由于List的具体类型并不确定,因此带有泛型类型参数的方法都无法正常调用。比如`add(T item);`,即使是传Object也无法通过编译。 33 | 34 | 但返回类型就是Fruit,与上界类型一样。 35 | 36 | ## 下界通配符 37 | 38 | ```java 39 | static void add(List list) { 40 | // list.add(new Fruit()); // 无法编译 41 | Object object = list.get(0);// pass 42 | } 43 | ``` 44 | 如何向泛型类型中 “写入” ( 传递对象给方法参数) 。 45 | ```java 46 | public static void copy(List dest, List src) { 47 | for (int i = 0; i < src.size(); i++) 48 | dest.add(i, src.get(i)); 49 | } 50 | ``` 51 | 52 | 53 | 54 | ## 无界通配符 55 | 56 | `List list` 表示 `list` 是持有某种特定类型的 List,但是不知道具体是哪种类型。而单独的 `List list` ,也就是没有传入泛型参数,表示这个 list 持有的元素的类型是 `Object`。 57 | 58 | ## getGenericSuperclass 59 | 60 | ```java 61 | public class SubClass extends Base { } 62 | ``` 63 | 64 | 对SubClass.class调用`getGenericSuperclass`可以获取到T所绑定的类型。 65 | 66 | ```java 67 | Type type = SubClass.class.getGenericSuperclass(); 68 | Type targ = ((ParameterizedType) type).getActualTypeArguments()[0]; 69 | System.out.println(type); // SubClass 70 | System.out.println(targ); // class java.lang.String 71 | ``` 72 | 73 | 具体用法可以参考Gson和Guice的源码: 74 | 75 | https://github.com/google/guice/blob/abc78c361d9018da211690b673accb580a52abf2/core/src/com/google/inject/TypeLiteral.java#L94 76 | 77 | https://github.com/google/gson/blob/master/gson/src/main/java/com/google/gson/internal/%24Gson%24Types.java 78 | 79 | ## 桥方法 80 | 81 | 为了使Java的泛型方法生成的字节码与1.5以前的字节码相兼容,由编译期自己生成的方法。顾名思义,桥方法是一座桥,沟通着泛型与多态。 82 | 83 | 可以通过`Method.isBridge()`方法来判断一个方法是否是桥接方法,在字节码中桥接方法会被标记为`ACC_BRIDGE`和`ACC_SYNTHETIC`。 84 | 85 | ```java 86 | public class Fruit { 87 | T value; 88 | public T getValue() { 89 | return value; 90 | } 91 | } 92 | public class Apple extends Fruit { 93 | @Override 94 | public String getValue() { 95 | return "foo was call"; 96 | } 97 | } 98 | ``` 99 | 100 | 反编译生成的字节码: 101 | 102 | ``` 103 | public class Apple extends Fruit { 104 | public Apple(); 105 | Code: 106 | 0: aload_0 107 | 1: invokespecial #1 // Method Fruit."":()V 108 | 4: return 109 | 110 | public java.lang.String getValue(); 111 | Code: 112 | 0: ldc #2 // String calling 113 | 2: areturn 114 | 115 | public java.lang.Object getValue(); 116 | Code: 117 | 0: aload_0 118 | 1: invokevirtual #3 // Method getValue:()Ljava/lang/String; 119 | 4: areturn 120 | } 121 | ``` 122 | 123 | 编译器为我们自动生成了有一个桥方法,这个桥方法返回类型为Object,内部调用了我们自定义的另一个getValue方法。 124 | 125 | 在Java代码中,方法的特征签名只包括方法名称,参数顺序和参数类型,而字节码中的特征签名还包括方法返回值和受查异常表。因此,桥方法`public Object getValue()`与`public String getValue()`是可以被JVM区分而在同一个Class文件中共存的。 126 | 127 | 由于编译期泛型擦除机制,在父类中带泛型参数的方法会被替换成Object类型。要让子类重写父类带泛型参数的方法,需要通过桥方法直接复写父类的方法,然后桥方法再调用子类自定义的方法,就以上面作为例子,子类Apple中的桥方法`public Object getValue()`直接override父类Fruit的`public Object getValue()`,然后桥方法内部再调用子类Apple的`public String getValue()`。因此,Java利用桥方法在保证多态机制不被破坏情况下实现了泛型。 128 | 129 | ## 所有信息都被擦除了吗 130 | 131 | 所谓的擦除,仅仅是对方法的Code属性中的字节码(也就是方法内的逻辑代码)进行擦除,实际上元数据(类和接口的声明,类字段的声明)中还是保留了泛型信息。 132 | 133 | ```java 134 | public class GenericClass { // 1 135 | private List list; // 2 136 | private Map map; // 3 137 | public U genericMethod(Map m) { // 4 138 | List list = new ArrayList<>(); // 5 139 | return null; 140 | } 141 | } 142 | ``` 143 | 144 | 位于声明一侧的,源码里写了什么到运行时就能看到什么; 145 | 位于使用一侧的,源码里写什么到运行时都没了。 146 | 147 | 上面的代码中,1-4的T和U是保留在Class文件当中的,源码是什么,那么通过反射获取得到的就是什么。也就是说,在运行时,是无法获取到具体的T和U是什么类型的。 148 | 149 | 但运行时,在方法内部的局部变量的泛型信息是被全部擦除的。 150 | 151 | **参考**: 152 | 153 | - https://segmentfault.com/a/1190000005337789 154 | - http://rednaxelafx.iteye.com/blog/586212 155 | - http://blog.csdn.net/mhmyqn/article/details/47342577# 156 | - http://blog.csdn.net/lonelyroamer/article/details/7868820 -------------------------------------------------------------------------------- /Android/高效加载Bitmap.md: -------------------------------------------------------------------------------- 1 | ## Bitmap 2 | 3 | 一张png或jpg图片经过解码后会变成Bitmap对象,它会将图片上的每一个像素都保存在内存中,导致稍有不慎就会创建出一个占用内存非常大的Bitmap对象,从而导致加载过慢,还会有内存溢出的风险。 4 | 5 | BitmapFactory类提供了4类方法:decodeFile,decodeResource,decodeStream和decodeByteArray来加载一个Bitmap对象。 6 | 7 | ### decodeResource与decodeFile 8 | 9 | **decodeFile()**用于读取SD卡上的图,得到的是图片的原始尺寸 10 | **decodeResource()**用于读取Res、Raw等资源,得到的是图片的原始尺寸 * 缩放系数 11 | 12 | **缩放系数的计算** 13 | 14 | 通过BitmapFactory.Options的这几个参数可以调整缩放系数。 15 | 16 | ```java 17 | public class BitmapFactory { 18 | public static class Options { 19 | public boolean inScaled; // 默认true 20 | public int inDensity; // 无dpi的文件夹下默认160 21 | public int inTargetDensity; // 取决具体屏幕 22 | } 23 | } 24 | ``` 25 | 26 | **inScaled属性** 27 | 28 | 如果inScaled设置为false,则不进行缩放,解码后的图片与原图一致。 29 | 30 | 如果inScaled设置为true,则根据inDensity和inTargetDensity计算缩放系数。 31 | 32 | 计算公式为,缩放系数=inTargetDensity/inDensity 33 | 34 | 放在不同的dpi文件夹会影响inDensity,进而影响加载出来图片的大小。 35 | 36 | ### Bitmap占用的内存 37 | 38 | 一张图片Bitmap所占用的内存 = 图片长度 x 图片宽度 x 一个像素点占用的字节数 39 | 40 | 而Bitmap.Config,正是指定单位像素占用的字节数的重要参数。 41 | 42 | ALPHA_8 43 | 表示8位Alpha位图,即A=8,一个像素点占用1个字节,它没有颜色,只有透明度 44 | ARGB_4444 45 | 表示16位ARGB位图,即A=4,R=4,G=4,B=4,一个像素点占4+4+4+4=16位,2个字节 46 | ARGB_8888 47 | 表示32位ARGB位图,即A=8,R=8,G=8,B=8,一个像素点占8+8+8+8=32位,4个字节 48 | RGB_565 49 | 表示16位RGB位图,即R=5,G=6,B=5,它没有透明度,一个像素点占5+6+5=16位,2个字节 50 | 51 | ## 先压缩后加载 52 | 53 | 根据传入的宽和高(ImageView),通过设置BitmapFactory.Options中inSampleSize的值就可以实现可以实现对图片的压缩。当inSampleSize为1时,采样后的图片大小与原图一致;当i你sampleSize大小为2时,那么采样后的图片其宽高均为原图大小的二分之一,像素数为原图的四分之一。因此,inSampleSize大于一才有缩小效果,缩放比例为1/(inSampleSize的2次方)。 54 | 55 | 具体流程如下: 56 | 57 | 1. 将BitmapFactory.Options中的inJustDecodeBounds参数设为true,准备解析图片的原始宽高信息。 58 | 2. 从BitmapFactory.Options中去除图片的原始宽高信息 59 | 3. 根据采样率规则结合目标View尺寸大小计算出采样率inSampleSize 60 | 4. 将BitmapFactory.Options的inJustDecodeBounds参数设为false,然后重新加载图片。 61 | 62 | ```java 63 | public static int calculateInSampleSize(BitmapFactory.Options options, 64 | int reqWidth, int reqHeight) { 65 | // 源图片的高度和宽度 66 | final int height = options.outHeight; 67 | final int width = options.outWidth; 68 | int inSampleSize = 1; 69 | if (height > reqHeight || width > reqWidth) { 70 | // 计算出实际宽高和目标宽高的比率 71 | final int heightRatio = Math.round((float) height / (float) reqHeight); 72 | final int widthRatio = Math.round((float) width / (float) reqWidth); 73 | // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高 74 | // 一定都会大于等于目标的宽和高。 75 | inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; 76 | } 77 | return inSampleSize; 78 | } 79 | public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, 80 | int reqWidth, int reqHeight) { 81 | // 第一次解析将inJustDecodeBounds设置为true,来获取图片大小 82 | final BitmapFactory.Options options = new BitmapFactory.Options(); 83 | options.inJustDecodeBounds = true; 84 | BitmapFactory.decodeResource(res, resId, options); 85 | // 调用上面定义的方法计算inSampleSize值 86 | options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); 87 | // 使用获取到的inSampleSize值再次解析图片 88 | options.inJustDecodeBounds = false; 89 | return BitmapFactory.decodeResource(res, resId, options); 90 | } 91 | // 加载图片 92 | mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100)); 93 | ``` 94 | 95 | ## Cache缓存 96 | 97 | ### LruCache 98 | 99 | ```java 100 | mMemoryCache = new LruCache(cacheSize) { 101 | @Override 102 | protected int sizeOf(String key, Bitmap bitmap) { 103 | // 重写此方法来衡量每张图片的大小,默认返回图片数量。 104 | return bitmap.getByteCount() / 1024; 105 | } 106 | }; 107 | ``` 108 | 109 | LruCache内部维护一个LinkedHashMap以**强引用**的方式存储外界得缓存对象。 110 | 111 | 不同引用得区别如下: 112 | 113 | - 软引用:当一个对象只有软引用存在时,系统内存不足时该对象可能会被gc 114 | - 弱引用:当一个对象只有弱引用存在时,此对象随时会被gc回收 115 | 116 | 关于LruCache相关总结: 117 | 118 | - LruCache 是通过 LinkedHashMap 构造方法的第三个参数的 `accessOrder=true` 实现了 `LinkedHashMap` 的数据排序**基于访问顺序** (最近访问的数据会在链表尾部),在容量溢出的时候,将链表头部的数据移除。从而,实现了 LRU 数据缓存机制。 119 | - LruCache 在内部的get、put、remove包括 trimToSize 都是线程安全的(因为都上锁了)。 120 | - LruCache 自身并没有释放内存,将 LinkedHashMap 的数据移除了,如果数据还在别的地方被引用了,还是有泄漏问题,还需要手动释放内存。 121 | - 覆写 `entryRemoved` 方法能知道 LruCache 数据移除是是否发生了冲突,也可以去手动释放资源。 122 | - maxSize` 和 `sizeOf(K key, V value)` 方法的覆写息息相关,必须相同单位。( 比如 maxSize 是7MB,自定义的 sizeOf 计算每个数据大小的时候必须能算出与MB之间有联系的单位 ) 123 | 124 | 每次调用LinkedHashMap的get方法时,如果`accessOrder=true` ,那么它会调用`makeTail`方法,将entry移到链表的尾部。 125 | 126 | 参考LruCache的源码解析: 127 | 128 | https://github.com/LittleFriendsGroup/AndroidSdkSourceAnalysis/blob/master/article/LruCache%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90.md 129 | 130 | ### DiskLruCache 131 | 132 | http://blog.csdn.net/guolin_blog/article/details/28863651 133 | 134 | ## 大图加载 135 | 136 | 通过BitmapRegionDecoder这个类实现按区域加载Bitmap。 137 | 138 | 139 | 140 | ## 参考 141 | 142 | - [Android Bitmap 知识点梳理](http://blog.csdn.net/u012124438/article/details/72614602) -------------------------------------------------------------------------------- /C&C++/C Primer.md: -------------------------------------------------------------------------------- 1 | ## 概览 2 | 3 | ### 编译过程 4 | 5 | ![C的编译过程](C的编译过程.png) 6 | 7 | ### 头文件 8 | 9 | 在C的语法里,函数必须先定义后使用,或者在使用前先声明,后边再定义。.h头文件包含函数的声明,实现文件在同名的.c文件。编译时,编译器一次只能处理一个文件,它会依次将所有的.c文件编译成目标文件,然后链接器将它们链接成可执行程序。如果不引入头文件,而是写成`#include "xxx.c"`,这样虽然编译时不会出错,但链接时就会出错了,因为这样会重复定义了(multiple definition)。 10 | 11 | ## printf()和scanf()函数 12 | 13 | ![prinf()转换说明符](prinf()转换说明符.png) 14 | 15 | ```c 16 | char str[30]; 17 | int age; 18 | float assets; 19 | scanf("%d %f", &age, &assets); 20 | scanf("%s", str); 21 | printf("%d,%f", age, assets); 22 | printf("Input string is %s", str); 23 | ``` 24 | 25 | 常用的字符串输入输出函数有:gets,puts等等。 26 | 27 | ## 指针和数组 28 | 29 | ```c 30 | char str[] = "Hello World!"; 31 | printf("%s\n", str == &str[0] ? "true" : "false");// 相同的地址 32 | printf("%s\n", str + 1 == &str[1] ? "true" : "false");// 相同的地址 33 | printf("%s\n", *(str + 1) == str[1] ? "true" : "false");// 相同的值 34 | ``` 35 | 36 | **const 指针与指向const的指针** 37 | 38 | const 所修饰的右边就是const类型的。 39 | 40 | ```c 41 | /*定义指向const的指针(指针指向的内容不能被修改)*/ 42 | const int *p1; 43 | int const *p2; 44 | /*定义const指针(由于指针本身的值不能改变所以必须得初始化)*/ 45 | int *const p3; 46 | /*指针本身和它指向的内容都是不能被改变的所以也得初始化*/ 47 | const int *const p4; 48 | int const *const p5; 49 | ``` 50 | 51 | **二维数组与指针** 52 | 53 | ```c 54 | int a[4][2] = { 55 | {2, 4}, 56 | {6, 8}, 57 | {1, 3}, 58 | {5, 7} 59 | }; 60 | printf("%d", *(*(a + 2) + 1)); 61 | ``` 62 | 63 | | `a+2` | 二维数组的第三个子数组的地址 | 64 | | ----------------- | -------------------------------------- | 65 | | `*(a+2)` | 二维数组的第三个子数组,包含2个int的数组,也是这个数组第一个元素的首地址 | 66 | | `*(a + 2) + 1` | 二维数组的第三个子数组的第二个元素的地址 | 67 | | `*(*(a + 2) + 1)` | 二维数组的第三个子数组的第二个元素 | 68 | 69 | 70 | 71 | ## 内存管理 72 | 73 | ### 存储类 74 | 75 | C是没有命名空间的。C使用作用域,链接和存储时期来定义5种存储类。 76 | 77 | | 存储类 | 时期 | 作用域 | 链接 | 声明方式 | 78 | | --------- | ---- | ---- | ---- | ---------------- | 79 | | 自动 | 自动 | 代码块 | 空 | 代码块内 | 80 | | 具有外部链接的静态 | 静态 | 文件 | 外部 | 所有函数之外 | 81 | | 具有内部链接的静态 | 静态 | 文件 | 内部 | 所有函数之外,使用static | 82 | | 空链接 | 静态 | 代码块 | 空 | 代码块内,使用static关键字 | 83 | 84 | ```c 85 | /*People.c*/ 86 | int vars1; // 外部链接 87 | static int vars2; // 内部链接,外部不能访问,作用域在文件内 88 | /*Main.c*/ 89 | extern int vars1; 90 | // extern int vars2; // 不能访问内部链接静态变量 91 | int run() { 92 | static int a = 0; // 变量a是run函数内的局部变量,但每次调用同一个函数,都是访问同一个变量。 93 | a++; 94 | return a; 95 | } 96 | ``` 97 | 98 | 99 | 100 | ### 内存分配malloc()和free() 101 | 102 | malloc接受一个参数:所需内存字节数,然后malloc()找到可用内存中一个大小合适的块,返回那块内存第一个字节的地址。 103 | 104 | ```c 105 | double *items = malloc(n * sizeof(double)); 106 | ``` 107 | 108 | 每一次malloc分配的内存都需要调用一次free方法来释放内存。 109 | 110 | ## 结构体 111 | 112 | 内存对齐:按字对齐,假如一个字是4个字节,那么一个变量所占内存的偏移量必须是4byte的整数倍。 113 | 114 | ```c 115 | typedef struct { 116 | int a; 117 | short b; 118 | char c; 119 | } Library1; 120 | /** 121 | * +-------+-------+-------+-------+ 122 | * | a | 123 | * +-------+-------+-------+-------+ 124 | * | b | c | pad | 125 | * +-------+-------+-------+-------+ 126 | */ 127 | 128 | typedef struct { 129 | char a[3]; 130 | short b; 131 | long c;// long占4个字节 132 | char d[3]; 133 | } Library2; 134 | /** 135 | * +-------+-------+-------+-------+ 136 | * | a | pad1 | 137 | * +-------+-------+-------+-------+ 138 | * | b | pad2 | 139 | * +-------+-------+-------+-------+ 140 | * | c | 141 | * +-------+-------+-------+-------+ 142 | * | d | pad3 | 143 | * +-------+-------+-------+-------+ 144 | */ 145 | 146 | typedef struct { 147 | int a; 148 | char c[3]; 149 | short b; 150 | } Library; 151 | /** 152 | * +-------+-------+-------+-------+ 153 | * | a | 154 | * +-------+-------+-------+-------+ 155 | * | c | pad1 | 156 | * +-------+-------+-------+-------+ 157 | * | b | pad2 | 158 | * +-------+-------+-------+-------+ 159 | */ 160 | ``` 161 | 162 | ## 函数指针 163 | 164 | 函数也有地址,函数的机器语言实现是由载入到内存的代码组成,指向函数的指针中保持着函数代码起始处的地址。 165 | 166 | ```c 167 | int add(int a, int b); 168 | int call(int (*pf)(int a, int b)); 169 | int call(int (*pf)(int, int)) { 170 | return pf(1, 2); 171 | } 172 | int main() { 173 | printf("%d", call(add)); 174 | return 0; 175 | } 176 | ``` 177 | 178 | ## C预处理器 179 | 180 | ### #define 181 | 182 | ```c 183 | #define P(x) printf("%d\n",x) 184 | #define AVG(X, Y) (X+Y)/2 185 | #define PI 3.14 186 | int main() { 187 | P(AVG(2, 6)); 188 | return 0; 189 | } 190 | ``` 191 | 192 | ### #include 193 | 194 | 预处理器发现`#include`指令后,就会寻找后跟的文件名并把这个文件的内容包含到当前文件中,被包含文件的文本将替换源代码文件中的`#include`指令。 195 | 196 | | `#include ` | 搜索系统目录 | 197 | | -------------------------- | ------------- | 198 | | `#include "hot.h"` | 搜索当前工作目录 | 199 | | `#include "/usr/biff/p.h"` | 搜索/usr/biff目录 | 200 | 201 | 头文件通常包含: 202 | 203 | - 明显常量 204 | - 宏函数 205 | - 函数声明 206 | - 结构模板定义 207 | - 类型定义 208 | 209 | -------------------------------------------------------------------------------- /Java/基础/Java IO.md: -------------------------------------------------------------------------------- 1 | ## IO底层原理 2 | 3 | ![data-buffering-at-os-level](data-buffering-at-os-level.png) 4 | 5 | 通常,进程执行操作系统的I/O请求包括数据从缓冲区排出(写操作)和数据填充缓冲区(读操作)。以Input为例,首先,进程要求其缓冲通过read()系统调用填满。这个系统调用导致内核向磁盘控制硬件发出一条命令要从磁盘获取数据。磁盘控制器通过DMA直接将数据写入内核的内存缓冲区,不需要主CPU进一步帮助。当请求read()操作时,一旦磁盘控制器完成了缓存的填充,内核从内核空间的临时缓存拷贝数据到进程指定的缓存中。 6 | 7 | 由于现代操作系统都是用了虚拟内存,这意味着: 8 | 9 | 1. 多个虚拟地址可以映射到相同的物理地址 10 | 2. 进程的虚拟地址空间可以大于实际可用的硬件内存 11 | 12 | 因此,用户空间内的buffer与内核空间内的buffer可以映射到同一块物理内存当中。从而避免了一次拷贝。 13 | 14 | ![buffer内存映射](buffer内存映射.png) 15 | 16 | ## IO模型 17 | 18 | - **同步模型(synchronous IO)** 19 | - 阻塞IO(bloking IO) 20 | - 非阻塞IO(non-blocking IO) 21 | - 多路复用IO(multiplexing IO) 22 | - 信号驱动式IO(signal-driven IO) 23 | - **异步IO(asynchronous IO)** 24 | 25 | ![IO模型](IO模型.jpg) 26 | 27 | 参考:[也谈IO模型](http://mp.weixin.qq.com/s?__biz=MjM5NzMyMjAwMA==&mid=2651477804&idx=1&sn=c764e6318eb5ffd8fc045f5f786e0e6e&chksm=bd253b538a52b245ec41022c13ef15190afe549b4d1180814a08544d88a0805840903d32b8e5&mpshare=1&scene=23&srcid=1127ZQuCkomrtx4j8eLOrKEC#rd) 28 | 29 | http://www.jianshu.com/p/486b0965c296# 30 | 31 | ## NIO 32 | 33 | NIO的速度提高来自于所使用的结构更接近于操作系统执行IO的方式:通道和缓冲器。 34 | 35 | 我们可以把数据源比喻成煤矿,通道是一个通往矿藏的地道,而缓冲器则是运输煤炭的卡车。也就是说,IO操作直接与缓冲器交互,并把缓冲器派送到通道。通道要么从缓冲器获取数据,要么向缓冲器发送数据。 36 | 37 | ### 示例 38 | 39 | ByteBuffer是唯一直接与通道交互的缓冲器,只能保存字节类型的数据。旧IO类库中FileInputStream,FileOutputStream以及用于既读又写的RandomAccessFile都可以用来产生FileChannel。 40 | 41 | ```java 42 | public class GetChannel { 43 | public static final int BSIZE = 1024; 44 | public static void main(String[] args) throws IOException { 45 | FileChannel fc = new FileOutputStream(new File("data.txt")).getChannel(); 46 | fc.write(ByteBuffer.wrap("Some text ".getBytes())); 47 | fc.close(); 48 | fc = new RandomAccessFile(new File("data.txt"), "rw").getChannel(); 49 | fc.position(fc.size()); 50 | fc.write(ByteBuffer.wrap("Some more ".getBytes())); 51 | fc.close(); 52 | fc = new FileInputStream(new File("data.txt")).getChannel(); 53 | ByteBuffer buffer = ByteBuffer.allocate(BSIZE); 54 | fc.read(buffer); 55 | buffer.flip(); //做好数据被读取的准备 56 | while (buffer.hasRemaining()) { 57 | System.out.print((char) buffer.get()); 58 | } 59 | fc.close(); 60 | } 61 | } 62 | ``` 63 | 64 | 简单的文件复制操作: 65 | 66 | ```java 67 | public static void copy(FileInputStream in, FileOutputStream out) throws IOException { 68 | FileChannel fcin = in.getChannel(); 69 | FileChannel fcout = out.getChannel(); 70 | fcin.transferTo(0, fcin.size(), fcout); 71 | // fcout.transferFrom(fcin, 0, fcin.size()); 72 | } 73 | 74 | public static void copy_(FileInputStream in, FileOutputStream out) throws IOException { 75 | FileChannel fcin = in.getChannel(); 76 | FileChannel fcout = out.getChannel(); 77 | ByteBuffer buffer = ByteBuffer.allocate(1024); 78 | while ((fcin.read(buffer) != -1)) { 79 | buffer.flip(); // Prepare for writing 80 | fcout.write(buffer); 81 | buffer.clear(); // Prepare for reading 82 | } 83 | } 84 | ``` 85 | 86 | ### Buffer 87 | 88 | #### 四个索引 89 | 90 | 缓冲区是对Java原生数组的对象封装,Buffer有四个索引:mark,position,limit和capacity。 91 | 92 | 容量(Capacity):缓冲区能够容纳的数据元素的最大数量。初始设定后不能更改。 93 | 上界(Limit):缓冲区中第一个不能被读或者写的元素位置。或者说,缓冲区内现存元素的上界。 94 | 位置(Position):缓冲区内下一个将要被读或写的元素位置。在进行读写缓冲区时,位置会自动更新。 95 | 标记(Mark):一个备忘位置。初始时为“未定义”,调用mark时mark=positon,调用reset时position=mark。 96 | 97 | ![Buffer-Mode](Buffer-Mode.png) 98 | 99 | #### 操作函数 100 | 101 | 所有Buffer都有get和put函数,用来写入和读取数据。注意,每put一个元素,position自动加1;而每get一个元素,position也自动加1。 102 | 103 | flip()就是从写入转为读出前的一个设置buffer属性的操作,其意义是将limit=position,position=0。 104 | 105 | compact()方法是为了将读取了一部分的buffer,其剩下的部分整体挪动到buffer的头部(即从0开始的一段位置),便于后续的写入或者读取。其含义为limit=limit-position,position=0。 106 | 107 | ### Channel 108 | 109 | 通道代表一个通向具有独立IO能力实体的连接。 110 | 111 | **1)通道是一种高效传输数据的管道,2)通道的一端(接收端或发送端)必须是字节缓冲区,3)另一端则是拥有IO能力的实体,4)通道本身不能存储数据,5)且往往通过流或套接字来创建,6)一旦创建,则通道与之形成一一对应的依赖关系。** 112 | 113 | 通道主要分为文件通道和Socket通道。文件通道是通过文件流对象来获取的,只支持阻塞模式。Socket通道支持阻塞模式和非阻塞模式。 114 | 115 | ### Selector 116 | 117 | Selector是Java NIO中的一个组件,用于检查一个或多个NIO Channel的状态是否处于可读、可写。如此可以实现单线程管理多个channels,也就是可以管理多个网络链接。Selector在Linux等主流操作系统上是通过epoll实现的。 118 | 119 | ![Selector](Selector.png) 120 | 121 | 具体的实现流程如下: 122 | 123 | 1. 创建ServerSocketChannel监听客户端连接并绑定监听端口,设置为非阻塞模式。 124 | 2. 创建Reactor线程,创建多路复用器(Selector)并启动线程。 125 | 3. 将ServerSocketChannel注册到Reactor线程的Selector上。监听accept事件。 126 | 4. Selector在线程run方法中无线循环轮询准备就绪的Key。 127 | 5. Selector监听到新的客户端接入,处理新的请求,完成tcp三次握手,建立物理连接。 128 | 6. 将新的客户端连接注册到Selector上,监听读操作。读取客户端发送的网络消息。 129 | 7. 客户端发送的数据就绪则读取客户端请求,进行处理。 130 | 131 | ### Pipe管道 132 | 133 | Pipe管道的概念最先应该出现在Unix系统里,用来表示连接不同进程间的一种单向数据通道,很多Unix系统都提供了支持管道的API。Java NIO借用了这个概念,发明了NIO中的Pipe,它是指同一个Java进程内,不同线程间的一种单向数据管道,其sink端通道写入数据,source端通道则读出数据,其间可以保证数据按照写入顺序到达。 134 | 135 | ![Pipe管道](Pipe管道.png) 136 | 137 | ## 参考 138 | 139 | - https://zhuanlan.zhihu.com/p/23488863?utm_source=qq&utm_medium=social 140 | - http://www.jianshu.com/p/a9b2fec31fd1 141 | - http://wiki.jikexueyuan.com/project/java-nio-zh/java-nio-selector.html 142 | - https://zhuanlan.zhihu.com/p/25701512 143 | - http://www.importnew.com/14111.html 144 | - http://www.rowkey.me/blog/2016/01/18/io-model/ -------------------------------------------------------------------------------- /数据库/数据库的基础知识.md: -------------------------------------------------------------------------------- 1 | ## 什么是事务? 2 | 3 | 事务(Transaction)是**并发控制的基本单位**。所谓的事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。事务是数据库维护数据一致性的单位,在每个事务结束时,都能保持数据一致性。 4 | 5 | ## 事务的特性 6 | 7 | 1. 原子性 Atomicity:当你对数据有任何操作的时候,所有的操作需要都成功反之则全不成功。 8 | 2. 一致性 Consistency:一致性,即在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏,使得系统从一个一致的状态转换到另一个一致状态。 9 | 3. 隔离性 Isolation:并发的事务是相互隔离的,互不影响的。 10 | 4. 持久性 Durability:当系统或介质发生故障时,确保已提交事务的更新不能丢失。 11 | 12 | 13 | 14 | ## 事务的隔离界别 15 | 16 | ### 脏读,不可重复读,幻读 17 | 18 | **脏读**:一个事务读取到另一事务未提交的更新新据。当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有 19 | 提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据, 那么另 20 | 外一个事务读到的这个数据是脏数据,依据脏数据所做的操作也可能是不正确的。 21 | 22 | **不可重复读**:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的 23 | 更新数据。相反,“可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务 24 | 已提交的更新数据。 25 | 26 | **幻读**:事务T1执行一次查询,然后事务T2新插入一行记录,这行记录恰好可以满足T1所使用的查询的条件。然后T1又使用相同的查询再次对表进行检索,但是此时却看到了事务T2刚才插入的新行。这个新行就称为“幻像”,因为对T1来说这一行就像突然出现的一样。 27 | 28 | ### 隔离级别 29 | 30 | 1. ISOLATION_READ_UNCOMMITTED:在并发的事务中,它充许一个事务可以读到另一个事务未提交的更新数据。(会出现脏读,不可重复读和幻读) 31 | 2. ISOLATION_READ_COMMITTED:读已提交数据,保证在并发的事务中,一个事务修改的数据提交后才能被另外一个事务读取到。但其他事务可以在当前事务未提交前更新或插入数据。(会出现不可重复读和幻读) 32 | 3. ISOLATION_REPEATABLE_READ:规定在当前事务提交之前其他事务不能对数据库进行update操作,防止了不可重复读,但不能避免有幻读。 33 | 4. ISOLATION_SERIALIZABLE:最严的事务隔离界别,规定事务之间的执行时串行执行的。 34 | 35 | 36 | 37 | ## 数据库范式 38 | 39 | 1NF:字段是最小的的单元不可再分 40 | 2NF:满足1NF,表中的字段必须完全依赖于全部主键而非部分主键,消除非主属性对码的部分依赖 41 | 3NF:满足2NF,非主键外的所有字段必须互不依赖,消除非主属性对码的传递依赖 42 | BCNF: 消除主属性对码的部分依赖和传递依赖。 43 | 44 | ## 索引是什么?作用以及优缺点? 45 | 46 | 索引是对数据库表中一或多个列的值进行排序的结构,能加快数据的检索。 47 | 48 | **MySQL**数据库几个基本的索引类型:普通索引、唯一索引、主键索引、全文索引 49 | 50 | - 主键索引 51 | 52 | 一种特殊的唯一索引,不允许有空值。 53 | 54 | - 唯一索引 55 | 56 | 唯一索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。 57 | 58 | - 全文索引 59 | 60 | 广泛应用于搜索引擎。 61 | 62 | 作用以及优缺点: 63 | 64 | - 索引加快数据库的检索速度 65 | - 索引增加了插入、删除、修改等维护任务的时间开销 66 | - 唯一索引可以确保每一行数据的唯一性 67 | - 通过使用索引,可以在查询的过程中使用优化隐藏器,提高系统的性能 68 | - 索引需要占物理和数据空间 69 | - 每当有记录在表中增减或索引列被修改时,索引本身也会被修改 70 | 71 | 参考:[MySQL索引及查询优化总结](http://blog.csdn.net/qcloudcommunity/article/details/71227006) 72 | 73 | ## 数据库的乐观锁和悲观锁 74 | 75 | 乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段。 76 | 77 | - 悲观锁:假定会发生并发冲突,在对任意记录进行修改前,先尝试为该记录加上排他锁。 78 | - 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性(利用数据库版本号或时间戳)。 79 | 80 | ### 悲观锁 81 | 82 | 悲观并发控制实际上是“先取锁再访问”的保守策略。在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载;还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数。 83 | 84 | 适用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。 85 | 86 | ### 乐观锁 87 | 88 | 乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,直到提交的时候才去锁定,所以不会产生任何锁和死锁。但一旦事务回滚的成本太高会影响效率。 89 | 90 | 参考:http://www.open-open.com/lib/view/open1452046967245.html 91 | 92 | ## **drop、delete**与**truncate**的区别 93 | 94 | - **delete和truncate**只删除表的数据不删除表的结构,**drop**则删除整个表(结构和数据) 95 | - 速度,一般来说: drop> truncate >delete 96 | - truncate和drop是DDL,会隐式提交,所以,不能回滚,不会触发触发器。 97 | - truncate会删除表中所有记录。 98 | - delete是DML,执行delete操作时,每次从表中删除一行,并且同时将该行的的删除操作记录在redo和undo表空间中以便进行回滚(rollback)和重做操作,但要注意表空间要足够大,需要手动提交(commit)操作才能生效,可以通过rollback撤消操作。 99 | 100 | 参考:http://blog.csdn.net/ws0513/article/details/49980547 101 | 102 | ## 什么是视图 103 | 104 | 视图是一种**虚拟的表**,具有和物理表相同的功能。可以对视图进行增,改,查,操作,试图通常是有**一个表或者多个表的行或列的子集**。对视图的修改不影响基本表。它使得我们获取数据更容易,相比多表查询。 105 | 106 | 参考:https://zhuanlan.zhihu.com/p/23713529 107 | 108 | ## SQL查询语句的执行数据 109 | 110 | ```sql 111 | (7) SELECT 112 | (8) DISTINCT 113 | (1) FROM 114 | (3) JOIN 115 | (2) ON 116 | (4) WHERE 117 | (5) GROUP BY 118 | (6) HAVING 119 | (9) ORDER BY 120 | (10) LIMIT 121 | ``` 122 | 123 | 1. 利用FROM子句确定查询的是哪张表 124 | 2. 利用ON确定连接条件 125 | 3. 确定连接方式,是左外连接还是右外连接 126 | 4. 利用WHERE子句筛选元组 127 | 5. 利用GROUP BY子句对筛选后的元组进行分组 128 | 6. 利用HAVING子句筛选分组 129 | 7. 执行SELECT子句,挑选需要的属性列 130 | 8. 利用DISTINCT子句过滤重复的元组 131 | 9. 利用ORDER BY子句对元组进行排序 132 | 10. 执行LIMIT子句进行分页 133 | 134 | 135 | ## 如何判定是否需要创建索引 136 | 137 | 1. 较频繁地作为查询条件的字段应该创建索引 138 | 2. 唯一性太差字段不适合单独创建索引 139 | 3. 更新非常频繁的字段不适合创建索引 140 | 4. 不会出现在where子句中的字段不需要创建索引 141 | 142 | ## 复合索引 143 | 144 | 索引可以包含一个、两个或更多个列。两个或更多个列上的索引被称作复合索引。 145 | 146 | 利用索引中的附加列,您可以缩小搜索的范围,但使用一个具有两列的索引不同于使用两个单独的索引。复合索引的结构与电话簿类似,人名由姓和名构成,电话簿首先按姓氏对进行排序,然后按名字对有相同姓氏的人进行排序。如果您知道姓,电话簿将非常有用;如果您知道姓和名,电话簿则更为有用,但如果您只知道名不姓,电话簿将没有用处。 147 | 148 | **查询优化器在在where查询中的作用:** 149 | 150 | 1. 如果一个多列索引存在于 列 Col1 和 Col2 上, 151 | 152 | 则以下语句:Select * from table where col1=val1 AND col2=val2 153 | 查询优化器会试图通过决定哪个索引将找到更少的行。之后用得到的索引去取值。 154 | 155 | 2. 如果存在一个多列索引,任何最左面的索引前缀能被优化器使用。所以联合索引的顺序不同,影响索引的选择,尽量将值少的放在前面。 156 | 157 | 如:一个多列索引为 (col1 ,col2, col3) 158 | ​ 那么在索引在列 (col1) 、(col1 col2) 、(col1 col2 col3) 的搜索会有作用。 159 | 160 | ```sql 161 | SELECT * FROM tb WHERE col1 = val1 162 | SELECT * FROM tb WHERE col1 = val1 and col2 = val2 163 | SELECT * FROM tb WHERE col1 = val1 and col2 = val2 AND col3 = val3 164 | ``` 165 | 166 | 3. 如果列不构成索引的**最左面前缀**,则建立的索引将不起作用。 167 | 168 | ```sql 169 | SELECT * FROM tb WHERE col3 = val3 170 | SELECT * FROM tb WHERE col2 = val2 171 | SELECT * FROM tb WHERE col2 = val2 and col3=val3 172 | ``` 173 | 174 | 4. 如果一个 Like 语句的查询条件不以通配符起始则使用索引。 175 | 176 | 如:%车 或 %车% 不使用索引。 177 | ​ 车% 使用索引。 178 | 179 | **一些常见的索引限制问题:** 180 | 181 | 1. 使用不等于操作符(<>, !=)。 182 | 183 | 通过把用 or 语法替代不等号进行查询,就可以使用索引,以避免全表扫描:上面的语句改成下面这样的,就可以使用索引了。 184 | 185 | ```sql 186 | select * from dept shere staff_num < 1000 or staff_num > 1000; 187 | ``` 188 | 189 | 2. 使用 is null 或 is not null 190 | 191 | -------------------------------------------------------------------------------- /Java/JVM/深入理解JVM方法调用的内部机制.md: -------------------------------------------------------------------------------- 1 | 我们都知道,Java源代码需要编译成字节码文件,由JVM解释执行,而方法调用可以说是很常见的操作。Java不同于C++,Java中的实例方法默认是虚方法,因此父类引用调用被子类覆盖的方法时能体现多态性。下面我们来看看JVM是如何完成方法调用操作并实现动态绑定的。 2 | 3 | ### 栈帧结构 4 | 5 | 为了能高效地管理程序方法调用,有条不紊地进行嵌套的方法调用和方法返回,JVM维护了一个栈结构,称为虚拟机方法栈(这里没考虑Native方法)。栈里面存放的一个个实体称为栈帧,每一个栈帧都包括了局部变量表,操作数栈,动态连接,方法返回地址和一些额外的附加信息。在编译时,栈帧中需要多大的局部变量表,多深的操作数栈都已经完全确定了,并且写入到方法表的Code属性之中。 6 | 7 | #### 局部变量表 8 | 9 | 局部变量表用于存放方法参数和方法内部定义的局部变量。局部变量表的容量以Slot为最小单位,一个Slot可以存放一个32位以内的数据类型,long和double需要两个Slot存放。 10 | 11 | 如果执行的方法是非static方法,那局部变量表中第0位索引的Slot默认是用于传递方法所属对象实例的引用(this)。 12 | 13 | 为了节省栈帧空间,局部变量表中的Slot是可以重用的。如果一个局部变量定义了但没有赋初值是不能使用的。 14 | 15 | #### 操作数栈 16 | 17 | JVM解析执行字节码是基于栈结构的。比如做算术运算时是通过操作数栈来进行的,在调用其他方法时是通过操作数栈来进行参数的传递。 18 | 19 | #### 方法调用大致过程 20 | 21 | 1. 除非被调用的方法是类方法,每一次方法调用指令之前,JVM先会把方法被调用的对象引用压入操作数栈中,除了对象的引用之外,JVM还会把方法的参数依次压入操作数栈。 22 | 2. 在执行方法调用指令时,JVM会将函数参数和对象引用依次从操作数栈弹出,并新建一个栈帧,把对象引用和函数参数分别放入新栈帧的局部变量表slot0,1,2…。 23 | 3. JVM把新栈帧push入虚拟机方法栈,并把PC指向函数的第一条待执行的指令。 24 | 25 | 到此,有人可能会问,JVM是如何得到被调用方法的地址呢?两种方式,一种是编译期的静态绑定,另一种是运行期的动态绑定。不同类型的方法用不同的绑定方式。 26 | 27 | ### 方法调用的字节码指令 28 | 29 | JVM里面提供了4条方法调用字节码指令。分别如下: 30 | 31 | - **invokestatic**:调用静态方法 32 | - **invokespecial**:调用实例构造器``方法、私有方法和父类方法(super(),super.method()) 33 | - **invokevirtual**:调用所有的虚方法(静态方法、私有方法、实例构造器、父类方法、final方法都是非虚方法) 34 | - **invokeinterface**:调用接口方法,会在运行时期再确定一个实现此接口的对象 35 | 36 | invokestatic和invokespecial指令调用的方法都可以在解析阶段中确定唯一的调用版本,符合这个条件的有静态方法、私有方法、实例构造器、父类方法4类,它们在类加载阶段就会把符号引用解析为该方法的直接引用。直接引用就是一个指针或偏移量,可以让JVM快速定位到具体要调用的方法。 37 | 38 | invokevirtual和invokeinterface指令调用的方法是在运行时确定具体的方法地址,接口方法和实例对象公有方法可以用这两个指令来调用。 39 | 40 | 下面我们通过一个代码示例来展现这几种方法调用: 41 | 42 | ```Java 43 | public class Test { 44 | private void run() { 45 | List list = new ArrayList<>(); // invokespecial 构造器调用 46 | list.add("a"); // invokeinterface 接口调用 47 | ArrayList arrayList = new ArrayList<>(); // invokespecial 构造器调用 48 | arrayList.add("b"); // invokevirtual 虚函数调用 49 | } 50 | public static void main(String[] args) { 51 | Test test = new Test(); // invokespecial 构造器调用 52 | test.run(); // invokespecial 私有函数调用 53 | } 54 | } 55 | ``` 56 | 57 | 反编译字节码: 58 | 59 | ``` 60 | public class Test { 61 | public Test(); 62 | Code: 63 | 0: aload_0 64 | 1: invokespecial #1 // Method java/lang/Object."":()V 65 | 4: return 66 | 67 | private void run(); 68 | Code: 69 | 0: new #2 // class java/util/ArrayList 70 | 3: dup 71 | 4: invokespecial #3 // Method java/util/ArrayList."":()V 72 | 7: astore_1 73 | 8: aload_1 74 | 9: ldc #4 // String a 75 | 11: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z 76 | 16: pop 77 | 17: new #2 // class java/util/ArrayList 78 | 20: dup 79 | 21: invokespecial #3 // Method java/util/ArrayList."":()V 80 | 24: astore_2 81 | 25: aload_2 82 | 26: ldc #6 // String b 83 | 28: invokevirtual #7 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z 84 | 31: pop 85 | 32: return 86 | 87 | public static void main(java.lang.String[]); 88 | Code: 89 | 0: new #8 // class Test 90 | 3: dup 91 | 4: invokespecial #9 // Method "":()V 92 | 7: astore_1 93 | 8: aload_1 94 | 9: invokespecial #10 // Method run:()V 95 | 12: return 96 | } 97 | ``` 98 | 99 | 从上面的字节码可以看出,每一条方法调用指令后面都带一个Index值,JVM可以通过这个索引值从常量池中获取到方法的符号引用。 100 | 101 | 每个class文件都有一个常量池,主要是关于类、方法、接口等中的常量,也包括字符串常量和符号引用。方法的符号引用是唯一标识一个方法的信息结构体,包含类名,方法名和方法描述符,方法描述符又包含返回值、函数名和参数列表。这些字符值都存放到class文件的常量池中,通过整型的Index来标识和索引。 102 | 103 | ### 动态分派 104 | 105 | 当JVM遇到invokevirtual或invokeinterface时,需要运行时根据方法的符号引用查找到方法地址。具体过程如下: 106 | 107 | 108 | 1. 在方法调用指令之前,需要将对象的引用压入操作数栈 109 | 2. 在执行方法调用时,找到操作数栈顶的第一个元素所指向的对象实际类型,记作C 110 | 3. 在类型C中找到与常量池中的描述符和方法名称都相符的方法,并校验访问权限。如果找到该方法并通过校验,则返回这个方法的引用; 111 | 4. 否则,按照继承关系往上查找方法并校验访问权限; 112 | 5. 如果始终没找到方法,则抛出java.lang.AbstractMethodError异常; 113 | 114 | 可以看到,JVM是通过继承关系从子类往上查找的对应的方法的,为了提高动态分派时方法查找的效率,JVM为每个类都维护一个虚函数表。 115 | 116 | #### 虚函数表 117 | 118 | JVM实现动态绑定的原理类似于C++的虚函数表机制,但C++的虚函数表是实现多态中必不可少的数据结构,但JVM里引入虚函数表的目的是加快虚方法的索引。 119 | 120 | JVM 会在链接类的过程中,给类分配相应的方法表内存空间。每个类对应一个方法表。这些都是存在于方法区中的。这里与 C++略有不同,C++中每个对象的第一个指针就是指向了相应的虚函数表。而 Java 中每个对象的对象头有一个类型指针,可以索引到对应的类,在对应的类数据中对应一个方法表。也就是C++的方法表是对象级别的,而Java的方法表是类级别的。 121 | 122 | 一个类的方法表包含类的所有方法入口地址,从父类继承的方法放在前面,接下来是接口方法和自定义的方法。如果某个方法在子类中没有被重写,那子类的虚方法表里面的地址入口和父类相同的方法的入口地址一致。如果子类重写了这个方法,子类方法表中的地址将会替换为指向子类实现版本的入口地址。 123 | 124 | 比如对于如下的Foo类: 125 | 126 | ```Java 127 | class Foo { 128 | @Override 129 | public String toString() { 130 | return "Foo"; 131 | } 132 | void run(){} 133 | } 134 | ``` 135 | 136 | 它的虚函数表如下: 137 | 138 | ![虚函数表](虚函数表.png) 139 | 140 | 141 | 142 | #### invokevirtual和invokeinterface的区别 143 | 144 | 从上面我们可以发现,虚函数表上的虚方法是按照从父类到子类的顺序排序的,因此对于使用invokevirtual调用的虚函数,JVM完全可以在编译期就确定了虚函数在方法表上的offset,或者在首次调用之后就把这个offset缓存起来,这样就可以快速地从方法表中定位所要调用的方法地址。 145 | 146 | 然而对于接口类型引用,由于一个接口可以被不同的Class来实现,所以接口方法在不同类的方法表的offset当然就(很可能)不一样了。因此,每次接口方法的调用,JVM都会搜寻一遍虚函数表,效率会比invokevirtual要低。 147 | 148 | 149 | 150 | ### 参考链接 151 | 152 | - [How the Java virtual machine handles method invocation and return](http://www.javaworld.com/article/2076949/learn-java/how-the-java-virtual-machine-handles-method-invocation-and-return.html?page=2) 153 | - [图解JVM执行引擎之方法调用](http://www.cnblogs.com/chenyangyao/p/5305352.html) 154 | - [Java:方法的虚分派(virtual dispatch)和方法表(method table)](http://blog.csdn.net/biaobiaoqi/article/details/6894869) 155 | - [Java调用重载方法(invokevirtual)和接口方法(invokeinterface)的解析](http://blog.csdn.net/pun_c/article/details/43118697) -------------------------------------------------------------------------------- /Android/深入理解Android的窗口系统.md: -------------------------------------------------------------------------------- 1 | ### 什么是Window 2 | 3 | Window是View的载体,持有一个Surface实例来绘制各种UI元素,并可以响应用户的输入事件。 4 | 5 | Window的类型: 6 | 7 | - 应用Window,对应于一个Activity, 8 | - 子WIndow,需要附属在特定的Window中,如一些常见的Dialog。 9 | - 系统Window,需要声明权限才能创建,如Toast和ANR警告对话框 10 | 11 | `` 12 | `layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;` 13 | ### WindowManagerService 14 | 15 | #### 重要成员 16 | 17 | - mInputManger:`InputManagerService`。用于管理每个窗口的输入事件通道以及向通道上派发事件。 18 | - mChoreographer:`Choreographer`。功能类似于Handler,但处理消息的时机是由VSYNC来触发的。WMS使用Choreographer来驱动窗口动画,屏幕旋转动画。 19 | - mPolicy:`WindowPolicyManager`。WMS的首席顾问。 20 | - mTokenMap:`HashMap`。保存所有WindowToken,用于窗口管理。 21 | 22 | > WindowToken的意义: 23 | > 24 | > 1. 在WMS对窗口管理的过程中,用WindowToken指代一个应用组件,WindowToken将属于同一个应用组件的Window组织在一起。 25 | > 2. WindowToken具有令牌的作用。WindowToken由应用组件或客户端负责向WMS声明并持有。应用组件在需要添加新的窗口时,必须提供WindowToken以表明自己的身份,但添加系统窗口时不需要提供WindowToken,但需要申请权限。 26 | > 3. 对于应用组件或客户端来说,Token仅仅是一个Binder对象而已。 27 | > 4. 在Activity启动过程中,AMS将ActivityRecord.appToken向WMS声明为Activity的WindowToken,并且将其传给即将启动的Activity。AMS通过ActivityRecord表示一个Activity,AMS可以通过ActivityRecord的appToken和WMS来操纵Activity的窗口绘制。比如,AMS将Task切换到前台或后台,除了要调整ActivityRecord在ActivityStack中的顺序,还要调用WMS的相关接口移动AppWindowToken在mAppTokens中的顺序,以保证两者的顺序一致。 28 | 29 | - mWindowMap:`HashMap`。保存了所有窗口的状态信息,key是IWindow的Bp端。而IWindow的Bn端即IWindow.Stub提供了很多与窗口管理相关通知的回调,例如尺寸变化,焦点变化等。 30 | - mSession:`List`。保存了所有当前想向WMS寻求窗口管理服务的客户端,Session是进程唯一的。 31 | 32 | #### 窗口创建 33 | 34 | - 获取IWindowSession和WMS实例。客户端通过IWindowSession向WMS发送请求。 35 | - 创建并初始化WindowManager.LayoutParams。其中最重要的是type属性,它是WMS对多个窗口进行ZOrder排序的依据。 36 | - 向WMS添加一个WindowToken,后续添加的应用窗口都需要带上这个Token。 37 | - 向WMS添加窗口。调用IWindowSession.add方法。向WMS添加一个窗口之后,其仅仅是将它在WMS中注册而已。 38 | - 向WMS申请对窗口进行重新布局,调用IWindowSession.relayout方法(对应于WMS的relayoutWindow方法),根据客户端提供的布局参数为窗口创建Surface,并将其放置在屏幕的指定位置。 39 | - 在WMS的relayoutWindow中,修改指定窗口的布局参数,然后调用performLayoutAndPlaceSurfacesLocked遍历所有窗口并对它们进行重新布局。 40 | - 客户端有了Surface之后,就可以进行窗口绘制了。 41 | 42 | #### 输入系统 43 | 44 | ![输入系统的总体流程与参与者](输入系统的总体流程与参与者.png) 45 | 46 | - InputDispatcher保管了来自WMS的所有窗口的信息,其收到来自InputReader的输入事件后,会在其保管的窗口中寻找合适的窗口,并将事件派发给此窗口。 47 | - WMS找到派发事件的目标Window所对应的IWindow对象,调用这个IWindow对象的dispatchKey。IWindow对象的Bn端位于ViewRoot中,ViewRoot再根据内部View的位置信息找到真正处理这个事件的View。 48 | 49 | #### WMS的作用和地位 50 | 51 | ![Android显示系统的三个层次](Android显示系统的三个层次.png) 52 | 53 | - UI框架层为在Surface上绘制的UI元素以及响应输入事件 54 | - WMS主要工作是管理Surface的分配,Window的层级顺序等 55 | - SurfaceFlinger负责将多个Surface混合并渲染输出到屏幕上 56 | 57 | ### WindowManager 58 | 59 | WIndowManager是一个继承自ViewManger的接口。ViewManager定一个了三个函数,分别用于添加/删除一个控件,以及更新控件的布局。 60 | ``` java 61 | public interface ViewManager 62 | { 63 | public void addView(View view, ViewGroup.LayoutParams params); 64 | public void updateViewLayout(View view, ViewGroup.LayoutParams params); 65 | public void removeView(View view); 66 | } 67 | ``` 68 | 69 | 鉴于窗口布局和控件布局的一致性,WindowManger和ViewGroup都继承并实现了ViewManger接口,可以把WindowManger设想成一个ViewGroup,其区域为整块屏幕,其中的各个窗口就是一个一个的View。WindowManger通过WMS将这些View按照其布局参数显示到屏幕的特定位置。 70 | 71 | #### WindowManger的实现类 72 | 73 | WindowManger的实现者是WindowMangerImpl,WindowMangerImpl除了保存窗口所属的屏幕以及父窗口,有关窗口管理的接口方法都委托给WindowMangerGlobal的实例来完成。 74 | 75 | WindowMangerGlobal在一个进程中只有一个实例,它有三个数组统一挂历整个进程中所有窗口的信息,这些信息包括View,LayoutParam和ViewRoot三个元素。WindowMangerGlobal将窗口的创建,销毁和布局更新等操作交给相应的ViewRootImpl来完成。 76 | 77 | 调用ViewRootImpl.setView方法,将View交给ViewRootImpl进行托管。这个动作将使得ViewRootImpl向WMS添加窗口,获取Surface以及重绘等一系列操作。这一步是控件能够作为一个窗口显示在屏幕上的根本原因。 78 | 79 | ![WindowMangerGlobal 的窗口管理](WindowMangerGlobal 的窗口管理.png) 80 | 81 | ### ViewRoot 82 | 83 | ViewRootImpl实现了ViewParent接口,是整个控件树的根部。它负责与WMS直接通信以创建窗口和更新窗口,负责管理Surface,负责触发View的测量,布局与绘制,同时也是输入事件的中转站,它是WindowMangerGlobal的实际执行者。 84 | 85 | ViewRootImpl继承了Handler类,重写了handleMessage方法。Android的UI操作不是线程安全的,而且很多操作也是建立在单线程的假设之上。采用单线程模型的目的是降低系统的复杂度,并降低锁的开销。 86 | 87 | #### 重要成员 88 | 89 | - mWindowSession:`IWindowSession`。它是ViewRootImpl和WMS的通信代理 90 | - mThread:`Thread`。UI线程 91 | - mDirty:`Rect`。窗口中的无效区域(需要重绘的区域) 92 | - mWindow:`W`。W是IWIndow.Stub的子类。在WMS中作为新窗口的ID,并接收来自WMS的回调。 93 | - mSurface。在WMS通过relayoutWindow为其分配一块Surface之前尚不能使用。 94 | 95 | #### setView 96 | 97 | ```java 98 | public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { 99 | synchronized (this) { 100 | if (mView == null) { 101 | mView = view; 102 | int res; /* = WindowManagerImpl.ADD_OKAY; */ 103 | requestLayout(); 104 | try { 105 | res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, 106 | getHostVisibility(), mDisplay.getDisplayId(), 107 | mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, 108 | mAttachInfo.mOutsets, mInputChannel); 109 | } catch (RemoteException e) {...} 110 | } 111 | } 112 | ``` 113 | 114 | ViewRoot的setView做了三件事: 115 | 116 | - 保存传入的View 117 | - 调用requestLayout,内部其实就是往Handler发送一个DO_TRAVERSAL消息,这个消息最终会在ViewRoot的handleMessage方法中执行 118 | - 调用IWindowSession的add方法,mWindow就是IWindow.Stub类型,向WMS添加窗口。 119 | 120 | #### perforTraversals 121 | 122 | ViewRootImpl在其创建过程中通过requestLayout向主线程发送一条触发遍历操作的消息,ViewRootImpl的performTraversals会被调用。 123 | 124 | - 预测量阶段。这个阶段,View及其子类的onMeasure方法会沿着控件树依次得到回调,计算出控件树显示其内容所需的尺寸,即期望的窗口尺寸。 125 | - 布局窗口阶段。根据预测量的结果,通过IWindowSession.relayout方法向WMS请求调整窗口的尺寸等属性,并将布局结果返回给ViewRootImpl。 126 | - 最终测量阶段。以窗口的实际尺寸对控件进行最终测量。这个阶段,View及其子类的onMeasure方法会沿着控件树依次得到回调。 127 | - 布局控件树阶段。完成最终测量之后便可以对控件树进行布局。这个阶段中,View的onLayout方法会被调用。 128 | - 绘制阶段。确定了控件的位置与尺寸之后,便可以对控件树进行绘制。这个阶段中,View及其子类的onDraw方法将会被回调。 129 | 130 | #### 与 WMS的关系 131 | 132 | ![ViewRoot 和 WMS 的关系](ViewRoot 和 WMS 的关系.png) 133 | 134 | ### Surface 135 | 136 | Surface是一块画布,App可以通过Canvas或OpenGL在其上作画。SurfaceFlinger将多块Surface的内容按照特定的顺序Z-Order进行混合并输入到FrameBuffer(存储图形/图像帧数据的缓冲)。窗口绘制过程: 137 | 138 | 1. 通过Surface.lock()函数获取可以在其上作画的Canvas实例 139 | 2. 使用Canvas实例进行作画 140 | 3. 通过Surface.unlockCanvasAndPost()函数提交绘制结果 141 | 142 | -------------------------------------------------------------------------------- /Android/Android动画.md: -------------------------------------------------------------------------------- 1 | ## View动画 2 | 3 | ### 用法 4 | 5 | 官方文档:https://developer.android.google.cn/guide/topics/resources/animation-resource.html 6 | 7 | ```xml 8 | 9 | 12 | 15 | 22 | 27 | 32 | 33 | ... 34 | 35 | 36 | ``` 37 | 38 | ```java 39 | Animation animation = AnimationUtils.loadAnimation(ctx, R.anim.abc_fade_in); 40 | animation.setAnimationListener(new Animation.AnimationListener() { 41 | @Override 42 | public void onAnimationStart(Animation animation) { 43 | 44 | } 45 | 46 | @Override 47 | public void onAnimationEnd(Animation animation) { 48 | 49 | } 50 | 51 | @Override 52 | public void onAnimationRepeat(Animation animation) { 53 | 54 | } 55 | }); 56 | view.startAnimation(animation); 57 | ``` 58 | 59 | 60 | 61 | ### 自定义View动画: 62 | 63 | 继承Animation这个抽象类,实现它的initialize和applyTransaction方法,在initialize方法中进行一些初始化工作,在applyTransaction方法对Transformation对象的属性进行变换。 64 | 65 | 66 | 67 | ### 帧动画 68 | 69 | 使用AnimationDrawable来使用帧动画。 70 | 71 | ```xml 72 | 73 | 75 | 78 | 79 | ``` 80 | 81 | ```java 82 | ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image); 83 | rocketImage.setBackgroundResource(R.drawable.rocket_thrust); 84 | rocketAnimation = (AnimationDrawable) rocketImage.getBackground(); 85 | rocketAnimation.start(); 86 | ``` 87 | 88 | 89 | 90 | ## 使用场景 91 | 92 | ### LayoutAnimation 93 | 94 | LayoutAnimation作用于ViewGroup,为ViewGroup指定一个动画,这样当它的子元素出场时都会具有这种动画效果。 95 | 96 | ```xml 97 | 98 | 103 | 104 | ``` 105 | 106 | ```xml 107 | 108 | ``` 109 | 110 | 111 | 112 | ### Activity的切换动画 113 | 114 | 启动Activity 115 | 116 | ```java 117 | Intent intent = new Intent(MainActivity.this, AidlClientActivity.class); 118 | startActivity(intent); 119 | overridePendingTransition(R.anim.abc_fade_in, R.anim.abc_fade_out); 120 | ``` 121 | 122 | Activity退出 123 | 124 | ```java 125 | finish(); 126 | overridePendingTransition(R.anim.abc_fade_in, R.anim.abc_fade_out); 127 | ``` 128 | 129 | ## 属性动画 130 | 131 | 官方文档:https://developer.android.google.cn/guide/topics/graphics/prop-animation.html 132 | 133 | 步骤流程: 134 | 135 | ![属性动画](属性动画.jpg) 136 | 137 | ### ObjectAnimator 138 | 139 | ```java 140 | ObjectAnimator.ofFloat(view,"translationX",300).setDuration(1000).start(); 141 | PropertyValuesHolder pvh1 = PropertyValuesHolder.ofFloat("translation", 300f); 142 | PropertyValuesHolder pvh2 = PropertyValuesHolder.ofFloat("scaleX",1f,0,1f); 143 | PropertyValuesHolder pvh3 = PropertyValuesHolder.ofFloat("scaleY", 1f,0,1f); 144 | ObjectAnimator.ofPropertyValuesHolder(view,pvh1,pvh2,pvh3).setDuration(1000).start(); 145 | ``` 146 | 147 | 常用的属性: 148 | 149 | - translationX和translationY:控制着View对象从布局容器左上角坐标偏移的增量 150 | - rotation、rotationX,rotationY:控制View对象围绕支点进行2D和3D旋转 151 | - scaleX、scaleY:控制着View对象围绕它支点进行2D缩放 152 | - pivotX、pivotY:控制着View对象支点的位置,围绕这个支点进行旋转和缩放变换,默认情况下该支点的位置就是VIew对象的中心点 153 | - x、y:描述VIew对象在它的容器中的最终位置,等于最初的左上角坐标和translationX、translationY值得累计和。 154 | - alpha:设置View对象的alpha透明度,默认值是1,,不透明,0代表完全透明,不可见 155 | 156 | ### **ValueAnimator** 157 | 158 | ObjectAnimator是ValueAnimator的子类,本身并没有提供动画效果,而更像是一个数值产生器。 159 | 160 | ```java 161 | ValueAnimator animator = ValueAnimator.ofFloat(0,100); 162 | animator.setTarget(view); 163 | animator.setDuration(1000).start(); 164 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 165 | @Override 166 | public void onAnimationUpdate(ValueAnimator animation) { 167 | float value = (float) animation.getAnimatedValue(); 168 | //TODO use the value to set the property. 169 | } 170 | }); 171 | ``` 172 | 173 | 174 | 175 | ### 插值器和估值器 176 | 177 | 时间插值器TimeInterpolator作用是根据时间流逝的百分比来计算出当前属性值改变的百分比。 178 | 179 | 估值器TypeEvaluator作用是根据当前属性改变的百分比来计算改变后的属性值。 180 | 181 | 182 | 183 | ### 对任意属性做自定义属性动画 184 | 185 | 属性动画原理:属性动画要求动画作用对象提供该属性的get和set方法,内部通过Java反射调用getter获取view中属性初始值,然后多次调用setter来实现动态改变view的属性。 186 | 187 | - 使用包装类包装原始对象,间接为其提供getter和setter。 188 | 189 | ```java 190 | public class WrapperView { 191 | private View mTarget; 192 | public WrapperView(View target){ 193 | mTarget=target; 194 | } 195 | public int getWidth(){ 196 | return mTarget.getLayoutParams().width; 197 | } 198 | public void setWidth(int width){ 199 | mTarget.getLayoutParams().width=width; 200 | mTarget.requestLayout(); 201 | } 202 | } 203 | ``` 204 | 205 | - 使用ValueAnimator 206 | 207 | 208 | 209 | ## Notice 210 | 211 | 1. 在Activity退出或停止时,需要停止正在播放的属性动画,防止Activity的引用无法释放而导致内存泄漏 212 | 2. 开启硬件加速会提高动画的流畅性。 -------------------------------------------------------------------------------- /Java/JVM/Java内存模型与并发.md: -------------------------------------------------------------------------------- 1 | ## Java内存模型 2 | 3 | ### 为什么会有JMM 4 | 5 | 1. **缓存一致性**。Cache能很好解决CPU与内存的速度矛盾,但在多CPU系统中,每个处理器都有自己的Cache,而它们又共享同一个主存。当多个CPU的运算任务都涉及同一块主存区域时,将可能导致各自的缓存数据不一致。为了解决缓存一致性的问题,需要各个CPU访问Cache时都遵循一些协议。而内存模型就是在特定的操作协议下,对特定的内存或Cache进行读写访问的抽象过程。 6 | 2. **不同硬件平台的兼容性**。JVM规范试图定义一种JMM来屏蔽各种硬件和OS的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果。 7 | 8 | ### 主内存与工作内存 9 | 10 | JMM的主要目标是定义程序中各个变量的存取规则。JMM规定了所有变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存(类比Cache)中保存了被该线程使用到的变量的主内存副本,线程对变量的所有操作都必须在工作内存中进行。 11 | 12 | JMM定义了8中原子操作完成工作内存和主内存之间的交互:lock,unlock,read,load,use,assign,store,write。 13 | 14 | ![工作内存和主内存](工作内存和主内存.png) 15 | 16 | ### volatile关键字 17 | 18 | volatile关键字有两种语义: 19 | 20 | 1. 变量在线程之间可见 21 | 2. 禁止指令重排 22 | 23 | 实现原理:如果一个变量是volatile修饰的,JMM会在写入这个字段之后插进一个Write-Barrier指令,并在读这个字段之前插入一个Read-Barrier指令。多执行一条以lock为前缀的空操作指令,将对Cache的修改写入主存,让前面对volatile变量的修改对其他CPU立即可见。同时lock前缀指令会形成一道内存屏障,禁止指令重排。 24 | 25 | volatile变量的读操作性能消耗与普通变量几乎没什么差别,但写操作则可能会慢一些,因为它需要在本地代码插入许多内存屏障指令来保证CPU不发生乱序执行。 26 | 27 | ### JMM的三大特征 28 | 29 | **原子性**:由JMM直接保证的原子性变量操作包括read,load,assign,use,store和write。如果应用场景选的一个更大范围的原子性保证,需要用到lock和unlock操作,对应的更高层次的字节码指令monitorenter和monitorexit来隐式地使用这两个操作,这两个字节码指令反映到Java代码中就是同步快--synchronized关键字。 30 | 31 | **可见性**:指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。volatile,synchronized和final都能实现可见性。 32 | 33 | **有序性**:volatile和synchronized两个关键字来保证线程之间操作的有序性,volatile关键字包含了禁止指令重排序的语义,而synchronized规定持有同一个锁的两个同步块只能串行地进入。 34 | 35 | ### happens-before原则 36 | 37 | happens-before原则用于判断资源是否存在竞争,线程是否安全的重要依据。如果操作A happens-before B,那么在发生B操作之前,A操作产生的影响能被操作B观察到。 38 | 39 | 参考:http://www.jianshu.com/p/d3fda02d4cae 40 | 41 | ## Java线程 42 | 43 | ### 线程的实现方式 44 | 45 | 实现线程主要有3种方式:使用内核线程实现,使用用户线程实现和使用用户线程加轻量级线程混合实现。 46 | 47 | - 使用内核线程实现 48 | 49 | 内核线程就是直接由操作系统内核(kernel)支持的线程,这种线程由内核来完成线程切换,内核通过操作调度器对线程进行调度,将线程的任务映射到各个CPU上。每个内核线程相当于内核的一个分身。 50 | 51 | 程序一般不会直接使用内核线程,而是使用内核线程的一种高级接口--轻量级进程(LWP)。 52 | 53 | 缺点:由于基于内核线程实现的,各种线程操作,如创建,析构及同步都需要进行系统调用。而用户态与内核态的切换代价相对较高。其次,一个系统支持LWP数量是有限的。 54 | 55 | ![LWP与KLT](LWP与KLT.png) 56 | 57 | - 使用用户线程实现 58 | 59 | 狭义上的用户线程指的是完全建立在用户空间的线程库上,系统内核不能感知线程存在的实现。用户线程的建立,同步,销毁和调度完全在用户态中完成,不需要内核的帮助。 60 | 61 | 缺点:线程的创建,切换和调度都是需要考虑的问题,诸如阻塞如何处理,多处理器系统如何将线程映射到其他CPU上等问题难以解决。 62 | 63 | ![用户线程](用户线程.png) 64 | 65 | - 使用用户线程+轻量级进程混合实现 66 | 67 | 用户线程还是完全建立在用户空间中,因此线程的创建,切换,析构等操作依然廉价,并且可以支持大规模的用户线程并发。而OS提供轻量级进程作为用户线程和内核线程的桥梁,可以使用内核提供的线程调度功能及处理器映射。 68 | 69 | ![UT与LWP混合](UT与LWP混合.png) 70 | 71 | ### Java线程调度 72 | 73 | 线程调度是指系统为线程分配处理器使用权的过程。主要的调度方式为协同式线程调度和抢占式线程调度。 74 | 75 | 协同式调度:线程的执行时间由线程本身来控制,线程把自己的工作执行完之后,要主动通知系统切换到另外一个线程上。优点:实现简单。缺点:线程执行时间不可控制,如果一个线程堵塞了,那么程序就会一直阻塞在那里。 76 | 77 | 抢占式调度;每一个线程将由系统来分配执行时间,线程的切换不由线程本身来决定。通过设置线程的优先级,可以让线程更容易被系统选择执行。 78 | 79 | ### 线程状态转换 80 | 81 | ![线程状态转换关系](线程状态转换关系.png) 82 | 83 | ## 线程安全与锁 84 | 85 | 线程安全:当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的。 86 | 87 | ### 线程安全的实现方法 88 | 89 | #### 互斥同步 90 | 91 | synchronized关键字经过编译之后,会在同步块的前后分别形成monitorenter和monitorexit这两个字节码指令,这两个字节码都需要一个reference类型的参数来指明要锁定和解锁的对象,如果Java程序中的synchronized明确指定了对象参数,那这就是这个对象的reference;如果没有明确指定,那就根据synchronized修饰的实例方法还是类方法,取对应的对象实例或Class对象所谓锁对象。 92 | 93 | 如果需要阻塞或唤醒一个线程都需要操作系统帮忙完成,这就需要从用户态转换到核心态中,因此状态转换需要耗费很多的处理器时间。 94 | 95 | **synchronized的实现原理** 96 | 97 | 每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下: 98 | 99 | 1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者 100 | 101 | 2、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1 102 | 103 | 3、如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权 104 | 105 | **synchronized与ReentrantLock的区别** 106 | 107 | 1. 一个表现为原生语法层面的互斥锁,一个表现为API层面的互斥锁(lock()和unlock()方法配合try/finally语句块来完成) 108 | 2. ReentrantLock等待可中断。即当持有锁的线程长期不释放锁时,正在等待的线程可以选择放弃等待。 109 | 3. ReentrantLock可以实现公平锁。即按照申请锁的时间顺序来依次获得锁。 110 | 4. ReentrantLock可以绑定多个条件。即一个ReentrantLock对象可以同时绑定多个Condition对象。 111 | 112 | #### 非阻塞同步 113 | 114 | 互斥同步主要问题是进行线程阻塞和唤醒所带来的性能问题,互斥同步属于一种悲观的并发策略。 115 | 116 | 非阻塞同步是基于冲突监测的乐观并发策略,不需要将线程挂起,而是通过CAS来实现线程的同步。乐观并发策略需要操作和冲突检测这两个步骤具备原子性,如CAS指令。 117 | 118 | CAS指令需要3个操作数,分别是内存地址V,旧的预期值A和新值B。CAS指令执行时,当且仅当V的值符合旧的预期值A时,处理器用新值更新V的值,否则不执行更新。 119 | 120 | 缺点:CAS会存在ABA问题,但一般不会影响程序并发的正确性,如果需要解决ABA问题,可以改用传统的互斥同步。 121 | 122 | ### synchronized底层原理 123 | 124 | #### Mark Word与Monitor 125 | 126 | 对象在堆中内存包含对象头,实例数据和对齐填充。对象头包含Mark Word和类型指针,Mark Word占一个字的大小,包含指向Monitor的指针等锁信息。每个对象都存在着一个monitor与之关联。monitor包含一个owner变量,用于记录持有锁的线程;一个等待队列和一个同步队列。 127 | 128 | #### 同步代码块 129 | 130 | 使用的是monitorenter 和 monitorexit 指令,其中monitorenter指令指向同步代码块的开始位置,monitorexit指令则指明同步代码块的结束位置,当执行monitorenter指令时,当前线程将试图获取 objectref(即对象锁) 所对应的 monitor 的持有权,当 objectref 的 monitor 的进入计数器为 0,那线程可以成功取得 monitor,并将计数器值设置为 1,取锁成功。如果当前线程已经拥有 objectref 的 monitor 的持有权,那它可以重入这个 monitor (关于重入性稍后会分析),重入时计数器的值也会加 1。倘若其他线程已经拥有 objectref 的 monitor 的所有权,那当前线程将被阻塞,直到正在执行线程执行完毕,即monitorexit指令被执行,执行线程将释放 monitor(锁)并设置计数器值为0 ,其他线程将有机会持有 monitor 。 131 | 132 | #### 同步方法 133 | 134 | 方法级的同步是隐式,即无需通过字节码指令来控制的,它实现在方法调用和返回操作之中。JVM可以从方法常量池中的方法表结构(method_info Structure) 中的 ACC_SYNCHRONIZED 访问标志区分一个方法是否同步方法。当方法调用时,调用指令将会 检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先持有monitor(虚拟机规范中用的是管程一词), 然后再执行方法,最后再方法完成(无论是正常完成还是非正常完成)时释放monitor。在方法执行期间,执行线程持有了monitor,其他任何线程都无法再获得同一个monitor。 135 | 136 | 参考:[深入理解Java并发之synchronized实现原理](http://blog.csdn.net/javazejian/article/details/72828483) 137 | 138 | ### 锁优化 139 | 140 | #### 自旋锁和自适应自旋 141 | 142 | 互斥同步对性能最大的影响时阻塞的实现。一般情况下,共享数据的锁定状态只会持续很短一段时间,为了这段时间去挂起和恢复线程并不值得。在多核处理器中,我们可以让后面请求锁的线程稍等一会,暂时不放弃CPU的执行时间,通过自旋看看持有锁的线程是否很快就会释放锁。 143 | 144 | 自适应的自旋锁意味着自旋的时间不再固定,而是由前一次在同一个锁上的自旋时间及锁的拥有者状态来决定。 145 | 146 | #### 锁消除 147 | 148 | 锁消除是指虚拟机即时编译器在运行时,对一些代码要求同步,但被检测 到不可能存在共享数据竞争的锁进行消除。 149 | 150 | #### 锁粗化 151 | 152 | 如果虚拟机检测到有一串零碎的操作都对同一个对象加锁,就会把加锁同步的范围扩展到整个操作序列的外部。因为频繁地进行不必要的互斥同步操作会导致性能损耗。 153 | 154 | #### 轻量级锁与偏向锁 155 | 156 | Synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的。但是监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的。而操作系统实现线程之间的切换这就需要从用户态转换到核心态,这个成本非常高,状态之间的转换需要相对比较长的时间。为了减少获得锁和释放锁所带来的性能消耗,提高性能,引入了“轻量级锁”和“偏向锁”。 157 | 158 | 偏向锁的核心思想是,如果一个线程获得了锁,那么锁就进入偏向模式,此时Mark Word 的结构也变为偏向锁结构,当这个线程再次请求锁时,无需再做任何同步操作,即获取锁的过程,这样就省去了大量有关锁申请的操作,从而也就提供程序的性能。 159 | 160 | 轻量级锁是在没有多线程竞争的前提下,减小传统的重量级锁使用操作系统互斥量产生的性能消耗。 161 | 162 | 轻量级锁是在无竞争的情况下使用CAS操作去消除同步使用的互斥量,那偏向锁就是在无竞争的情况下把整个同步都消除掉,连CAS操作都不做了。 163 | 164 | -------------------------------------------------------------------------------- /网络基础/Http学习笔记.md: -------------------------------------------------------------------------------- 1 | ## 协议之间的关系 2 | 3 | ![协议之间的关系](协议之间的关系.png) 4 | 5 | ## URI的格式 6 | 7 | ![URI格式](URI格式.png) 8 | 9 | ## Http/1.1 协议 10 | 11 | ![请求报文和响应报文的结构](请求报文和响应报文的结构.png) 12 | 13 | ### 请求报文格式 14 | 15 | 请求报文由请求方法,请求URI,协议版本,可选的请求首部字段和内容实体构成。 16 | 17 | ![请求报文格式](请求报文格式.png) 18 | 19 | ### 响应报文格式 20 | 21 | 响应报文基本由协议版本,状态码,用以解析状态码原因的短语,可选的响应首部字段以及实体主体构成。 22 | 23 | ![响应报文的构成](响应报文的构成.png) 24 | 25 | ### 请求方法 26 | 27 | 1. GET 从服务端获取资源 28 | 2. POST 提交参数给服务端 29 | 3. PUT 向传输文件文件 30 | 4. DELETE 删除服务端中的对应URI 的资源 31 | 5. HEAD 与GET方法一样,只是不返回报文主体不问。 32 | 6. OPTIONS 查询针对请求URI 指定的资源支持的方法 33 | 34 | 35 | ## 事务 36 | 37 | 一个HTTP事务由一条(从客户端发往服务器的)请求命令和一个(从服务器发往客户端的)响应结果组成。 38 | 39 | ## HTTP连接的处理 40 | 41 | 42 | ### Connection首部 43 | 44 | 在客户端和服务器之间常常会存在多个中间体。Connection首部可以承载三类不同类型的标签: 45 | 46 | - HTTP首部字段名,告诉负责转发的中间体在转发前把相应的header删除 47 | - 任意标签值 48 | - close。说明操作完成后需要关闭这条持久连接。 49 | 50 | ### 并行连接 51 | 52 | 通过多条TCP连接发起并发的HTTP请求。 53 | 54 | 缺点: 55 | 56 | - 每个事务都会打开/关闭一条新的连接,会耗费时间和带宽 57 | - 由于TCP慢启动特性的存在,每条新连接的性能都会有所降低 58 | - 可打开的并行连接数量实际上是有限的 59 | 60 | ### 持久连接 61 | 62 | ![TCP&HTTP](TCP&HTTP.png) 63 | 64 | HTTP协议采用的是"请求-应答" 模式,是无状态无连接的协议。当使用普通模式时,每一个请求/应答都需要新建一个连接,完成之后需要断开连接。 65 | 66 | 持久连接特点是,只要任意一端没有明确提出断开连接,则保持TCP连接状态。 67 | 68 | http 1.0中默认是关闭的,需要在http头加入"Connection: Keep-Alive",才能启用Keep-Alive;http 1.1中默认启用Keep-Alive,如果加入"Connection: close ",才关闭。 69 | 70 | http://a280606790.iteye.com/blog/1095085 71 | 72 | ## 发送多种数据的多部分对象集合 73 | 74 | 发送一份报文主体内可包含有多类型实体。 75 | 76 | 使用boundary字符串来划分多部分对象集合指明的各类实体。在boundary字符串指定的各个实体的起始行之前插入"--"标记。 77 | 78 | ![multipart-form-data](multipart-form-data.png) 79 | 80 | ## 范围请求 81 | 82 | 指定范围发送的请求叫做范围请求(Range Request)。 83 | 84 | 在执行范围请求时,会用首部字段Range来指定资源的byte范围。 85 | 86 | - 5001-10000字节 87 | 88 | ``` 89 | Range: bytes=5001-10000 90 | ``` 91 | 92 | - 从5001字节之后全部的 93 | 94 | ``` 95 | Range: bytes=5001- 96 | ``` 97 | 98 | - 从一开始到3000字节和5000-7000字节的多重范围 99 | 100 | ``` 101 | Range: bytes=-3000,5000-7000 102 | ``` 103 | 104 | 针对范围请求,响应会返回状态吗为206 Partial Content的响应报文。对于多重范围,响应会在添加首部Content-Type: multipart/byteranges。 105 | 106 | 如果服务端无法响应范围请求,则返回状态码 200 OK和完整的实体内容。 107 | 108 | 109 | 110 | ## Http响应的状态码 111 | 112 | ![状态码类别](状态码类别.png) 113 | 114 | ### 2XX 成功 115 | 116 | 1. 200 OK 117 | 2. 204 No Content 请求处理成功,但服务器没有资源可返回。 118 | 119 | ### 3XX 重定向 120 | 121 | 1. 300 Multiple Choice 客户端请求一个实际指向多个资源的URL时会返回这个状态码。 122 | 2. 301 Moved Permanently 资源的URI已更新,需要更新书签引用。 123 | 3. 302 Found 临时性重定向。 124 | 4. 303 See Other 告知客户端应该用另一个URL来获取资源。 125 | 5. 304 Not Modified 表示请求未满足访问的条件。304状态码返回时,不包含任何响应的主体部分。 126 | 127 | ### 4XX 客户端错误 128 | 129 | 1. 400 Bad Request 表示请求报文中存在语法错误。 130 | 2. 401 Unauthorized 表示发送的请求需要通过HTTP认证的认证信息。 131 | 3. 403 Forbidden 表明对请求资源的访问被服务器拒绝了。 132 | 4. 404 Not Found 服务器上没有请求的资源。 133 | 134 | ### 5XX 服务器错误 135 | 136 | 1. 500 Internal Server Error 表明服务器端在执行请求时发生了错误。 137 | 2. 503 Service Unavailable 表明服务器暂时处于超负载或正在停机维护。 138 | 139 | 140 | 141 | ## Web服务器 142 | 143 | - 用单台虚拟主机实现多个域名 144 | 145 | 虚拟主机可以寄存多个不同主机名和域名的web网站,但http请求的IP地址确实相同的。因此在放松HTTP请求时,必须在Host首部内完整指订主机名或域名的URI。 146 | 147 | - 代理 148 | 149 | ![代理](代理.png) 150 | 151 | - 网关 152 | 153 | ![网关](网关.png) 154 | 155 | 利用网关能提高通信的安全性,因为可以在客户端与网关之间的通信线路上加密以确保连接安全。 156 | 157 | - 隧道 158 | 159 | 隧道的目的时确保客户端能与服务器进行安全的通信,客户端与服务端之间使用SSL等加密手段进行通信。 160 | 161 | 162 | 163 | ## HTTPS首部 164 | 165 | ### 分类 166 | 167 | - 通用首部字段:请求报文和响应报文两方都会使用的首部。 168 | 169 | ![通用首部字段](通用首部字段.png) 170 | 171 | - 请求首部字段:请求报文包含的首部 172 | 173 | ![请求首部字段](请求首部字段.png) 174 | 175 | - 响应首部字段:响应报文包含的首部 176 | 177 | ![响应首部字段](响应首部字段.png) 178 | 179 | - 实体首部字段:针对报文的实体部分使用的首部 180 | 181 | 182 | ### Cache-Control 183 | 184 | 参考:https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=zh-cn 185 | 186 | http://www.uml.org.cn/site/201701042.asp 187 | 188 | 操作缓存的工作机制。多个指令之间通过","来分隔。 189 | 190 | ```java 191 | Cache-Control: private, max-age=0, no-cache 192 | ``` 193 | 194 | ![Cache-Control](缓存请求指令.png) 195 | 196 | | 指令 | 参数 | 说明 | 197 | | --------------- | ---- | -------- | 198 | | only-if-cached | 无 | 从缓存中获取资源 | 199 | | cache-extension | - | 新指令标记 | 200 | 201 | 202 | ![缓存响应指令](缓存响应指令.png) 203 | 204 | #### no-cache指令 205 | 206 | ``` 207 | Cache-Control: no-cache 208 | ``` 209 | 210 | ![no-cache](no-cache.png) 211 | 212 | #### no-store指令 213 | 214 | ``` 215 | Cache-Control: no-store 216 | ``` 217 | 218 | 规定缓存不能在本地存储请求或响应的任一部分。 219 | 220 | 221 | 222 | #### max-age指令 223 | 224 | ``` 225 | Cache-Control: max-age=604800 226 | ``` 227 | 228 | ![max-age](max-age.png) 229 | 230 | #### min-fresh指令 231 | 232 | ``` 233 | Cache-Control: min-fresh=60 234 | ``` 235 | ![min-fresh](min-fresh.png) 236 | 237 | #### max-stale指令 238 | 239 | 即使过期了也照样接收。 240 | 241 | #### only-if-cached指令 242 | 243 | 表示客户端仅在缓存服务器本地缓存目标资源的情况下才会要求其返回。 244 | 245 | 246 | 247 | ### Connection 248 | 249 | HTTP/1.1之前的HTTP版本默认连接都是非持久连接。为此,向维持持续连接则需要指定Connection首部字段的值为Keep-Alive。 250 | 251 | 252 | 253 | ### Transfer-Encoding 254 | 255 | 规定了传输报文主体时采用的编码方式。 256 | 257 | 258 | 259 | ### Accept 260 | 261 | 通知服务器,用户能够处理的媒体类型以及媒体类型的相对优先级。 262 | 263 | ``` 264 | Accept: text/html,application/xhtml+xml,application/xml 265 | ``` 266 | 267 | ### Authorization 268 | 269 | 用来告知服务器,用户代理的认证信息。 270 | 271 | 272 | 273 | ### Location 274 | 275 | 将响应接收方引导至某个与请求URI位置不同的资源。该字段会配合3XX的响应,提供重定向的URI。 276 | 277 | ### Expires 278 | 279 | 将资源失效的日期告知客户端。源服务器不希望缓存服务器对资源缓存时,最好将Expires设置为与Date相同的时间值。 280 | 281 | ### Cookie相关的首部 282 | 283 | #### Set-Cookie 284 | 285 | 响应报文的首部,当服务器准备开始管理客户端状态时,会事先告知各种信息。 286 | 287 | ![Set-Cookie字段属性](Set-Cookie字段属性.png) 288 | 289 | #### Cookie 290 | 291 | 请求报文的首部。 292 | 293 | ![Cookie&Session](Cookie&Session.png) 294 | 295 | ## Https 296 | 297 | HTTP的缺点: 298 | 299 | - 通信使用明文,内容可能会被窃取 300 | - 不验证通信方身份,可能遭遇伪装 301 | - 无法证明报文的完整性,有可能被篡改。 302 | 303 | HTTP协议中没有加密机制,但可以通过和SSL或TLS的组合使用,加密HTTP的通信内容。 304 | 305 | SSL不仅提供加密处理,还使用了一种被称为证书的手段。证书由值得信任的第三方机构颁发,用以证明服务器和客户端实际存在的。 306 | 307 | **HTTP+加密+认证+完整性保护=HTTPS** 308 | 309 | 通常,HTTP直接和TCP通信。当使用SSL时,则演变成先和SSL通信,再由SSL和TCP通信。 310 | 311 | HTTPS采用混合加密机制: 312 | 313 | 1. 使用非对称加密技术对密钥进行加密 314 | 2. 再确保密钥安全的前提下,使用对称加密的方式进行通信 315 | 316 | 如何校验服务器端的公钥是货真价实的呢?这时就需要数字证书认证机构了。 317 | 318 | ![数字证书机构](数字证书机构.png) 319 | 320 | -------------------------------------------------------------------------------- /Java/多线程/深入ThreadPoolExecutor.md: -------------------------------------------------------------------------------- 1 | ## 重要的类或成员 2 | 3 | ### ctl 4 | 5 | ```java 6 | private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); 7 | ``` 8 | 9 | ThreadPoolExecutor用这一个原子整型变量保存了两个内容: 10 | 11 | - 所有有效线程的数量 12 | - 线程池的运行状态(runState) 13 | 14 | 低29位存线程数,高3位存`runState`(线程池的运行状态),这样runState有5个值: 15 | 16 | - RUNNING:-536870912 17 | - SHUTDOWN:0 18 | - STOP:536870912 19 | - TIDYING:1073741824 20 | - TERMINATED:1610612736 21 | 22 | ### Worker 23 | 24 | ```java 25 | private final class Worker extends AbstractQueuedSynchronizer implements Runnable 26 | ``` 27 | 28 | Worker是ThreadPoolExecutor的静态内部类,主要是对Thread对象的包装,一个Worker内部有一个Thread对象。同时Worker继承自AQS来实现一个简单互斥锁,每一个任务的执行前和执行后都会分别获取和释放一次锁。(acquiring and releasing a lock surrounding each task execution)这样做是为了让线程执行任务时屏蔽中断操作。 29 | 30 | 那么为什么不用ReentrantLock呢?其实是为了避免在任务执行中修改线程池的变量和状态,不能用可重入锁。参考:[stackoverflow](https://stackoverflow.com/questions/42189195/why-threadpoolexecutorworker-extends-abstractqueuedsynchronizer) 31 | 32 | ``` 33 | We implement a simple non-reentrant mutual exclusion lock rather than use ReentrantLock because we do not want worker tasks to be able to reacquire the lock when they invoke pool control methods like setCorePoolSize. -- From java doc 34 | ``` 35 | 36 | 37 | 38 | 39 | 40 | ## execute 41 | 42 | ### execute(Runnable command) 43 | 44 | ```java 45 | public void execute(Runnable command) { 46 | int c = ctl.get(); // 获取线程池的状态 47 | if (workerCountOf(c) < corePoolSize) { // 如果当前工作线程数小于所配置的核心线程数 48 | if (addWorker(command, true)) // 新建一个工作线程作为核心线程,并把该任务交给它执行 49 | return; 50 | c = ctl.get(); 51 | } 52 | if (isRunning(c) && workQueue.offer(command)) { // 往等待队列添加任务 53 | // double-check 54 | int recheck = ctl.get(); 55 | if (! isRunning(recheck) && remove(command)) 56 | reject(command); 57 | else if (workerCountOf(recheck) == 0) 58 | addWorker(null, false); 59 | } 60 | else if (!addWorker(command, false)) // 开辟一个线程执行该任务 61 | reject(command); 62 | } 63 | ``` 64 | 65 | ### addWorker 66 | 67 | ```java 68 | private boolean addWorker(Runnable firstTask, boolean core) { 69 | ... 70 | Worker w = null; 71 | try { 72 | w = new Worker(firstTask);// 参见1. 73 | final Thread t = w.thread; 74 | if (t != null) { 75 | final ReentrantLock mainLock = this.mainLock; 76 | mainLock.lock(); 77 | try { 78 | ... 79 | workers.add(w); // workers是一个HashSet,一个工作线程的集合 80 | ... 81 | } finally { 82 | mainLock.unlock(); 83 | } 84 | if (workerAdded) { 85 | t.start(); // 参见2. 86 | workerStarted = true; 87 | } 88 | } 89 | } finally { 90 | if (! workerStarted) 91 | addWorkerFailed(w); 92 | } 93 | return workerStarted; 94 | } 95 | ``` 96 | 97 | 1. 创建Worker对象,同时通过线程工厂创建一个新的线程 98 | 99 | ```java 100 | Worker(Runnable firstTask) { 101 | setState(-1); // inhibit interrupts until runWorker 102 | this.firstTask = firstTask; 103 | // this是指Worker对象,它将作为Thread的Runnable target 104 | this.thread = getThreadFactory().newThread(this); 105 | } 106 | ``` 107 | 108 | 109 | 110 | 2. 启动Worker线程,由于Worker也是一个Runnable对象,在Worker线程启动之后,Worker的run方法会被调用 111 | 112 | ```java 113 | /** Delegates main run loop to outer runWorker */ 114 | public void run() { 115 | runWorker(this); //Worker线程开始工作啦 116 | } 117 | ``` 118 | 119 | 120 | 121 | ### runWorker(Worker w) 122 | 123 | ThreadPoolExecutor#runWorker会开启worker线程的子任务循环。 124 | 125 | ```java 126 | final void runWorker(Worker w) { 127 | Thread wt = Thread.currentThread(); 128 | Runnable task = w.firstTask; 129 | w.firstTask = null; 130 | w.unlock(); // allow interrupts 131 | try { 132 | while (task != null || (task = getTask()) != null) { // 参见1. 133 | w.lock(); // 开始执行任务前加锁,屏蔽中断 134 | ... 135 | try { 136 | beforeExecute(wt, task); 137 | ... 138 | try { 139 | task.run(); // 执行子任务 140 | } catch (RuntimeException x) { 141 | thrown = x; throw x; 142 | } catch (Error x) { 143 | thrown = x; throw x; 144 | } catch (Throwable x) { 145 | thrown = x; throw new Error(x); 146 | } finally { 147 | afterExecute(task, thrown); 148 | } 149 | } finally { 150 | task = null; 151 | w.completedTasks++; 152 | w.unlock(); // 执行完任务,开中断 153 | } 154 | } 155 | completedAbruptly = false; 156 | } finally { 157 | processWorkerExit(w, completedAbruptly); 158 | } 159 | } 160 | ``` 161 | 162 | 1. getTask()方法会从任务队列中取任务,如果工作队列没有任务则工作线程会被阻塞直到被生产者唤醒。 163 | 164 | ```java 165 | private Runnable getTask() { 166 | ... 167 | for (;;) { 168 | ... 169 | try { 170 | Runnable r = timed ? 171 | workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : 172 | workQueue.take(); // 从任务等待队列中取任务 173 | ... 174 | } catch (InterruptedException retry) { 175 | timedOut = false; 176 | } 177 | } 178 | } 179 | ``` 180 | 181 | ### 调用时序 182 | 183 | ![TPE#addWorker](TPE.addWorker.png) 184 | 185 | ## 总结 186 | 187 | ThreadPoolExecutor作为最基础的线程池,提供了多个可配置参数,如核心线程数,最大线程数,线程超时销毁,线程工厂,任务等待队列,拒绝策略等,方便开发者基于此做各种灵活的定制。 188 | 189 | ThreadPoolExecutor利用BlockingQueue实现典型的生产者和消费者模型,为Java开发者提供了优秀的范本。 190 | 191 | 192 | 193 | ## 参考 194 | 195 | http://www.cnblogs.com/zhanjindong/p/java-concurrent-package-ThreadPoolExecutor.html -------------------------------------------------------------------------------- /C&C++/C&C++面试题总结.md: -------------------------------------------------------------------------------- 1 | ### 智能指针的实现原理 2 | 3 | 智能指针类将一个计数器与类指向的对象相关联,通过引用计数法跟踪该类有多少个对象共享同一指针。 4 | 5 | 1. 重写构造函数。当智能指针类初始化时,初始化内部关联的指针,计数器设为1。 6 | 2. 重写复制构造函数。当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数。 7 | 3. 重写赋值运算符。对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数。 8 | 4. 重写析构函数。减少引用计数(如果引用计数减至0,则删除基础对象)。 9 | 5. 重写-> 和 * 操作符,让智能指针模拟普通指针的行为。 10 | 11 | ```c++ 12 | #include 13 | #include 14 | template 15 | class SmartPointer { 16 | private: 17 | T* _ptr; 18 | size_t* _count; 19 | public: 20 | SmartPointer(T* ptr = nullptr) : 21 | _ptr(ptr) { 22 | if (_ptr) { 23 | _count = new size_t(1); 24 | } else { 25 | _count = new size_t(0); 26 | } 27 | } 28 | SmartPointer(const SmartPointer& ptr) { 29 | if (this != &ptr) { 30 | this->_ptr = ptr._ptr; 31 | this->_count = ptr._count; 32 | (*this->_count)++; 33 | } 34 | } 35 | SmartPointer& operator=(const SmartPointer& ptr) { 36 | if (this->_ptr == ptr._ptr) { 37 | return *this; 38 | } 39 | if (this->_ptr) { 40 | (*this->_count)--; 41 | if (this->_count == 0) { 42 | delete this->_ptr; 43 | delete this->_count; 44 | } 45 | } 46 | this->_ptr = ptr._ptr; 47 | this->_count = ptr._count; 48 | (*this->_count)++; 49 | return *this; 50 | } 51 | T& operator*() { 52 | assert(this->_ptr == nullptr); 53 | return *(this->_ptr); 54 | } 55 | T* operator->() { 56 | assert(this->_ptr == nullptr); 57 | return this->_ptr; 58 | } 59 | ~SmartPointer() { 60 | (*this->_count)--; 61 | if (*this->_count == 0) { 62 | delete this->_ptr; 63 | delete this->_count; 64 | } 65 | } 66 | size_t use_count(){ 67 | return *this->_count; 68 | } 69 | }; 70 | ``` 71 | 72 | ### 如何定义一个只能在堆上(栈上)生成对象的类? 73 | 74 | - 只能在堆上创建,将析构函数设为私有 75 | - 只能在栈上创建,将new操作符函数设为私有 76 | 77 | 78 | ### 引用和指针的区别 79 | 80 | 本质:引用是别名,指针是地址,具体的: 81 | 82 | - 指针可以在运行时改变其所指向的值,引用一旦和某个对象绑定就不再改变 83 | - 从内存上看,指针会分配内存区域,而引用不会,它仅仅是一个别名 84 | - 在参数传递时,引⽤用会做类型检查,而指针不会 85 | - 引用不能为空,指针可以为空 86 | 87 | ### const和define的区别 88 | 89 | 本质:define只是字符串替换,const参与编译运行,具体的: 90 | 91 | - const对应编译器;#define对应预处理器 92 | 93 | 94 | - define不会做类型检查,const拥有类型,会执行相应的类型检查 95 | - const内存效率更高,编译器通常将const变量保存在符号表中,而不会分配存储空间,这使得它成 为一个编译期间的常量,没有存储和读取的操作 96 | 97 | ### malloc和new的区别 98 | 99 | - malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。 100 | - new可以认为是malloc加构造函数的执行。new出来的指针是直接带类型信息的。而malloc返回的都是void指针。 101 | - new会先分配内存资源利用operator::new,然后执行构造函数,而malloc不会执行构造函数。 102 | 103 | ### 虚函数的作用和底层实现 104 | 105 | 虚函数的作用:实现多态,使基类指针指向派生类对象时,访问派生类的同名函数,实现动态联编。 106 | 107 | 从C++对象内存模型来看,每个对象的开头部分内存至少有一个指针vfptr,指向一个虚函数表vtable,成员变量根据其继承和声明顺序依次放在后面。虚函数表的每一个表项是父类的虚函数指针(如果自身的类没有复写该虚函数的话),或者是自身的类的函数指针(复写了父类对应的虚函数)。每次通过基类指针调用虚函数,都是通过查找虚函数表最终找到函数指针。子类的成员函数被放到了第一个父类的表中。 108 | 109 | 在父类构造函数中调用虚函数仍然是父类版本的函数,子类中调用的仍然是子类版本的函数。 110 | 111 | 参考: 112 | 113 | - [C++ 虚函数表解析](http://blog.csdn.net/haoel/article/details/1948051) 114 | 115 | - [C++ 对象的内存布局(上)](http://blog.csdn.net/haoel/article/details/3081328) 116 | 117 | - [C++ 对象的内存布局(下)](http://blog.csdn.net/haoel/article/details/3081385) 118 | 119 | - [图说C++对象模型:对象内存布局详解](http://www.cnblogs.com/QG-whz/p/4909359.html) 120 | 121 | 122 | ### 复制构造函数 123 | 124 | 在C++中,有以下三种情况需要copy构造函数: 125 | 126 | 1. 对象以值传递的方式传参 127 | 2. 对象以值传递的方式从函数返回 128 | 3. 对象需要通过另一个对象进行初始化 129 | 130 | ### 为什么要内存对齐 131 | 132 | 在对齐的内存地址上,四字节存取粒度处理器可以一次性的将4个字节全部读出;而在非对齐的内存地址上,读取次数将加倍。 133 | 134 | 参考:[为什么要内存对齐 Data alignment: Straighten up and fly right](http://blog.csdn.net/lgouc/article/details/8235471) 135 | 136 | 137 | 138 | ### list和vector的区别 139 | 140 | - vector和数组类似,它拥有一段连续的内存空间,并且起始地址不变,因此它能非常好的支持随机存取(即使用[]操作符访问其中的元素),但由于它的内存空间是连续的,所以在中间进行插入和删除会造成内存块的拷贝(复杂度是O(n)),另外,当该数组后的内存空间不够时,需要重新申请一块足够大的内存并进行内存的拷贝。这些都大大影响了vector的效率。 141 | - list是由数据结构中的双向链表实现的,因此它的内存空间可以是不连续的。因此只能通过指针来进行数据的访问,这个特点使得它的随机存取变的非常没有效率,需要遍历中间的元素,搜索复杂度O(n),因此它没有提供[]操作符的重载。但由于链表的特点,它可以以很好的效率支持任意地方的删除和插入。 142 | 143 | 144 | ### 虚函数表在内存中的存放位置 145 | 146 | 在静态存储区。类对象的继承关系在编译时已经能够知道了,因此具体的虚表内容,在编译时就决定了整个继承链上V-table的信息。 147 | 148 | ### 内存管理 149 | 150 | #### 内存分区 151 | 152 | - 栈:在执行函数时,函数局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元会被自动释放。对栈的内存分配和释放的相关指令内置于处理器的指令集中,效率很高,但容量有限; 153 | - 堆:那些由 `new`分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个`new`就要对应一个 `delete`。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。 154 | - 全局/静态存储区:全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。 155 | - 常量存储区:这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改。 156 | 157 | #### 堆和栈的区别 158 | 159 | - 管理方式的不同。栈是由编译器自动管理的, 无需手工控制;堆的释放工作由程序员负责。 160 | - 碎片问题。频繁的new和delete会造成内存空间的不连续,从而造成大量的碎片。一个防止堆破碎的通用方法是从不同固定大小的内存持中分配不同类型的对象。对每个类重载`new` 和`delete`就提供了这样的控制。 161 | - 分配效率。栈是计算机底层支持的数据结构,压栈和出栈有专门的指令,因此栈的内存分配效率较高。堆的内存分配由C函数库支持,如果堆的碎片太多会大大降低内存分配的效率。 162 | 163 | #### 野指针的形成 164 | 165 | - 指针变量没有初始化,指针变量默认值是随机的,应该初始化为NULL 166 | - 指针被free或delete后没有置为NULL 167 | - 指针操作超越了变量的作用域范围 168 | 169 | #### malloc/free VS new/delete 170 | 171 | `malloc`与`free`是C++/C语言的标准库函数,`new/delete`是C++的运算符。它们都可用于申请动态内存和释放内存。 172 | 对于非内部数据类型的对象而言,光用`maloc/free`无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于`malloc/free`是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于`malloc/free`。 173 | 因此C++语言需要一个能完成动态内存分配和初始化工作的运算符`new`,以及一个能完成清理与释放内存工作的运算符`delete`。 174 | 175 | **参考:** [C/C++内存管理详解](C/C++内存管理详解) 176 | 177 | ### 用C语言实现memmove函数 178 | 179 | ```c 180 | /** 181 | 使用memmove来进行拷贝要考虑区间重叠问题,否则在拷贝过程中可能造成重叠错误。 182 | 针对有可能出现的问题给出了一定的处理措施来防止拷贝出错: 183 | (1)当源内存的首地址等于目标内存的首地址时,不进行任何拷贝 184 | (2)当源内存的首地址大于目标内存的首地址时,实行正向拷贝 185 | (3)当源内存的首地址小于目标内存的首地址时,实 行反向拷贝 186 | */ 187 | void memmove(void *dest, const void *src, int n) { 188 | if (dest == NULL || src == NULL) return; 189 | char *p, *q; 190 | int step; 191 | if ((char *) dest == (char *) src) { 192 | return; 193 | } else if ((char *) dest < (char *) src) { 194 | step = 1; 195 | p = src; 196 | q = dest; 197 | } else { 198 | step = -1; 199 | p = src + n - 1; 200 | q = dest + n - 1; 201 | } 202 | for (int i = 0; i < n; ++i) { 203 | *q = *p; 204 | p += step; 205 | q += step; 206 | } 207 | } 208 | ``` 209 | 210 | -------------------------------------------------------------------------------- /Java/JVM/GC算法与内存分配策略.md: -------------------------------------------------------------------------------- 1 | ## 什么时候回收?When 2 | 3 | 当方法结束或线程结束时,内存自然就跟着回收了。 4 | 5 | ## 哪些对象需要回收?What 6 | 7 | 无法被其他途径引用的对象需要被回收,如何判断一个对象已死?有两种判断方法。 8 | 9 | ### 引用计数算法 10 | 11 | 给对象添加一个引用计数器,每当一个地方引用它时,计数器的值就加一;当引用失效时,计数器的值就减一。当计数器的值为0时,证明该对象不可能再被引用了。 12 | 13 | 这种算法的优点:实现简单,判定效率高。缺点:很难解决对象之间的**相互循环引用**的问题。 14 | 15 | ### 可达性分析算法 16 | 17 | 通过一系列的GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径成为引用链,当一个对象到GC Roots没有任何引用链相连则证明此对象不可用。 18 | 19 | ![可达性分析算法](可达性分析算法.png) 20 | 21 | GC Roots对象包括: 22 | 23 | 1. 虚拟机栈 栈帧中的本地变量表的引用对象 24 | 2. 方法区中类静态属性引用变量 25 | 3. 方法区中常量引用的对象 26 | 4. Native 方法引用的对象 27 | 28 | ### Java中引用的类型 29 | 30 | - 强引用。GC不会回收被强引用的对象。 31 | - 软引用。描述一些还有用但非必需的对象。再OOM之前,将会把软引用关联的对象进行回收。 32 | - 弱引用,描述非必需对象。GC会回收掉只被弱引用关联的对象。 33 | - 虚引用。无法用过虚引用获取对象实例,唯一目的是能在这个对象被GC时收到一个系统的通知。 34 | 35 | ## GC算法 How 36 | 37 | ### 标记-清除算法 38 | 39 | 首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。 40 | 41 | 缺点:效率不高;空间问题,会产生大量不连续的内存碎片,导致大对象无法分配。 42 | 43 | ### 复制算法 44 | 45 | 将内存分为两等块,每次使用其中一块。当这一块内存用完后,就将还存活的对象复制到另外一个块上面,然后再把已经使用过的内存空间一次清理掉。 46 | 47 | 缺点:浪费一半的内存;如果对象存活率很高,复制的操作会有不少的开销。 48 | 49 | ### 标记-整理算法 50 | 51 | 标记:它的第一个阶段与标记/清除算法是一模一样的,均是遍历GC Roots,然后将存活的对象标记。 52 | 53 | 整理:移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收。 54 | 55 | 标记/整理算法不仅可以弥补标记/清除算法当中,内存区域分散的缺点,也消除了复制算法当中,内存减半的高额代价。 56 | 57 | 标记/整理算法唯一的缺点就是效率也不高,效率低于复制算法。 58 | 59 | ### 分代搜集算法 60 | 61 | JVM对象可以分为新生代,老年代和永生代。新生代和老年代位于Java堆,而永生代位于方法区。 62 | 63 | 由于新生代的存活率低,适合用复制算法;老年代的存活率高,适合用标记-清除法或标记整理法。 64 | 65 | 因此,JVM将内存的80%分为Eden空间和各10%的Survivor空间。当回收时,将Eden和Survior中还存活的对象一次性复制到另一个Survior空间上,最后清理掉Eden空间和刚才用过的Survior空间。 66 | ![分代搜集法](分代搜集法.jpg) 67 | 68 | ​ JVM在进行GC时,并非每次都对上面三个内存区域一起回收的,大部分时候回收的都是指新生代。因此GC按照回收的区域又分了两种类型,一种是普通GC(minor GC),一种是全局GC(major GC or Full GC),它们所针对的区域如下。 69 | 70 | ​ **普通GC(minor GC)**:只针对新生代区域的GC。 71 | 72 | ​ **全局GC(major GC or Full GC)**:针对年老代的GC,偶尔伴随对新生代的GC以及对永久代的GC。 73 | 74 | ​ 由于年老代与永久代相对来说GC效果不好,而且二者的内存使用增长速度也慢,因此一般情况下,需要经过好几次普通GC,才会触发一次全局GC。 75 | 76 | 大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间分配时,虚拟机将发起一次Minor GC。 77 | 78 | Minor GC(新生代回收)的触发条件比较简单,Eden空间不足就开始进行Minor GC 79 | 80 | 回收新生代。而Full GC(老年代回收,一般伴随一次Minor GC)则有几种触发条件: 81 | 82 | (1)老年代空间不足 83 | 84 | (2)PermSpace空间不足 85 | 86 | (3)统计得到的Minor GC晋升到老年代的平均大小大于老年代的剩余空间 87 | 88 | ### 内存分代 89 | 90 | 从对象分代的角度来说,JVM的内存划分大致分为3块:分别是`Permanent Generation`(简称PermGen、持久代)、`New Generation`(又叫Young Generation,年轻代)和`Tenured Generation`(又叫Old Generation,年老代)。其中Permanent Generation位于non-heap区。New和Old是Java应用的Heap区,用来存放类的实例Instance的。 91 | 92 | ![JVM内存分代](JVM内存分代.png) 93 | 94 | 其中New Generation的目标就是尽可能快速的收集掉那些生命周期短的对象;New Generation又分为Eden Space,From Survivor Space和To Survivor Space三块,Eden Space用于存放新创建的对象,From区和To区都是救助空间Survivor Space(图中的S0和S1);当Eden区满时,JVM执行垃圾回收GC(Garbage Collection),垃圾收集器暂停应用程序,并会将Eden Space还存活的对象复制到当前的From救助空间,一旦当前的From救助空间充满,此区的存活对象将被复制到另外一个To区,当To区也满了的时候,从From区复制过来并且依然存活的对象复制到Old区,从而From和To救助空间互换角色,维持活动的对象将在救助空间不断复制,直到最终转入Old域。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来对象,和从前一个Survivor复制过来的对象,而且,Survivor区总有一个是空的。同时,根据程序需要,Survivor区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。每一次垃圾回收后,Eden Space都会被清空。 95 | 96 | Old区用于存放长寿的对象,在New区中经历了N次垃圾回收后仍然存活的对象,就会被放到Old区中;如那些与业务信息相关的对象,包括Http请求中的Session对象、线程、Socket连接,这类对象跟业务直接挂钩,因此生命周期比较长。 97 | 98 | ### 内存分配与回收策略 99 | 100 | 1. 对象优先在新生代Eden区分配,如果启动了本地线程缓冲,将按线程优先在TLAB上分配。当Eden区内存不足时,将促发一次Minor GC。 101 | 2. 大对象直接进入老年代。 102 | 3. 长期存活的对象将进入老年代。JVM为每一个对象定义了一个对象年龄计数器,每熬过一次Minor GC就增加一岁,年龄增加到一定阈值将晋升到年老代。 103 | 104 | ## 垃圾收集器 105 | 106 | 参考:http://www.tianshouzhi.com/api/tutorials/jvm/96 107 | 108 | ### CMS收集器 109 | 110 | 一种以获取最短回收停顿时间为目标的收集器,基于标记清除算法。整个过程分为4个步骤: 111 | 112 | - 初始标记:需要Stop World,仅仅标记一下GC Roots能直接关联到的对象。 113 | - 并发标记:GC Roots tracing。 114 | - 重新标记:修正并发标记期间产生变动那一部分对象的标记记录。 115 | - 并发清除:可以与用户线程并发执行。 116 | 117 | ![CMS运行图](CMS运行图.png) 118 | 119 | 优点:并发低停顿 120 | 121 | 缺点: 122 | 123 | - 会产生大量碎片。 124 | - 对CPU资源非常敏感,CMS需要的CPU资源较多,导致应用程序变慢,总吞吐量会降低。 125 | - 无法处理浮动垃圾。由于CMS并发清理时用户线程再次产出的垃圾成为浮动垃圾。 126 | 127 | ### G1 128 | 129 | http://www.importnew.com/15311.html 130 | 131 | - 基于标记-整理算法,不会产生空间碎片。 132 | - 极力避免全区域的GC。将整个堆分成多个大小固定的Region,跟踪区域里面的垃圾堆积程度,在后台维护一个优先列表,优先回收垃圾最多的区域。 133 | 134 | ## Android中的虚拟机 135 | 136 | ### Dalvik虚拟机 137 | 138 | **Java堆** 139 | 140 | Java堆实际上是由一个Active堆和一个Zygote堆组成的。当创建第一个应用程序进程时,会将已经使用了的那部分堆内存划分为一部分,还没有使用的堆内存划分为另外一部分。前者就称为Zygote堆,后者就称为Active堆。这样只需把zygote堆中的内容复制给应用程序进程就可以了。以后无论是Zygote进程,还是应用程序进程,当它们需要分配对象的时候,都在Active堆上进行。这样就可以使得Zygote堆尽可能少地被执行写操作,因而就可以减少执行写时拷贝的操作。在Zygote堆里面分配的对象其实主要就是Zygote进程在启动过程中预加载的类、资源和对象了。 141 | 142 | **GC算法和内存碎片** 143 | 144 | 主流的大部分Davik采取的都是标注与清理(Mark and Sweep)回收算法,也有实现了拷贝GC的,这一点和HotSpot是不一样的,具体使用什么算法是在编译期决定的,无法在运行的时候动态更换。 145 | 146 | 采用标记清除法时,难以避免会有内存碎片的问题。所以对于Dalvik虚拟机的手机来说,我们首先要尽量避免掉频繁生成很多临时小变量(比如说:getView, onDraw等函数中new对象),另一个又要尽量去避免产生很多长生命周期的大对象。当VM不足以为一个对象分配内存时,会触发GC。频繁的GC会造成卡顿的问题。 147 | 148 | ### ART内存回收机制 149 | 150 | **Java堆** 151 | 152 | ART运行时内部使用的Java堆的主要组成包括Image Space、Zygote Space、Allocation Space和Large Object Space四个Space,Image Space用来存在一些预加载的类, Zygote Space和Allocation Space与Dalvik虚拟机垃圾收集机制中的Zygote堆和Active堆的作用是一样的,Large Object Space就是一些离散地址的集合,用来分配一些大对象从而提高了GC的管理效率和整体性能。 153 | 154 | **并发GC与非并发GC** 155 | 156 | Alloc内存不够的时候会采用非并发GC,而在Alloc后发现内存达到一定阀值的时候又会触发并发GC。 157 | 158 | - **非并发GC** 159 | 160 | 步骤1. 调用子类实现的成员函数InitializePhase执行GC初始化阶段。 161 | 162 | 步骤2. 挂起所有的ART运行时线程。 163 | 164 | 步骤3. 调用子类实现的成员函数MarkingPhase执行GC标记阶段。 165 | 166 | 步骤4. 调用子类实现的成员函数ReclaimPhase执行GC回收阶段。 167 | 168 | 步骤5. 恢复第2步挂起的ART运行时线程。 169 | 170 | 步骤6. 调用子类实现的成员函数FinishPhase执行GC结束阶段。 171 | 172 | - **并发GC** 173 | 174 | 步骤1. 调用子类实现的成员函数InitializePhase执行GC初始化阶段。 175 | 176 | 步骤2. 获取用于访问Java堆的锁。 177 | 178 | 步骤3. 调用子类实现的成员函数MarkingPhase执行GC并行标记阶段。 179 | 180 | 步骤4. 释放用于访问Java堆的锁。 181 | 182 | 步骤5. 挂起所有的ART运行时线程。 183 | 184 | 步骤6. 调用子类实现的成员函数HandleDirtyObjectsPhase处理在GC并行标记阶段被修改的对象。 185 | 186 | 步骤7. 恢复第4步挂起的ART运行时线程。 187 | 188 | 步骤8. 重复第5到第7步,直到所有在GC并行阶段被修改的对象都处理完成。 189 | 190 | 步骤9. 获取用于访问Java堆的锁。 191 | 192 | 步骤10. 调用子类实现的成员函数ReclaimPhase执行GC回收阶段。 193 | 194 | 步骤11. 释放用于访问Java堆的锁。 195 | 196 | 步骤12. 调用子类实现的成员函数FinishPhase执行GC结束阶段。 197 | 198 | **前后台GC** 199 | 200 | 前台Foreground指的就是应用程序在前台运行时,而后台Background就是应用程序在后台运行时。应用程序在前台运行时,响应性是最重要的,因此也要求执行的GC是高效的。相反,应用程序在后台运行时,响应性不是最重要的,这时候就适合用来解决堆的内存碎片问题。因此,Mark-Sweep GC适合作为Foreground GC,而Mark-Compact GC适合作为Background GC。 201 | 202 | 203 | 204 | ART在GC上做的比Dalvik好太多了,不光是GC的效率,减少Pause时间,而且还在内存分配上对大内存的有单独的分配区域,同时还能有算法在后台做内存整理,减少内存碎片。对于开发者来说ART下我们基本可以避免很多类似GC导致的卡顿问题了。另外根据谷歌自己的数据来看,ART相对Dalvik内存分配的效率提高了10倍,GC的效率提高了2-3倍。 205 | 206 | ## 参考 207 | 208 | - http://www.cnblogs.com/zuoxiaolong/p/jvm6.html 209 | - http://www.cnblogs.com/zuoxiaolong/p/jvm5.html 210 | - http://www.cnblogs.com/leesf456/p/5218594.html 211 | - http://www.tianshouzhi.com/api/tutorials/jvm/97 212 | - http://www.tianshouzhi.com/api/tutorials/jvm/96 213 | 214 | -------------------------------------------------------------------------------- /网络基础/TCP_IP.md: -------------------------------------------------------------------------------- 1 | ## OSI七层模型的作用 2 | 3 | ![OSI七层模型](OSI七层模型.png) 4 | 5 | 6 | 7 | ## 电路交换和分组交换 8 | 9 | 电路交换:计算机之间发送数据时,需要独占双方之间的通信线路。 10 | 11 | 分组交换:将计算机所要发送的数据分成若干个数据包,按照一定排列顺序后分别发送。这样就可以所有计算机一起收发数据。 12 | 13 | 发送端计算机将数据分组发送给路由器,路由器收到这些分组数据后,缓存到相应队列中,再转发给目标计算机。 14 | 15 | 16 | 17 | ## 网络的构成要素 18 | 19 | ![网络构成要素](网络构成要素.png) 20 | 21 | 22 | 23 | ## TCP/IP 协议分层模型 24 | 25 | ![TCP-IP与OSI参考模型](TCP-IP与OSI参考模型.png) 26 | 27 | ### 通信示例 28 | 29 | 数据包的结构: 30 | 31 | ![数据包的结构](数据包的结构.png) 32 | 33 | 1. 应用程序对数据进行处理 34 | 2. TCP根据应用层(会话层)的指示,负责建立连接,发送数据以及断开连接。TCP层会在应用层数据的前端附加一个TCP首部。TCP首部中包含源端口号和目标端口号,序号(用以发送的包中哪部分是数据)以及校验和。 35 | 3. IP将TCP传过来的数据前端附上IP首部,包含接收端IP地址以及发送端IP地址,紧接着还有用来判断其后面数据是TCP还是UDP的信息。IP包生成后,参考路由控制表决定接受此IP包的路由器或主机。如果不知道接收端的MAC地址,可以利用ARP查找。然后就可以将MAC地址和IP地址交给以太网的驱动程序了。 36 | 4. 以太网驱动负责附上以太网首部并进行发送处理。以太网首部中包含接收端MAC地址,发送端MAC地址以及标志以太网类型的以太网数据的协议。 37 | 38 | 39 | 40 | ## IP协议 41 | 42 | 网络层的主要作用是实现终端节点之间的通信。 43 | 44 | 数据链路的作用就相当于某一段行程的机票或火车票;而IP的作用就相当于旅行的行程表。 45 | 46 | 所有主机都维护者一张路由控制表,记录IP数据在下一步应该发给哪个路由器。 47 | 48 | ![IP分片](IP分片.png) 49 | 50 | 再网络上遇到比较大的报文无法一下子发送出去时,路由器往往会进行IP分片处理。经过分片之后的IP数据报在被重组的时候,只能由目标主机进行。路由器虽然做分片但不会进行重组。 51 | 52 | ### IP地址 53 | 54 | 每一台主机上的每一块网卡都设置有IP地址。IP地址(IPv4)由32位正整数表示,每8位位一组,分成4组,每组以"."隔开,再将每组数转换为十进制数。 55 | 56 | IP地址由网络地址和主机地址两部分组成。IP包被转发到途中某个路由器时,正是利用目标IP地址的网络地址进行路由。通过网络地址可以判断时哪一个网段内的主机,同一网段的主机地址必须不一样。 57 | 58 | #### IP地址的分类: 59 | 60 | ![IP地址的分类](IP地址的分类.png) 61 | 62 | #### 子网掩码: 63 | 64 | IP地址与子网掩码做&运算后可得到网络地址。 65 | 66 | | IP地址类别 | 子网掩码 | 67 | | ------ | ------------- | 68 | | A | 255.0.0.0 | 69 | | B | 255.255.0.0 | 70 | | C | 255.255.255.0 | 71 | 72 | 73 | 74 | 公有IP地址是整个互联网唯一的,但私有IP地址只需要局域网内保证唯一即可。 75 | 76 | 77 | 78 | #### IPv6 79 | 80 | IP地址长度为128位,每16位一组,每组用冒号分隔并用十六进制表示。 81 | 82 | ### 路由控制 83 | 84 | 路由控制表中记录着网络地址与下一步应该发送至路由器的地址。路由控制表的形成方式有静态路由控制和动态路由控制两种。前者是管理员手动设置,后者是路由器与其他路由器相互交换信息时自动刷新。 85 | 86 | ### IP首部 87 | 88 | IPv4: 89 | 90 | ![IPv4首部](IPv4首部.png) 91 | 92 | 同一分片的标识值相同;标志表示包被分片的信息。片偏移标识被分片的每一个分段相对于原始数据的位置。 93 | 94 | 95 | 96 | ### ARP 97 | 98 | ARP是一种解决地址问题的协议。以目标IP地址为线索,用来定位下一个应该接收数据分包的网络设备对应的MAC地址。 99 | 100 | ### ICMP 101 | 102 | ICMP(Internet Control Message Protocol)主要功能包括:确认IP包是否成功送达目标地址,通知在发送过程中IP包被废弃的具体原因。ping 使用的是ICMP协议,它发送icmp回送请求消息给目的主机。ICMP协议规定:目的主机必须返回ICMP回送应答消息给源主机。如果源主机在一定时间内收到应答,则认为主机可达。 103 | 104 | **ICMP的应用--Traceroute** 105 | 106 | 它可以追踪路由路径,它的原理是向目的主机发送ICMP报文,发送第一个报文时,设置TTL为0,TTL即Time to Live,是报文的生存时间,由于它是0,所以下一个路由器由到这个报文后,不会再继续转发了,会给源主机发送ICMP出错的报文,就可以知道第一个路由的IP地址,同理,设置TTL为1,就可以知道第二个路由的IP地址,依次类推。 107 | 108 | ### DHCP 109 | 110 | DHCP(Dynamic Host Configuration Protocol) 是为了实现自动设置IP地址,统一管理IP地址的分配。DHCP属于应用层协议,并且使用的是UDP协议的应用层协议。 111 | 112 | ### NAT 113 | 114 | NAT(Network Address Translator) 是用于在本地网络中使用私有地址,在连接互联网时转而使用全局IP地址的技术。 115 | 116 | 117 | 118 | ## TCP 与 UDP 119 | 120 | 从IP层来说,通信的两端是两个主机。IP协议虽然能把分组送到目的主机,但这个分组还停留在主机的网络层而没有交付中的应用进程。从运输层的角度,真正的通信端点是主机中的进程。 121 | 122 | 运输层有一个重要的功能:复用和分用。复用是指在发送方不同的应用进程都可以使用同一个运输层协议传送数据;分用是指接收方的运输层剥去报文首部分发给对应的应用进程。 123 | 124 | IP数据报只检验首部是否出现差错而不检查数据部分,运输层会对收到的报文进行差错检测。 125 | 126 | ### UDP 127 | 128 | UDP(用户数据报协议 User Datagram Protocol)利用IP提供面向无连接的通信服务。若传输过程出现丢包,UDP不负责重发,甚至出现包到达顺序乱掉时也没有纠正的功能。UDP只是在网络层增加了分用和复用功能以及差错检测的功能。 129 | 130 | ### TCP 131 | 132 | TCP(传输控制协议 Transmission Control Protocol)通过校验和,序列号,确认应答,重发控制,连接管理,拥塞控制和窗口控制等机制实现可靠性传输。 133 | 134 | #### 通过序列号提高应答可靠性 135 | 136 | 序列号是按顺序给发送数据的每一个字节都标上号码的标号。接收端查询接收数据TCP首部中的序列号和数据的长度,将自己下一步应该接收的序号作为确认应答(ACK)返回。 137 | 138 | #### TCP重传机制 139 | 140 | TCP要保证所有的数据包都可以到达,所以,必需要有重传机制。接收端给发送端的Ack确认只会确认最后一个连续的包。比如,发送端发了1,2,3,4,5一共五份数据,接收端收到了1,2,于是回ack 3,然后收到了4(注意此时3没收到)。这时服务器不能对4进行确认,否则发送端就会以为4之前的包都已经收到了。这时需要让发送端重传3。 141 | 142 | ##### 超时重传 143 | 144 | 如果等待确认应答到来超时,发送端会将数据进行重发。那么发送端有两种重传的策略: 145 | 146 | - 仅仅重传TimeOut的包,也就是第三份数据 147 | - 重传TimeOut后的所有数据,也就是第3,4,5这三份数据。 148 | 149 | 前者的优点是节约带宽;后者的优点是效率会快一些。 150 | 151 | ##### 快速重传 152 | 153 | 不以时间驱动,而是以数据驱动重传。如果,包没有连续到达,就ack最后那个可能被丢了的包,如果发送方连续收到3次相同的ack,就重传。快速重传解决的是发送端不需要等待超时才发送丢包,但仍然面临同样的选择,一是仅重传TimeOut的包,二是重传包含TimeOut包之后的所有包。 154 | 155 | ##### 停止等待ARQ协议 156 | 157 | 每发送完一个分组就停止发送,等待对方的确认。在收到确认后再发送下一个分组。需要做到: 158 | 159 | - 超时重传 160 | - 暂存已发送的分组 161 | - 确认丢失和确认迟到 162 | 163 | 缺点:信道利用率低。 164 | 165 | ##### 连续ARQ协议 166 | 167 | 发送方每收到一个确认就把发送窗口向前滑动一个分组的位置。接收方一般都是采用累积确认的方式,接收方不必对收到的分组 逐个发送确认,而是在收到几个分组后,对按序到达的最后一个分组发送确认。 168 | 169 | 回退N重传(Go-Back-N): 170 | 171 | - 如果发送方发送了前5个分组,第三个分组丢失了。那么发送方只能把第三个分组之后的分组都重传一遍。 172 | 173 | 选择重传(Selective Repeat): 174 | 175 | - 发送点连续发送数据包但对每个数据包都设有个一个计时器。 176 | - 当在一定时间内没有收到某个数据包的ACK时,发送点只重新发送那个没有ACK的数据包。 177 | 178 | #### 连接管理 179 | 180 | 一个连接的建立与断开需要三次握手和四次挥手。 181 | 182 | ![TCP连接管理](TCP连接管理.png) 183 | 184 | #### 滑动窗口协议 185 | 186 | 滑动窗口协议在在发送方和接收方之间各自维持一个滑动窗口,发送发是发送窗口,接收方是接收窗口,发送窗口的大小根据接收窗口来设置的。它允许发送方发送多个分组而不需等待确认。 187 | 188 | 对于发送窗口: 189 | 190 | - 凡是已经发送过的数据,在未收到确认之前,都必须暂时保留,以便在超时重传时使用。 191 | - 当发送方A发送的数据经过一段时间没有收到确认(由超时计时器控制),就要使用回退N步协议,回到最后接收到确认号的地方,重新发送这部分数据。 192 | - 收到确认应答的情况下,将窗口滑动到确认应答中的序列号的位置。 193 | - 发送窗口左边是已发送并收到确认的字节,窗口里面是已发送但未收到确认的字节,窗口右边是未发送的字节。 194 | 195 | 对于接受窗口: 196 | 197 | - 对于不按序到达的数据先暂时存放在接受窗口中,等到字节流中所缺少的字节收到后,再按序交付上层应用进程。 198 | - 接收方有累积确认和捎带确认功能。 199 | 200 | ![滑动窗口控制](滑动窗口控制.png) 201 | 202 | 使用了窗口控制,某些确认应答即使丢失也无需重发,只要后续数据段能收到确认应答。在报文段丢失的情况下,同一个序列号的确认应答将会不断重复返回,而发送端主机如果连续3次收到同一个确认应答,就会将其所对应的数据进行重发。 203 | 204 | #### 流量控制 205 | 206 | **流量控制就是利用可变的滑动窗口,让发送方的发送速度不要太快,要让接收方来得及接受。**发送方的发送窗口不能超过接收方给出的接受窗口的数值。TCP的滑动窗口是以字节为单位的。 207 | 208 | 死锁:当B给A发送了零窗口报文后一段时间,又给A发送了一个非零窗口的报文。假如这个报文中途丢失,A一直等待B的零窗口报文,而B一直等待A发送数据。解决方法:TCP为每个连接设有一个持续计时器,只要TCP连接的一方收到对方的零窗口通知,就启动计时器。若计时器时间到就发送一个零窗口探测报文段,探测现在的窗口值。 209 | 210 | Nagle算法:解决某些应用进程逐个进程送到TCP发送缓存中来,发送方先发送第一个字节,后面到达的额数据先缓存起来,之后再一起发送。 211 | 212 | 糊涂窗口综合症:接收方的某些应用进程逐个字节从TCP接受缓存中读取,每读取一个字节都向发送方传窗口更新报文段。 213 | 214 | ![流控制](流控制.png) 215 | 216 | #### 拥塞控制 217 | 218 | 拥塞控制就是防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。 219 | 220 | 控制方法: 221 | 222 | - 慢启动 223 | 224 | 先设置拥塞窗口cwnd = 1,每经过一个传输轮次,拥塞窗口cwnd就加倍。 225 | 226 | - 拥塞避免 227 | 228 | 为了防止拥塞窗口增长过大引起网络拥塞,还需设置一个慢启动门限ssthresh状态变量。 229 | 230 | 当cwnd=ssthresh时,改用拥塞避免算法。 233 | 234 | 让cwnd按线性规律缓慢增长。 235 | 236 | - 快重传 237 | 238 | 要求接收方每接收到要给失序的报文段后就立即发出重复确认,让发送方及早知道有报文段没有到达对方。 239 | 240 | 当发送方收到三个重复的确认执行快重传算法。 241 | 242 | - 快恢复 243 | 244 | 把cwnd值减半,然后执行拥塞避免算法,使拥塞窗口缓慢地线性增大。 245 | 246 | ![拥塞控制曲线图](拥塞控制曲线图.png) 247 | 248 | #### Nagle算法 249 | 250 | Nagle算法的核心思想是:等数据积累多了再一起发出去,大概等待200ms,这样可以提高网络的吞吐率。目的是为了防止有效内容在TCP包中占比太低。 251 | 252 | 但是在现在光纤的时代,带宽和速度已经不是太大的问题了,如果每个请求都要延迟200ms,会造成实时性比较差。所以通常是要把Nagle算法禁掉,可以在创建套接字的时候设置TCP_NODELAY标志位。 253 | 254 | 255 | 256 | **参考** 257 | 258 | - [TCP 的那些事儿(上)](https://coolshell.cn/articles/11564.html) 259 | - [TCP 的那些事儿(下)](https://coolshell.cn/articles/11609.html) 260 | 261 | ### 首部格式 262 | 263 | ![UDP首部格式](UDP首部格式.png) 264 | 265 | 长度为UDP用户数据报长度,最小值为8。 266 | 267 | ![TCP首部格式](TCP首部格式.png) 268 | 269 | - 源端口和目的端口 各占2个字节。 270 | - 序号:占4个字节,在一个TCP连接中传送的字节流中每一个字节都按序编号。 271 | - 确认号:占4个字节,期望收到对方下一个报文段的第一个数据字节的序号。 272 | - 数据偏移:占4位,TCP首部的长度。 273 | - 窗口大小:以字节为单位 274 | 275 | ## 应用层协议 276 | 277 | ### 远程登录 278 | 279 | - TELNET 利用TCP一条连接,向主机发送文字命令并在主机上执行。 280 | - SSH 是加密的远程登录系统。TELNET中登录时无需输入密码就可以发送 281 | 282 | ### 文件传输 283 | 284 | FTP使用两条TCP连接,一条用来控制,一条用来数据的传输。 285 | 286 | ### 电子邮件 287 | 288 | ![SMTP](SMTP.png) 289 | 290 | --------------------------------------------------------------------------------