Java的性能优化实在是一个很大的话题,这里只打算讨论一些日常编码中可以很容易做到,并且可以习惯化的一些小技巧。这些技巧的道理可能大家都懂,在一些特定的场景(比如面试的时候)也知道怎么去做(说),但日常编码时能做到习以为常,自然而然就这样处理的人,并不多。
1、Java技巧
1.1、定义公共变量,减少对象创建
当对象实例可以通过工厂类很容易获得的时候,则容易出现这个问题,每次都创建一个实例。优化前:优化后:... LogFactory.getLog(this.class).info(msg1); ... LogFactory.getLog(this.class).info(msg2); ...
Log log = LogFactory.getLog(this.class); ... log.info(msg1); ... log.info(msg2); ...
1.2、使用StringBuilder进行字符串的拼凑
字符串相加的时候,特别是内容比较多的情况,一定记得用StringBuilder,这样既可以减少对象的创建,也有利于垃圾回收。
1.3、在需要的时候才使用线程安全的类
在不需要线程安全的时候,尽量避免使用Vector、Hashtable等线程安全的类,而是用ArrayList、HashMap等来替换。synchronized的使用也要注意,只用在需要的方法或代码块上,不要滥用,甚至加到整个类上去。
1.4、善用final修饰符提高性能
final修饰变量或者修饰类,可以让编译器进行一些优化,甚至进行内联(inline),大大提高代码性能。
1.5、对循环进行优化
在大循环或循环套循环的地方,往往是代码优化的最佳着手点。可以结合前面讲的几点进行优化,减少对象的创建、使用StringBuilder、避免使用线程安全的类等,可以显著提高性能。
优化前:
ResultSet rs = ...; String names = ""; while (rs.next()) { names += rs.getString("userName") + ","; LogFactory.getLog(Hello.class).info(rs.getString("userName")); ... }
优化后:ResultSet rs = ...; Log log = LogFactory.getLog(Hello.class); StringBuilder names = new StringBuilder(); while (rs.next()) { String name = rs.getString("userName"); names.append(name); names.append(","); log.info(name);
}...
1.6、慎用System.out.println
不管是往控制台(console)还是文件中输出信息,都是比较耗资源的,调试时的信息输出代码,在正式发布的时候都应该去掉。即使是用Log来做信息输出,可以通过信息的级别来减少信息的输出,但任然要消耗一些计算。不过用Log来代替System.out.println还算是一个best practice,你如果曾经在生产系统的Tomcat窗口上看见过满屏飞的调试输出,或者试图在几个G的日志文件中去找一个异常的信息,就明白我的意思了......
2、JSP技巧
2.1、尽量少写JAVA代码
业务逻辑不要往JSP里放,很难维护,比较理想的情况是JSP中只有<%=...%>。很多人(特别是老鸟们)可能会喜欢利用JSP的动态脚本特性来进行代码的开发测试,这样可以使代码立即生效而不用重启容器(Tomcat等),特别是在生产环境上做应急处理的时候,可能会常用这招,但在下一个版本里这些代码都应该移到java bean去。
2.2、css、js使用独立的文件
这个技巧也适合html,使用独立的文件保存css、js,可以充分利用浏览器的缓存机制,浏览器在下一次访问这个jsp的时候,可以只去取有更新的资源,从而减少网络流量;jsp文件的缩小,也可以使Response的资源消耗变少,从而提高应用服务器的性能;在设计良好时,考虑到代码的共享,也需要css、js文件独立;当然了,独立的js文件,在eclipse这样的IDE中,也有比较好的编辑器可用,提高编码效率;还有一个额外的好处,在对服务器进行优化的时候,可以用一个专门的HTTP服务器(比如apache)来提供css、js、图片等静态内容,分担应用服务器的压力。
2.3、不需要session时就禁用
<%page session="false"%>
2.4、避免在session中存太多对象或者存放比较大的对象
如果说无session的JSP是轻装上阵的话,在session里存很多对象或大对象就是铠甲勇士了,很容易在用户量大的时候拖垮服务器。
3、数据库技巧
3.1、使用连接池
连接池不仅解决了数据库建立连接很耗资源以及数据库的连接数有限等问题,也借助JNDI技术和commons-pool或C3P0等优秀的连接池实现给我们的应用带来了一个好的结构,数据库账号信息的调整、连接数的调整、连接的异常恢复、甚至事务处理等操作都可以与具体的业务代码分离开来,方便了部署、维护和性能调优。
3.2、记得释放资源
如果不使用jdbcTemplate、Hibernate等框架而直接使用JDBC访问数据库,则一定记得要释放资源,比较好的习惯是先写资源释放代码再去实现业务逻辑。
Connection conn = getPoolConnection(...); try { ... } finally { conn.close(); }
(见过很多资源释放的代码,比如:if (conn!=null) try {conn.close; ...,所以,呃,尽量用jdbcTemplate这样的模板吧)3.3、使用JDBC时尽量避免SQL重新编译
尽量用PreparedStatement代替Statement,前者会进行预编译,后续执行时只是参数不同而已,Statement则是RAW SQL,执行时都进行编译。特别是循环中执行的SQL,基本上都是可以在循环外创建PreparedStatement,然后在循环中传递参数去执行。另外,鉴于不同数据库的兼容总是那么不尽如人意,所以从性能考虑,使用存储过程也不失为一个好主意。
String[] names = {"张三", "李四", ...}; for (int i=0; i<names.length; i++) { Statement stat = conn.createStatement(); try { String sql = "select age, sex, mobile,email from student where name='" + names[i] + "'"; ResultSet rs = stat.executeQuery(sql); ... rs.close(); } finally { stat.close(); } }
优化:String[] names = {"张三", "李四", ...}; String sql = "select age, sex, mobile,email from student where name=?"; PreparedStatement stat = conn.prepareStatement(sql); try { for (int i=0; i<names.length; i++) { stat.setString(1, names[i]); ResultSet rs = stat.executeQuery(); ... rs.close(); } } finally { stat.close(); }
注:String sql外提减少了String的创建,PreparedStatement的使用减少了数据库的SQL编译
3.4 优化SQL
比较常见的SQL优化技巧是建索引,减少关联,分表,分区,使用存储过程等
4、其它
4.1、不要滥用XML
XML就尽量用在数据交换、配置文件这些方面吧,其它能用对象就用对象,能用JSON就JSON吧
4.2、使用缓存
缓存对象或SQL查询结果,都可以比较好提高响应速度,但会增大内存占用,也就是以空间换时间。自己用HashMap来实现简单的缓存或者使用比较成熟的缓存框架都可以。(这似乎是架构方面的东西,有点脱离本文讨论的范围了^_^)
(暂时就这么多吧...)