Reading Note on WG Java Developer Chapter 6

《Java程序员修炼之道》笔记之理解性能调优

引言:本章总体上简要地介绍了性能调优的一般方法以及Java平台的内存管理机制。对基本的概念和思想有个了解即可,真正生产环境的性能调优的话这本书也不够。下面即笔记正文,以概念和提纲为主,枯燥冗长,慎入。

秘诀?没有秘诀。

你必须量体裁衣。没有评测,就没有合适的调优。

评测或者说度量,是一切改进和优化的基础,猜测和凭空分析都不是。

1. 性能术语

  • 等待时间(Latency): 在给定工作量下处理一个任务单元所消耗的时长。(性能肘?)
  • 吞吐量(Throughput):系统在限定资源、限定时长内完成的单位工作量。用的最多的是在某一参考平台(比如指明了硬件配置、操作系统和软件环境的特定品牌服务器)上的每秒事务处理数。
  • 利用率(Utilization):表示可用资源中用来处理工作单元(而不是清理任务或处于空闲状态)的资源百分比。
  • 效率(Efficiency):系统的效率等于吞吐量除以所用资源。
  • 容量(Capacity):任一时刻能通过系统的工作单元(比如事务)数量。也就是在特定的等待时间或吞吐量下,能够得到同步处理的工作单元数量。
  • 扩展性(Scalability):当系统得到更多资源时,它的吞吐量或等待时间就会发生变化。这种发生在吞吐量或等待时间上的变化就是系统的扩展性。
  • 退化(Degradation):如果在不增加资源的情况下增加工作单元或网络系统的客户端,一般等待时间或吞吐量都会发生变化。这是系统在负载增加时出现的退化。

2. 务实的性能分析法

你正在测量的代码有哪些可观测的环节

如何测量那些可观测的环节

  • 知道怎么测量:直接测量,在源码中插入测量代码;
  • 通过类加载自动测量(有价值):特殊的类加载器在方法开始和结束的地方记录进入和退出时间的字节码。类似产品有Optier corefirst,还没有相似的开源软件

这些可观测环节的目标是什么

你怎么判断性能调优是否做好了

  • 优化那些重要,而不是最容易的代码
  • 首先优化那些最重要(通常是调用最频繁)的方法
  • 在遇到那些唾手可得的优化时,把它办了,但要清楚代码的调用频率

性能调优可接受的最大支出是多少

  • 分析和优化所占用的时间(开发人员的开支很大)
  • 所作的调整可能会引入额外的技术复杂度
  • 为了让主处理线程运行得更快,可能会引入额外的线程来执行辅助任务,但这些线程可能会在负载较高时对系统整体产生不可预料的影响

在优化过程中,哪些东西是不能舍弃的

程序员浪费了大量的时间考虑,或担心程序中无关紧要部分的速度,并且那些尝试改进效率的行为实际上有很强的负面影响……过早优化是万恶之源。—— Donald Knuth

作者对于Knuth这段名言的解读,给出了一些实际的建议。书中又说:性能调优的工作之一就是从一开始就写出质地优良、高效运行的代码。更好地认识Java平台,知道它的底层运行机制(比如理解在合并两个字符串时隐含的对象分配),并在编码时考虑到性能问题,才能写出更好的代码。

3. 哪里出错了?我们担心的原因

3.1 摩尔定律——过去和未来的性能趋势

晶片上的晶体管数量每两年翻一番是合算的。

3.2 理解内存延迟层级

数据在哪里?

  • 寄存器
  • 主存
  • 固态硬盘
  • 硬盘

3.3 为什么Java性能调优存在困难

造成调优困难的平台特性主要是

  • 线程调度
  • 垃圾收集(GC)
  • 即时(JIT)编译

4. 来自于硬件的时间问题

4.1 硬件时钟

  • RTC:实时时钟
  • 8254:可编程计时芯片
  • TSC:时间戳计时器——应用最广泛的现代计时器。跟踪CPU运行了多少个周期的CPU计数器。
  • HPET:高精度事件计时器。

4.2 麻烦的nanoTime()

  • currentTimeMillis()和nanoTime()的区别:毫秒级/纳秒级,前者几乎所有情况都和钟表时间相符,后者可能偏离钟表时间

4.3 时间在性能调优中的作用

  • 精确度
  • 准确度
  • 粒度
  • 分布式网络计时

4.4 案例研究:理解缓存未命中

感觉这一节没写清楚,概念:代码热身

5. 垃圾收集

5.1 基本算法

标准的Java进程既有栈又有堆。保存原始型局部变量(引用型局部变量会指向以堆方式分配的内存)。保存要创建的对象。基本算法是标记清除

5.2 标记和清除

Java平台对基本的标记和清除进行了改进,采用分代式垃圾收集

  1. 内存区域:伊甸园;幸存者乐园;终身颐养园;PermGen:为内部结构分配的内存,比如类定义,不是严格的堆内存。

  2. 年轻代收集:年轻的空间指的是伊甸园和幸存者乐园

  3. 完全收集:当年轻收集不能把对象放进终身颐养园时,触发完全收集。老年代的内部迁移和压缩的概念。

  4. 安全点:垃圾收集的时机,让应用线程暂停一会儿

5.3 jmap工具

帮助弄清楚程序运行时的内存使用情况,以及所有内存都用在哪

  • 默认视图
  • 堆视图
  • 柱状视图

5.4 与GC相关的JVM参数

小技巧:把-Xms和-Xmx大小设成一样。

5.5 读懂GC日志

5.6 VisualVM查看内存使用情况

嘿,这个我还真用过!

5.7 逸出分析等

5.8 并发标记清除

5.9 新的收集器:G1的工作原理是把堆分成大小相同的区域,不分年轻年老,然后进行标记清除。问题:为什么这种方法更好?

6. HotSpot的JIT编译