Run Faster-JAVA
又好久没有写点啥了,平时都忙于工作,忙于应付工作中的问题,各种吸收却并没有好好的消化,该是"反刍"一下的时候了。
本篇名叫"Run
Faster,JAVA",其实JAVA发展到现阶段,无论是编译器优化还是运行时优化,都做的很好了,速度早已不像过去那样被人诟病,本篇只是自己日常工作的一些总结,很不全面,但是都是很实际且能立竿见影的,可以当低层次技术文章看看,当然最好能在实际的开发中尝试尝试。(PS:为照顾大家看技术文章容易犯怵的情绪,采用快餐式的讲解,不深入,不解释,只求精神上的共鸣,不喜欢技术的就当“杀马特"随便瞄一眼吧。。。)
常见的JAVA应用下容易出现瓶颈的因素:磁盘I/O,网络操作,CPU,数据库读写,应用单多线程,多线程锁竞争,JAVA GC等。针对这几个因素,一般的调优层次是: 设计调优--->代码调优--->JVM参数调优--->数据库调优--->OS调优,当实际应用出现瓶颈需要调优时,一般的步骤是:代码--->JVM参数--->数据库--->OS--->应用架构(设计),可以看到,首先需要调优的就是代码,那么我们今天先讲讲代码调优的一些口诀:
1:多用设计模式(主要是单例,享元,观察者,访问者,装饰者,代理等),如果你写了两年以上代码,还没用过常见的模式,我只能说,赶紧洗洗换行当吧。学习设计模式我推荐<<Head First 设计模式>>一书,轻轻松松看下来,找几个例子熟悉,关键是平常开发中有意识的培养自己去实现,每个模式写一两个例子就都了然于胸了。至于外界盛赞的GOF<<设计模式>>,建议当常备参考书放到书桌旁有需要时参考就行了。
2:Buffer/Cache,缓冲/缓存思想,这个应该很容易理解。Buffer是说take it easy,slow down.而Cache则是说take it ,you'll
user it someday.
3:多线程并行代替串行(要求一般串行间没有因果或前后关系),说白了就是用多线程替代单线程,充分利用CPU资源,当然很多在单线程下能正常执行的程序在多线程下可能会出现问题,用锁固然可以减少线程冲突,但是锁太多反正抑制了多线程的威力,而且容易出现锁等待,死锁等问题。尽管如此,多线程替代单线程可以大幅提高效率,尽量使用。
4:应用负载均衡(JVM级,需要处理好共享数据的一致性问题),如果应用将来做负载,那么在编写代码的时候就要考虑共享数据的一致性,共享缓存听起来是一个很诱人的东西,可是真正实现的时候会有很大的麻烦,别说你已经很熟悉MappedByteBuffer +
RandomAccessFile,too simple。
5:时间/空间的转换,缓存是这种转换的一种实现,当然还有生产者-消费者模式,如果你来不及计算所有的任务,花点内存先保存这些任务,然后启用线程慢慢计算。还有在循环中存储中间临时结果以备后续调用也算。
6:使用ValueObject减少计算或请求次数,将请求封装到一起,尽量一次请求就把需要的参数给全,得到所有想要的结果。多次请求总是会耗损资源的,无论是HTTP,TCP还是普通方法调用请求。
口诀说完了接下来就是招式了,优化的最高境界当然是无招胜有招,不过还得从简单的招式练起,这次先学下面几招入门级的:
0):不要在循环体里定义引用并创建对象,将定义引用放到外面
Object
obj = null;
for (int i = 0; i < 10000; ++i) {
(Object) obj = new Object();
System.out.println("obj= "+ obj);
}
1):避免重复初始化对象
public class A {
private Hashtable table = new Hashtable
(); //这个问题一般开发人员都会碰到吧
public A() {
table = new Hashtable(); // 将Hashtable对象table初始化了两次
}
}
2):字符串优化的几个地方
a:substring()方法速度很快,但是容易引起内存溢出问题,因为会保留对原串的引用,所以建议用new String(***.substring(参数))的模式
b:对字符串截取可用split,split内部调用的Pattern.complie.split,因此如果拆分规则比较复杂,每次Pattern.complie的时候会有损耗,可以考虑将Pattern.complie静态化然后直接调用split,这样只需要complie一次即可。当然还想速度快点的可以考虑StringTokenizer(sun不建议使用),想最快的可用indexOf+substring自己实现截取
c:对字符串的开头/结尾子串判断一般用startsWith/endsWith,由于他们和split一样是基于正则的,会有上面说过的问题。所以最好用charAt来自己实现更快
d:对字符串的相加,StringBuild最优,StringBuffer次之(线程安全),String.concat再次之,+/+=最次(此处不考虑静态常量编译器的优化)
3):ArrayList(Vector)与LinkedList比较
a:ArrayList基于数组(一块连续的内存空间),LinkedList基于双向链表,因此ArrayList的主要性能耗在扩展空间时数据复制,而LinkedList耗在数据遍历(尤其是找中间的数据)
b:头尾的新增/删除对LinkedList没有压力,但是每次随机访问需要从头查找,会要亲命
c:在尾部添加对ArrayList没有压力,但从头部删除都会数组复制,会要亲命。
d:对LinkedList的遍历,要么用for-Each,要么Iterator,不要用普通的ini
i=0;i<size;i++,因为get(i)是随机查找,每次都从头或尾遍历,会要亲命
e:一般实现了RandomAccess接口的类如ArrayList,Vector才可以在遍历时放心使用get,而且遍历时要尽量减少重复的获取size或元素,用临时变量缓存之
4):HashMap如果key是一个个重写了hashCode方法的对象,如果此hashCode方法放回相同的int值,则每次放入不同对象会到同一个桶下,同一桶下是用链表组织数据的,如果查找需要遍历链表,会要亲命。
5):HashMap底层用了数组实现,使用大于等于initialCapacity并且是2的指数次幂作为数组大小,threshold为当前数组总容量与负载因子的乘积,即阀值,当实际容量超阀值 时会进行数组扩展复制,而复制会消耗CPU资源,因此适当的initialCapacity有利于提高性能。另外别忘了HashMap在多线程下并发put会有导致CPU满负荷的bug。
6):LinkedHashMap基于元素进入顺序或被访问先后顺序(被访问的元素放到最后),而TreeMap基于传入的Comparetor参数或实现了Comparable的key对象进行排序,适用范围 不一样,前者可以用来实现LRU算法,后者在一致性Hash上大有作用。而HashSet,LinkedHashSet,TreeSet是对应Map的简单封装。
7):使用NIO(ByteBuffer+Channel)可以提升I/O读写速率,注意duplicate(),asReadOnlyBuffer(),slice()都是共享原始缓存数据的,一般用文件内存映射MappedByteBuffer,结构 化散射接口ScatteringByteChannel,结构化聚集接口GatheringByteChannel将多个ByteBuffer组成一个数组一次写入,用DirectBuffer代替ByteBuffer进行多次读写都可以提高效 率,但DirectBuffer是直接开辟OS内存,如果频繁新建回收,性能反而不如ByteBuffer虚拟机内的相同操作.
8):4个引用级别:强,软,弱,虚,其作用分别不同,一般软和弱引用可以作为缓存的一种方案避免内存溢出,而虚更多用于跟踪对象回收时机,弱引用WeakHashMap可以 作为简单的缓存对象。
9):不在循环代码中使用try-catch,尽量放到循环体外,多用局部变量或临时变量存储计算值以避免重复计算,可以考虑在一次循环中做多个操作减少循环次数(i+1,i+2,i+3...),多 用"阻断性"逻辑运算如&&来代替位运算如&(当然一般也不建议利用条件判断来做一些操作,因为后面的判断默认应该可以不去运行的)
10):多用一维数组代替二维数组;使用Buffer进行I/O;对构造成本大的函数,用clone代替new(注意该类实现Cloneable,并且默认是浅复制,深复制需要自己重写Object的clone 方法)
12):技巧:用位运算代替2次幂的乘除;
13): 多用static静态方法而不是实例方法
14): 多用native方法如arrayCopy等;
to be continued…..
Run Faster-JAVA的更多相关文章
- SpringCloud异常(Euruka):Application run failed java.lang.NoSuchMethodError: org.springframework.boot.builder.SpringApplicationBuilder
在测试Euruka作为服务注册中心的时候碰到了这个问题,错误提示如下: "C:\Program Files\Java\jdk1.8.0_161\bin\java" -XX:Tier ...
- 解决Run As -> Java Application不能运行问题
转自:https://breakshell.iteye.com/blog/467130 点 Run As -> Java Application 不能运行,报的错误如下: Plug-in org ...
- 线上zk节点报org.apache.zookeeper.server.NIOServerCnxnFactory.run(NIOServerCnxnFactory.java:187) at java.lang.Thread.run(libgcj.so.10)
线上zk做配置管理,最近突然发现两个节点一直在刷下边 java.nio.channels.CancelledKeyException at gnu.java.nio.SelectionKeyIm ...
- spring boot: 热部署(一) run as – java application (spring-loader-1.2.4.RELEASE.jar)
spring boot: 热部署(一) run as – java application (spring-loader-1.2.4.RELEASE.jar) 如果使用的run as – java a ...
- win10+eclipse+hadoop2.7.2+maven+local模式直接通过Run as Java Application运行wordcount
一.准备工作 (1)Hadoop2.7.2 在linux部署完毕,成功启动dfs和yarn,通过jps查看,进程都存在 (2)安装maven 二.最终效果 在windows系统中,直接通过Run as ...
- 5 Ways to Make Your Hive Queries Run Faster
5 Ways to Make Your Hive Queries Run Faster Technique #1: Use Tez Hive can use the Apache Tez execu ...
- eclipse中的项目运行时不出现run as→java application选项
eclipse中的运行java project时不出现run as→java application选项? 解决方案☞必须有正确的主方法,即public static void main(String ...
- Eclipse中run as run on server和run as java application
一.run java application (作为Java应用程序运行)是运行 java main方法 run on server是启动一个web 应用服务器 二.两者的区别: Eclipse中 ...
- maven project中,在main方法上右键Run as Java Application时,提示错误:找不到或无法加载主类XXX.XXXX.XXX
新建了一个maven project项目,经过一大堆的修改操作之后,突然发现在main方法上右键运行时,竟然提示:错误:找不到或无法加载主类xxx.xxx.xxx可能原因1.eclipse出问题了,在 ...
- 通过crontab调度java -jar任务提示"nohup: failed to run command `java': No such file or directory"
通过crontab无法运行,提示如标题的信息: 但直接在终端控制台执行sh和java -jar都可以: 网上给的提示解决方法,在.sh文件开始上面加上 source /etc/profile 然后cr ...
随机推荐
- 调试bug心得
1. 忌主观主义.遇到问题,优先考虑客观排除法,按模块一一屏蔽测试排除.忌自己认为某个模块里的某个逻辑有问题,一直研究修改测试,被其他模块导致的偶然事件所干扰.通过排除法,找到震源,再解决.
- 原生JS的使用,包括jquery和原生JS获取节点、jquery和原生JS修改属性的比较
一.前言 相比于JS这条直达终点.满是荆棘的小路,jquery无疑是康庄大道了,足够的简洁.易用给了它辉煌的地位.然而,毕竟是绕着道的插件,当小路走着走着变成大路的时候,曾经的大路也就失去了他自身的优 ...
- PHP获取当前文件路径
__FILE__ 是当前路径+文件名dirname(__FILE__)返回当前文件路径的路径部分 例如当前文件是 /home/data/demo/demo.php ,则 __FILE__ 得到的就是完 ...
- CentOS安装配置Tomcat-7
安装环境:CentOS-6.5安装方式:源码安装软件:apache-tomcat-7.0.29.tar.gz下载地址:http://tomcat.apache.org/download-70.cgi ...
- scanf是怎么从标准输入读取数据的
scanf是从标准输入读取数据的 假设现在标准输入中的数据是123456 int a; 而我scanf("%d",&a); 会把123456转化为数字然后存入到a中. 如果 ...
- echo $[1 + 2] shell中 $[] 在bash中同$(()),用于算术计算
shell脚本编写:echo $[ 11#8+1] 输出结果是几,为什么,怎么算来的? 摘自:https://zhidao.baidu.com/question/334766451.html 结 ...
- real-Time Correlative Scan Matching
启发式算法(heuristic algorithm)是相对于最优化算法提出的.一个问题的最优算法求得该问题每个实例的最优解.启发式算法可以这样定义:一个基于直观或经验构造的算法,在可接受的花费(指计算 ...
- mysql 空间索引的使用
CREATE TABLE tb_geo(id INT PRIMARY KEY AUTO_INCREMENT,NAME VARCHAR(128) NOT NULL,pnt POINT NOT NULL, ...
- 云存储上传控件更新日志-Xproer.cloud2
官方网站:http://www.ncmem.com/ 产品首页:http://www.ncmem.com/webapp/cloud2/index.asp 在线演示:http://www.ncmem.c ...
- 基于Qt5 跨平台应用开发
1.Qt简介 2.Qt 编程关键技术 2.1 信号与槽 2.2 Qt事件处理 3.Qt开发与实例分析 3.1 开发环境 3.2 系统实现基本框架 3.3 数据库管理 3.5 对Excel进行操作 4. ...