- 浏览: 98042 次
- 性别:
- 来自: 深圳
文章分类
最新评论
5.2 调优
找出性能瓶颈后,接下来就是调优,调优通常可从硬件,操作系统、 JVM 以及程序四方面着手,硬件和操作系统不是本书的重点。下面主要介绍 JVM 及程序方面的一些调优。
5.2.1 JVM 调优
JVM 调优主要是内存管理方面的调优,包括各个代的大小、 GC 策略等。
- 代大小的调优
通常 minor GC 会远快于 Full GC, 各个代大小的设置直接决定了 minor GC 和 Full GC 触发的时机。在代的大小调优上,最关健的参数有:
-Xms –Xmx 决定了 JVM Heap 所能使用的最大空间,生产环境下通常设置为相同值,避免运行时刻动态扩展 JVM 内存空间
-Xmn 决定新生代空间,新生代中 Eden 、 S0 、 S1 三个区域的比率可通过 -XX:SurvivorRatio 来控制
-XX : MaxTemuringThreshold 控制对象在经历过多少次 Minor GC 后才转入旧生代,只有在串行 GC 时有效,其它 GC 方式时由 Sun JDK 自行决定。
- GC 策略的调优
作者对并行 GC 和并发 GC (串行 GC 性能太差,不考虑)的测试结果, CMSCompactatFullCollection 明显要比 ParallelGC 效果好,对应用造成的停顿时间短。
目前内存管理方面, JVM 自身已经做得非常不错了,因此如果不是有确切的 GC 造成性能低的理由,就没必要做过多细节方面的调优。多数情况下只须选择 GC 策略并设置 JVM Heap 的大小即可。此外,应关注 JDK 的新版本对性能方面的提升。
5.2.2 程序调优
CPU 消耗严重的解决方法
1. CPU us 高
根据之前的分析, CPU us 高的原因主要是执行线程无任何挂起动作,一直执行,导致 CPU 没有机会去调度执行其它的线程,造成线程饿死。对这种情况,常用方法是对这种线程的动作增加 Thread.sleep ,释放 CPU 的执行权,降低消耗。
实际的 JAVA 应用有很多类似场景,如多线程的任务执行管理器,它通常通过扫描任务集合列表来执行任务。
另一种经典的场景是状态的扫描。例如某线程要等其它线程改变了值后才可继续执行,此时应采用 wait/notify 机制。
其它如循环次数太多,正则,计算等造成的 CPU us 过高的状况,则要结合业务场景来调优。
对于 GC 频繁造成的 CPU us 过高的现象,则要通过 JVM 调优或程序调优,降低 GC 执行次数。
2. CPU sy 高
主要原因是线程的运行状态要经常切换,此种情况,最简单的办法是减少线程数。
另一个重要原因是线程之间锁竞争激烈,锁竞争更有可能造成系统资源消耗不多,但系统性能不足的现象,降低锁竞争的调优技巧将在后续章节进行讲述。
除以上两种情况外,对于分布式 JAVA 应用而言,还有一种典型现象是应用中有较多的网络 IO 或确实需要一些锁竞争机制,例如数据库连接池,但为了能够支撑高的并发量,在 JAVA 应用中又只能借助启动更多的线程来支撑,这种情况下当并发量达到一定程度后,可能会造成 CPU sy 高的现象,可采用协程 (Coroutine) 来解决。在 JAVA 中目前主要可用现实现协程的框架为 Kilim ,目前 Kilim 的版本仅为 0.7 ,且没有商用的实际例子,要使用还需慎重。
JDK7 也有一个支持协程方式的实现,感兴趣的读者可进一步阅读。
文件 IO 消耗严重的解决方法
从程序角度而言, 要原因是多个线程在写大量的数据到同一文件,导致文件很快变得很大,导致写入速度越来越慢,并造成各线程激烈争抢文件锁。对这类情况,常用的调优办法有如下几种
- 异步写文件
如写日志,可用 log4j 提供的 AsyncAppender
- 批量读写
- 限流
还是用写日志作例子,当出现大量异常时,会出现所有的线程都在执行 log.error(…). 此时可采取一个简单的策略为统计一段时间内的 log.error 的执行频率。当超过这个频率时,一段时间内不再写 log ,或塞入一个队列后缓慢地写。
- 限制文件大小
此外,应尽可能地采用缓冲区等方式来读取文件内容,避免不断与操作系统交互,具体可参见 SUN 官方的关于 JAVA 文件 IO 优化的文章。
网络 IO 消耗严重的解决方法
主要原因是同时需要发送或接收的包太多,常用的调优方法为进行限流,限制发送 packet 的频率,从而在网络 IO 可接受的情况下来发送 packet.
对于内存消耗严重的情况
JVM Heap 内存消耗严重时,常用的程序调优方法有:
1. 释放不必要的引用
内存消耗严重的情况中最典型的一种现象是代码中持有了不需要的对象引用,造成对象无法被 GC ,最典型的一个例子是在利用线程的情况下使用 ThreadLocal, 由于线程复用, ThreadLocal 中存放的对象如未做主动释放的话则不会被 GC 。
- 使用对象缓存池
- 采用合理的缓存失效算法
- 合理使用 SoftReference 和 WeakReference
对于占据内存但又不是必须存在的对象,例如缓存对象,也可以基于 SoftReference 和 WeakReference 的方式来进行缓存,前者会在内存不够用的时候回收,后者会在 Full GC 的时候回收。
5.2.3 对于资源消耗不多,但程序执行慢的情况
对于分布式 JAVA 应用而言,造成这种情况的主要原因通常有锁竞争激烈及未充分利用硬件资源两种情况
锁竞争激烈
1. 使用并发包的类
并发包中的类多数都采用了 lock-free 、 nonblocking 算法,减少了多线程情况下资源的锁竞争。如并发包中的类无法满足需求时,可参考学习一些 nonblocking 算法来自行实现。 Nonblocking 算法的机制,为基于 CAS 来做到无需 lock 就可实现资源一致性的保证,主要的实现 nonblocking 算法有:
2. 使用 Treiber 算法
Treiber 算法主要用于实现 Stack ,基于 Ttrber 算法实现的无阻塞的 Stack 代码如下:
public class ConcurrentStack<E> { AtomicReference<Node<E>> head = new AtomicReference<Node<E>>();
public void push(E item) { Node<E> newHead = new Node<E>(item); Node<E> oldHead; do { oldHead = head.get(); newHead.next = oldHead; } while (!head.compareAndSet(oldHead, newHead)); }
public E pop() { Node<E> oldHead; Node<E> newHead; do { oldHead = head.get(); if (oldHead == null) return null; newHead = oldHead.next; } while (!head.compareAndSet(oldHead,newHead)); return oldHead.item; }
static class Node<E> { final E item; Node<E> next;
public Node(E item) { this.item = item; } } } |
以上代码摘自IBM DeveloperWorks 网站上的Java theory and practice 系统文章,由于Stack 是LIFO 方式,因此不能采取类似LinkedBlockingQueue 中两把锁的机制。这里巧妙地采用AtomicReference 来实现了无阻塞的push 和pop ,在push 时基于AtomicReference 的CAS 方法来比较目前的head 是否一致。如不一致,说明有其它线程改动了,如有改动则继续循环,直到一致,才修改head 元素,在pop 时采用同样的方式进行操作。
3. 使用 Michael-Scott 非阻塞队列算法
和Treiber 算法类似,也是基于CAS 以及AtomicReference 来实现队列的非阻塞操作,ConcurrentLinkedQueue 就是典型的基于Michael-Scott 实现的非阻塞队列。
从上面两种算法来看,基于CAS 和AtomicReference 来实现无阻塞算法是不错的选择。但值得注意的是,由于CAS 是基于不断的循环比较来保证资源一致性的,对于冲突较多的应用场景而言,CAS 会带来更高的CPU 消耗,因此不一定采用CAS 实现无阻塞的就一定比采用Lock 方式的性能好。业界还有一些无阻塞算法的改进,如MCAS 、WSTM20 等
4. 尽可能少用锁
尽可能让锁仅在需要的地方出现,通常没必要对整个方法加锁,而只对需要控制的资源做加锁操作。尽可能让锁最小化,例如一个操作中需要保护的资源只有HashMap, 那么在加锁时则可只synchronized(map) ,而没必要synchronized(this).
5. 拆分锁
把独占锁拆分为多把锁,常见的有读写锁拆分及类似ConcurrentHashMap 中默认拆分为16 把锁的方法。需要注意的是采用拆分锁后,全局性质的操作会变得比较复杂,例如ConcurrentHashMap 的size 操作。
6. 去除读写操作的互斥锁
在修改时加锁,并复制对象进行修改,修改完后切换对象的引用,而读取操作时则不加锁,这种方式称为CopyOnWrite 。CopyOnWriteArrayList 就是其中的典型实现。这种做法好处是可以明显提升读的性能,适用读多写少的场合,坏处是造成更多的内存消耗。
未充分使用硬件资源
1. 未充分使用CPU
对于 JAVA 应用而言,通常原因就是在能并行处理的场景中未使用足够的线程。
另外,单线程的计算,也可以拆分为多线程来分别计算,最后合并结果, JDK7 中的 fork-join 框架可以给以上场景提供一个好的支撑方法
2. 未充分使用内存
如数据的缓存、耗时资源的缓存(如数据库连接,网络连接)、页面片段的缓存等。
对于数据量大造成的性能不足,在第 7 章“构建可伸缩的系统”中提供了一些优化方案。从纯粹的软件调优角度来讲,充分而不过分使用硬件资源,合理调整 JVM 以及合理使用 JDK 包是调优的三大有效原则,调优没有银弹,结合系统现状和多尝试不同的调优策略是找到合适的调优方法的唯一途径。
发表评论
-
技术文章精华合集(持续更新中)
2016-09-20 19:09 789Kafka深度解析 分库分表系列文章 来自 ... -
<<More Joel on Software>> 如何扮演程序经理的角色
2011-07-03 15:13 829制作伟大软体的秘方之 ... -
<<More Joel on Software>> 利诱管理法
2011-07-03 15:10 1003利诱管理法 Joke: A poor J ... -
<<More Joel on Software>> The Joel Test: 软件开发成功 12 法则
2011-07-03 14:42 730作者: 周思博 (Joel Spols ... -
<<More Joel on Software>> 看起来简单, 实际上复杂
2011-07-03 14:41 791作者: 周思博 (Joel Spolsky) 译: Bo Y ... -
<<More Joel on Software>> 膨胀软件与80/20的谣传
2011-07-03 14:38 916作者: 周思博 (Joel Spols ... -
<<More Joel on Software>> 每日构建(daily build)是你的朋友
2011-07-03 14:37 895作者: 周思博 (Joel Spols ... -
<<More Joel on Software>> 五个为什么
2011-07-03 14:20 982五个为什么(译文) ... -
<<More Joel on Software>> 飙高音
2011-07-03 13:54 945飙高音(译文) 作者: 阮一峰 日期: 2009年 ... -
<<More Joel on Software>> 关于战略问题的通信之六
2011-07-03 13:51 799关于战略问题的通信之六(译文) 作者: 阮一峰 日 ... -
<<More Joel on Software>> 易用性是不够的
2011-07-03 13:41 776易用性是不够的(译文) 作者: 阮一峰 日期: 2 ... -
<<More Joel on Software>> 军事化管理法
2011-07-03 13:28 1014高科技公司能否采用军事化管理?(译文) 作者: 阮一峰 ... -
<<More Joel on Software>> 寻找优秀的程序员
2011-07-03 13:24 1235=================== 寻找 ... -
读书笔记:《分布式JAVA应用 基础与实践》 第七章 构建可伸缩的系统
2011-05-29 12:09 1142通常将通过升级或增加单机机器的硬件来支撑访问量及数据量增长的方 ... -
读书笔记:《分布式JAVA应用 基础与实践》 第六章 构建高可用的系统
2011-05-28 11:02 1241对于互联网或企业中的大型应用而言,多数要求做到 7*24 ... -
读书笔记:《分布式JAVA应用 基础与实践》 第五章 性能调优(一)
2011-05-22 13:43 11625.1 寻找性能瓶颈 通常性能瓶颈的 ... -
读书笔记:《分布式JAVA应用 基础与实践》 第四章 分布式JAVA应用与JDK类库
2011-05-21 11:23 12114.1 集合包 ArrayList, Li ... -
读书笔记:《分布式JAVA应用 基础与实践》 第三章 3.3 JVM线程资源同步及交互机制(二)
2011-05-17 21:52 963接下来网上没有,貌似 ... -
《分布式JAVA应用 基础与实践》 第三章 3.3 JVM线程资源同步及交互机制(一)
2011-05-17 19:09 9203.3 JVM线程资源同步及交互机制 Java程序采用 ... -
《分布式JAVA应用 基础与实践》 第三章 3.2 JVM内存管理(四)
2011-05-17 19:00 9543.2.4 JVM内存状况查看 ...
相关推荐
很抱歉,由于无法直接访问或解析链接内容,我无法提供具体的《乔杉架构笔记 Java工程师面试突击》中的详细知识点。但作为一个专业的IT行业大师,我可以根据通常Java工程师面试中的常见主题,分享一些重要的Java编程...
第五,JVM性能优化是Java开发者必须掌握的技能。笔记可能包括JVM的运行模式、类加载机制、方法区、类的生命周期,以及如何通过JProfiler、VisualVM等工具进行性能监控和调优。 第六,笔记可能还涉及到了Java网络...
《Go学习笔记第四版》是一本深度探讨Go语言的书籍,专为想要深入理解Go语言特性和实战应用的读者设计。Go语言,又称Golang,是Google在2009年发布的一种静态类型、编译型、并发型且具有垃圾回收功能的编程语言。其...
在准备AAA2024考试的过程中,考生应当通过这些资料深入了解Java在系统架构设计中的应用,如Java平台的优势、Java EE相关技术在构建企业级应用中的角色、以及Java在分布式系统中的最佳实践等。同时,掌握如何评估和...
5. **第5课Mycat跨分片问题**:探讨Mycat如何处理跨分片聚合查询、跨分片JOIN操作以及分布式事务管理等问题。 6. **第6课Mycat性能测试与调优**:介绍Mycat性能相关的参数设置及调优方法,以及如何进行性能测试。 7....
本笔记主要面向初学者,旨在帮助他们快速掌握Jmeter的基础知识和使用技巧。 一、Jmeter简介 Jmeter是由Apache软件基金会开发的一款开源性能测试工具,支持多种协议,如HTTP、HTTPS、FTP等,可用于测试静态和动态...
11. **监控与调优工具**:使用MySQL自带的慢查询日志、Performance Schema或第三方工具如Percona Toolkit,监控数据库性能,找出瓶颈并进行调优。 12. **读写分离**:对于高并发读写场景,可采用主从复制实现读写...
以上只是Java编程中的一部分知识点,实际的学习过程可能涉及更多高级主题,如设计模式、并发编程、JVM优化、性能调优、分布式系统等。对于初学者来说,理解和掌握这些基础知识是构建强大Java技能的第一步。
5. **Hadoop集群管理**:书中还涵盖了集群的部署、配置、监控和维护,包括硬件选择、网络规划、安全性设置,以及性能调优技巧。 6. **数据处理与分析**:书中探讨了如何利用Hadoop进行数据预处理、清洗、转换和分析...
│ 淘淘商城第五天笔记.docx │ ├─06.第六天 │ 01.内容复习.avi │ 02.课程计划.avi │ 03.cms系统分析.avi │ 04.内容分类列表展示.avi │ 05.添加节点、及课后作业.avi │ 06.新增内容-分析.avi │ 07.cms系统...
1. 数据库概念与设计:这部分可能涵盖关系数据库模型、ER图、范式理论(第一范式到第五范式)以及数据库设计原则,如最小冗余和正常化。 2. SQL语言:SQL是用于操作和查询数据库的标准语言。考生可能需要熟悉DML...
2. **Java EE(企业版)**:提供了一套用于构建企业级分布式应用的框架和服务,如Servlet、JSP、EJB等。 3. **Java ME(微型版)**:适用于嵌入式设备和移动设备,如早期的Java手机应用。 4. **Spring框架**:在企业...
关系模型是最常见的,以表格形式存储数据,遵循第一范式(1NF)到第五范式(5NF)的规范化原则。 3. **SQL语言**:SQL(结构化查询语言)是用于管理和操作数据库的标准语言。掌握SELECT语句用于查询数据,INSERT、...