【Java精品源码栏目提醒】:网学会员为需要Java精品源码的朋友们搜集整理了JAVA编程性能规则(1.1) - 其它资料相关资料,希望对各位网友有所帮助!
24. Feb 2010 Security Level: 机密
JAVA编程性能规则 软件公司设计管理部 李臻峰 www.huawei.comHUAWEI TECHNOLOGIES Co. Ltd. HUAWEI Confidential引言 楼倒倒、楼歪歪是09年楼市的重要话题。
出现这些问题,大家都知道是设计考虑不周造成的。
没有好的设计,绝不会有好的楼房质量。
然而仅有好的设计,就能一定能产生好的质量吗?杭州的楼垮垮,云南的桥倒塌,据可靠调查,是施工材料不合格和施工质量不过关。
仅
设计过关还不行,还必须保证施工材料和施工质量。
软件也一样,优秀的架构、优秀的软件设计是产生高质量
软件的前提,然后没有过硬的编程实现,一样得不到预期效果。
事实上,我们不少的软件版本,其需求复杂度并不高,复用的是现成的架构,也不存在有难度技术问题,可是质量、尤其是性能常常不过关,究其原因,
问题往往就出在编码上。
千里之堤,毁于蚁穴。
编码质量是软件性能的重要保障!HUAWEI TECHNOLOGIES Co. Ltd. HUAWEI Confidential Page 2培训目标 希望对
Java编码八个方面中常见性能规则进行讲解,让大家在编码设计 和编程实现中自觉地选用高性能的方法,使系统性能达到最优化,避免 虽然系统设计优良,却因为编码不当导致性能下降。
本胶片虽然以
Java编码为题材,但其思想仍然适用于其它编程语言,因 此也期望能对其它语言编程产生借鉴作用。
HUAWEI TECHNOLOGIES Co. Ltd. HUAWEI Confidential Page 3目录 字符串操作 IO操作 对象操作 垃圾回收 比较与排序操作 并发控制 容器类使用 XML
文档操作 WEB/Servlet 其它 HUAWEI TECHNOLOGIES Co. Ltd. HUAWEI Confidential Page 4字符串操作 1、“StringBuffer” or “StringBuilder” or “” StringBuffer:有线程安全要求下使用 public synchronized StringBuffer appendString str 线程1 线程2 super.appendstr return this StringBuffer StringBuilder:非线程安全情况下使用 public StringBuilder appendString str 线程 super.appendstr return this StringBuilder :静态字符串拼接时使用 编译 String str “Welcome” “ to ” “Huawei” String str “Welcome to Huawei”HUAWEI TECHNOLOGIES Co. Ltd. HUAWEI Confidential Page 5字符串操作 / param srcStr: 源字符串2、String的ReplaceAll方法性能问题 param anyStr:需替换字符字串ReplaceAll利用的是正则表达式的匹配处 param replaceStr:用于替换的字符串 return 字符串 /理方式,我们的使用很多情况下只是简单的 public static String replaceAllString srcStr String anyStr String replaceStr字符串替换。
JDK6.0实行如下 int len anyStr.lengthpublic String replaceAllString regex String replacement // 创建中间结果存放容器,并确保容器尺寸足够大 StringBuilder strBuild new StringBuildersrcStr.length ltlt 1 returnPattern.compileregex.matcherthis int fromIdx 0 int idx srcStr.indexOfanyStr fromIdx.replaceAllreplacement while idx gt 0 strBuild.appendsrcStr.substringfromIdx idx strBuild.appendreplaceStr fromIdx idx len可以使用重现实现ReplaceALL方法,顺序 // 从之前扫描后的位置继续扫描 idx srcStr.indexOfanyStr fromIdx扫描源字符串,替换匹配的字符子串,并用 if fromIdx lt srcStr.lengthStringBuilder作为中间结果存放容器,最后输 strBuild.appendsrcStr.substringfromIdx出目标字符串。
return strBuild.toString HUAWEI TECHNOLOGIES Co. Ltd. HUAWEI Confidential Page 6字符串操作 3、不要通过new方式来创建常量字符串 可以直接使用赋值方式为变量设置字符串值,赋值指向的字符串和源字 符串为同一字符串,使用“”比较结果为true; 指向同一个字符串的多个变量,若产生变化,JVM自动创建新的字符 串,其它变量的值不受影响; new方法创建字符串对象,会创建一个源字符串的拷贝,使用“”比较 结果为false; str1 例如: “abc” String str1 “abc” str2 “abc” String str2 “abc” str3 String str3 str1 “abc” String str4 new String“abc” str4
Java 堆 String Str5 new Stringstr1 str5
Java 栈变量HUAWEI TECHNOLOGIES Co. Ltd. HUAWEI Confidential Page 7字符串操作 4、为StringBuilder/StringBuffer设置初始容量 StringBuilder/StringBuffer的初始容器(字符数组)长度是16,在操作过程中超过此长度, JVM将重新分配容器,长度为原先容器长度的2倍,并释放原先容器,依此进行下去。
【例如】 JDK1.6的StringBuffer相关
源码: public StringBuffer AbstractStringBuilder super16 AbstractStringBuilderint capacity StringBuffer value new charcapacity 注意:使用String/CharSequence对象来构建StringBuffer/StringBuilder对象时,初始长度为 “对象字符长度16”。
【例如】 JDK1.6的StringBuffer相关
源码: public StringBuilderCharSequence seq thisseq.length 16 appendseq HUAWEI TECHNOLOGIES Co. Ltd. HUAWEI Confidential Page 8字符串操作 5、子串匹配查找 indexOfString str方法运行速度最快,效率最高,但不支持正则表达式。
matchesString regex方法性能最差,但支持正则表达式,使用起来简单,当进行一次正则表达式查找时可以使 用。
【例如】JDK
源码实现 类String: public boolean matchesString regex return Pattern.matchesregex this 类Pattern: public static boolean matchesString regex CharSequence input Pattern p Pattern.compileregex Matcher m p.matcherinput return m.matches 可见:性能差的原因是每一次调用,都要创建新的Pattern和Matcher对象,并重新编译正则表达式。
直接使用正则表达式工具类(包括Pattern类、Matcher类)来实现匹配查找,在频繁操作下性能比matchesString regex方法要好很多。
【示例】 Pattern p Pattern.compilequotabquot Matcher m p.matcherquotaaaaabquot boolean b m.matchesHUAWEI TECHNOLOGIES Co. Ltd. HUAWEI Confidential Page 9 测试10000次发现,字符串操作 StringTokenizer比split方法性能高 36倍,但是JDK6.0并不鼓励使 用它,保留它是出于兼容性考 虑,估计是出于简洁性。
6、字符串分割 使用splitString regex方法,由于使用了正则表达式,性能较差,然而功能 最强。
它可以用单个字符或子串进行分割。
【举例】 String result quotthis is a testquot.splitquotsquot 使用StringTokenizer对象分割字符串,性能最强,功能也就相对较弱。
它只 能使用字符作为分割。
【举例】 StringTokenizer st new StringTokenizerquotthis is a testquot String resultStr new Stringst.countTokens int i 0 while st.hasMoreTokens resultStri st.nextToken HUAWEI TECHNOLOGIES Co. Ltd. HUAWEI Confidential Page 10IO操作 1、Socket通信 Src.rar 在Socket连接数不多,且存在持续的数据通信时,可以为每个Socket 开辟一个线程进行收发数据处理。
当Socket连接数很多,成百上千个,而每个连接的
通信又不是很频繁 是,使用异步IO的Selector事件反应模式将是较好的选择。
该模式使用 一个线程来处理Selector上监听的所有连接消息。
C1 C1 T1 Selector C2 T2 C2 C3 T3 C3 多线程模式 Reactor模式HUAWEI TECHNOLOGIES Co. Ltd. HUAWEI Confidential Page 11IO操作1、Socket通信 JDK1.5 Update9以后支持epoll功能 使用epoll性能将明显优于Selector,特别在连接数大的情况下。
与 Select相比,在事件发生时,它不用去轮询每个连接,而是直接回调 其处理方法,可以做到处理更快捷,性能也与连接数无关。
在Tomcat6中,据其
文档介绍也已经支持epoll功能 说明:据了解,JDK1.5 update9及Tomcat6对epoll的支持,只需要 在启动参数中作适当的配置,不涉及代码的修改,
Java应用程序的 写法仍然是Reactor模式。
但没有具体研究过。
HUAWEI TECHNOLOGIES Co. Ltd. HUAWEI Confidential Page 12IO操作 2、使用缓冲机制进行读写 因为缓冲读写一次可以完成大量数据的读写操作,最大值为一个缓冲区大小,因此相对字节读写、字符、整数、浮点数、复合 数据的读写,其IO次数可以明显减少,从而提升效率。
【举例】 public static void mainString args throws IOException FileInputStream fin new FileInputStreamquotd:/test.rarquot FileOutputStream fout new FileOutputStreamquotd:/e/test.rarquot BufferedInputStream bufferInput new BufferedInputStreamfin BufferedOutputStream bufferOutput new BufferedOutputStreamfout int c -1 try while c bufferInput.read -1 bufferOutput.writec finally bufferInput.close bufferOutput.close HUAWEI TECHNOLOGIES Co. Ltd. HUAWEI Confidential Page 13IO操作 3、使用将文件或Socket等的关闭操作放在finally
程序块 文件、Socket、channel使用完毕,必须牢记close操作; Close操作必须放在finally程序块; 强烈建议Open操作和close操作放在同一个函数中。
【举例】 private void doCountString fileName try BufferedReader reader new BufferedReadernew FileReaderfileName try String line null while line reader.readLine null line line.trim if line.length gt 0 processLineline finally reader.close catch Exception ex ex.printStackTrace HUAWEI TECHNOLOGIES Co. Ltd. HUAWEI Confidential Page 14对象操作 1、动态对象应尽量按需创建,避免提前创建而不使用 对象的创建需要消耗CPU指令,也会消耗
系统内存,因此只有在对象会被使用的 情况下才去创建,否则应该避免创建。
private String doCountString param StringBuilder strBuilder new StringBuilder256 if 0 param.length return null else if 1 param.length return param0 else forint i 0 i lt param.length i strBuilder.appendparami return strBuilder.toString HUAWEI TECHNOLOGIES Co. Ltd. HUAWEI Confidential Page 15对象操作 2、对象的创建应尽量在循环外创建,在循环内复用 在循环外创建对象,在循环内使用对象,可以减少对象的创建数量。
【举例】 private String doCountString param String rst new Stringparam.length StringBuilder strBuilder new StringBuilder256 forint i 0 i lt param.length i strBuilder.setLength0 forint j 0 j lt parami.length j strBuilder.appendparamij rsti strBuilder.toString return rst HUAWEI TECHNOLOGIES Co. Ltd. HUAWEI Confidential Page 16对象操作 3、对于高频度使用的对象,应考虑建立对象池 对象的创建和释放会消耗系统CPU,减少对象的创建和释放有利于 提升性能。
对于高频度使用的对象,为其建立对象池,在需要对象时 从池中提取,对象使用完成后再归还对象池中,将可以避免对象的频 繁创建和释放操作,从而大大提升系统性能。
对象池被多线程共享时,对对象池操作需要加锁。
如果出现加锁冲 突较大的情况,建议为每个线程设立自己的对象池,以避免加锁操 作。
对象池 对象池 对象池 Thread1 Thread2 Thread1 Thread2HUAWEI TECHNOLOGIES Co. Ltd. HUAWEI Confidential Page 17对象操作 4、对象序列化 对象序列化目的 1、使得对象可以在链路上传送 2、使得对象可以持久化,并在需要的时候得以恢复 流 Obj 序列化 反序列化 Obj 文件 说明:序列化、反序列化操作需要CPU开销、字节开销,或链路传输 开销,所以进行序列化时只对需要传输的数据序列化。
HUAWEI TECHNOLOGIES Co. Ltd. HUAWEI Confidential Page 18对象操作 4、对象序列化 实现Serializable接口 1、默认方式下,序列化操作(ObjectOutputStream )仅对对象的非transient变量进行序列化,而不会序列化transient变量,也不会 序列化静态变量。
因此,可以将不需要序列化的数据设置成transient变量或静态变量。
同理ObjectInputStream不对transient变量反序 列化。
2、可以通过实现接口的writeObject和readObject方法,实现对象序列化操作的定制化操作,减少不必要的数据传输。
说明:如果对象实现了writeObject方法,那么序列化操作就会执行这一方法序列化,否则按默认方式序列化。
但是也可以在 writeObjectt方法中,调用defaultWriteObject方法,使得对象先执行默认的序列化操作。
同理反序列化时,在writeObjectt方法中 调用defaultReadObject方法,执行默认的反序列化操作。
说明:writeObject和 public class Customer implements Serializable ReadObject方法中,写 入和读出对象的顺序必 private String name “帅哥quot 须一致,default方法的 private int age 20 调用位置也必须对应。
private void writeObjectObjectOutputStream out throws IOException out.writeage // out.defaultWriteObject private void readObjectObjectInputStream in throws IOException age intin.read // in.defaultReadObject 本示例中,name不被序 列化,因此反序列化不被 读出,其值是null。
HUAWEI TECHNOLOGIES Co. Ltd. HUAWEI Confidential Page 19对象操作 4、对象序列化 实现Externalizable接口 Externalizable接口继承自Serializable接口,如果一个类实现了Externalizable接口,那么将完全由这个类控制自身的序列 化行为。
Externalizable接口声明了writeExternal和readExternal两个方法,前者负责序列化操作,后者负责反序列化操作。
在对实现了Externalizable接口的对象反序列化时,会先调用类的不带参数的构造方法,这是有别于默认反序列方式的。
如果把类的不带参数的构造方法删除,或者把该构造方法.