java 性能优化实践 硬件和操作系统
来源《java 性能优化实践》
/** * java 性能优化实践 * 硬件和操作系统 * 硬件性能提升是软件应用的希望之花,软件正在吞噬整个世界; * @内存 *更快的时钟速度意味着每秒能完成更多的指令执行 *更快的芯片需要更快的数据流来执行 * 主存储器已经无法满足处理器核心对新数据的需求 * 如果CPU等待数据,再快的周期也是浪费 * @内存高速缓存 *CPU上的内存区域,比寄存器慢,但是比主存快 * 思路——对于频繁访问的内存位置,让CPU将对应的内存数据的副本填充到高速缓存中,而不是每次都到主存中读取 * *CPU缓存—L1,L2 * 专用私有的CPU内部高速缓存 *高速缓存的架构实现方法可以缩短访问时间。有助于核心储备足够的数据用于操作 * *#高速缓存一致性协议保证高速缓存和主存储器之间的数据一致性问题 * *MESI协议 回写高速缓存一致性协议 * 首字母代表四种状态 * M modified 修改但未刷新到主存 * E exclusive 只出现在当前的高速缓存中,数据和主存一致 * S shared 可能也出现在其他高速缓存中 数据和主存一致 * I invalid 可能未被使用 会尽快废弃 * 该协议的理念是多个处理器可以同时处于共享状态,但是如果其中一个转换到其他任何一个有效状态(修改或独占) * 那么强制其他处理器进入无效状态 * 该协议通过将某个处理器想要改变状态的意图广播出去来工作的 * 通过共享内存总线发送一个电信号,其他处理器就能意识到完整状态转换的逻辑图 * #java性能的关键主题之一是应用程序对对象分配率的敏感性 * * #翻译后备缓冲器 TLB(translation lookaside buffer) * 充当页表的高速缓存,而页表是将虚拟内存地址映射到物理地址 * TLB可以大大提升访问虚拟地址下的物理地址这一非常频繁的操作速度 * 如果没有TLB支持,即使页表被保存L1中,所有虚拟地址查找也需要花费16个时钟周期 * * #分支预测和推测执行 * 分支预测用于防止出现处理器必须等待计算条件分支所需要的值的情况 * CPU用赌博的方式填充流水线,如果判断成功,CPU会向往常一样继续执行 *如果判断错误,部分被执行的指令就会被丢弃,还必须接受流水线被清空的可能 * 曾经玩数独的高级阶段是不是需要标记推理看是否可以走通?这样的假设赌博方式 TRUE or FALSE * * #硬件存储器模型 * 在多核CPU如何能够一致访问同一个内存位置? * 一般javac,JIT,cpu都允许改变代码的执行顺序 * 但是前提是任何改变都不能影响当前线程所观察到的结果 * 在java环境中,内存模型被明确的设计为一种弱模型,以便考虑到不同处理器类型之间内存访问的一致性的差异 * 要确保多线程代码工作正常,主要靠正确使用锁和易失性(volatile)访问 * Martin Thompson 机械共鸣 * * #操作系统 * 主要对控制对资源的访问,这些资源可以在多个进程之间共享 * 通过一个中心系统来对访问做仲裁和计量 *稀缺的资源最重要的两个 内存和CPU时间 * 通过内存管理单元(MMU)及其页表的虚拟寻址是实现内存访问控制的关键特性,可以防止一个进程破坏另一个进程所拥有的内存区域 * * #调度器 *对CPU的访问是由进程调度器控制的 *使用一个运行队列,作为有资格运行但必须等待CPU的线程或进程等待区 * 想要运行的总是比能够运行的要多 * 通过一个机制来解决CPU征用问题 * 调度器的工作是响应中断,管理对CPU核心的访问 * 调度器会将线程移动到核心上和将线程从核心上移走,在时间片结束后。调度器会将线程移动到运行队列的后面,等待到达队列的前面再次运行 * * 如果一个线程想要放弃时间片 * 使用sleep 固定时间 * wait 等待某个条件被满足 * 线程也可能阻塞在IO或者软件锁上 * @抖动和噪音是其他进程造成的影响数据 * #时间问题 *windows 上的java计时会有很大差异,原生方法实现不同 * * #上下文切换 *操作系统调度器移除当前正在运行的线程或任务,并将其替换为等待中的线程 *涉及正在执行的指令和线程的栈状态 * *线程切换或者模式(内核,用户)切换都有很高的成本,后者更为重要,需要切换到内核态执行某些操作再次切换为用户模式 * 这种切换会强制清空指令和其他高速缓存。因为用户空间代码所访问的内存区域与内核空间通常没有任何交叉 * 内核切换 TLB和其他高速缓存全部失效。当调用返回时,这些区域必须重新填充。 * 因此内核模式的切换影响会一直持续到控制权返回到用户空间之后。 *LINUX提供了一种vDSO机制,有用的技术 * 用户空间的一块内存区域,用于加速并不是真的需要内核权限的系统调用 * 避免实际的上下文切换到内核模式来提升速度 * * vmstat 1 虚拟内存使用状态 1标识提供持续状态 *r可运行 b阻塞 swpd交互区 free空闲区 buff缓存区 cache高速缓存 *si交换进来的内存量 so交换出的内存量 *bi块的输入 bo块的输出 显示的是从块设备接收到512字节大小的块的个数,以及发送给块设备的个数 *in 中断的次数 cs 每秒上下文切换的次数 *us 用户时间 sy 内核时间 id 空闲时间 wa 等待时间 st 被盗时间——虚拟机 * iostat I/O子系统的当前状态 * 简单的距离系统进的工具给我们最实际的表现数据 * * #垃圾收集 *JVM内存是启动时分配的,并在用户空间管理,说明不需要系统调用分配内存,垃圾收集内核切换活动相当少 *如果某个系统表现出系统的CPU利用率过高,那么它一定没有把大量的时间花在垃圾收集,垃圾收集会让用户空间CPU周期飙升,但不会影响内核空间的CPU利用率 *如果一个JVM进程在用户空间使用了接近100%,排查垃圾收集 * 检查垃圾收集日志查看日志中新条目增加的频率 * 启动垃圾收集日志非常有必要 * * #IO *旋转的铁锈 * * #内核旁通IO *JAVA的New IO 支持绕过java的堆直接与原生内存和底层IO一起工作 *裸金属操作系统 * * #机械共鸣 *JVM提供了一个远离硬件的抽象层 *当两个线程试图修改同一个高速缓存行时,发生竞争 * 第一个线程将使第二个线程的高速缓存失效,导致需要从内存中重新读取 * 一旦第二个线程修改了之后又会影响第一个线程的高速缓存失效,这样的相互影响导致性能下降,伪共享 *理解正在发生的事情,然后才能知道如何解决 */