性能

一般来说,性能通过以下几个方面来表现:

  • 执行速度
  • 内存分配
  • 启动时间
  • 负载承受能力

定量评测的性能指标:

  • 执行时间
  • CPU时间
  • 内存分配
  • 磁盘吞吐量
  • 网络吞吐量
  • 响应时间

调优的层面

  • 设计调优
  • 代码调优
  • JVM调优
  • 数据库调优
  • 操作系统调优

性能调优必须有明确的目标,不要为了调优而调优,如果当前程序并没有明显的性能问题,盲目地进行调整,其风险可能远远大于收益。

设计优化

1. 单例模式

对于系统的关键组件和被频繁使用的对象,使用单例模式可以有效地改善系统的性能

2. 代理模式

代理模式可以用来实现延迟加载,从而提升系统的性能和反应速度。

另外,可以考虑使用动态代理的方式 。 动态代理的方法有: JDK自带的动态代理, CGLIB, Javassist, 或ASM库。

3. 享元模式

好处:

1) 可以节省重复创建对象的开销

2) 对系统内存的需求减少

4. 装饰者模式

实现性能组件与功能组件的完美分离

5. 观察者模式

观察者模式可以用于事件监听、通知发布等场合。可以确保观察者在不使用轮询监控的情况下,及时收到相关的消息和事件。

6. Value Object 模式

将一个对象的各个属性进行封装,将封装后的对象在网络中传递,从而使系统拥有更好的交互模型,并且减少网络通信数据,从而提高系统性能。

7. 业务代理模式

将一组由远程方法调用构成的业务流程,封装在一个位于展示层的代理类中。

思考:

单例模式, 工厂模式和享元模式的差异?

常用优化组件和方法

1.缓冲

I/O 操作很容易成为性能瓶颈,所以,尽可能在 I/O 读写中加入缓冲组件,以提高系统的性能。

2. 缓存

缓存可以保存一些来之不易的数据或者计算结果,当需要再次使用这些数据时,可以从缓存中低成本地获取,而不需要再占用宝贵的系统资源。

Java缓存框架:

EHCache, OSCache,JBossCache

3. 对象复用 -- "池"

最熟悉的线程池和数据库连接池。

目前应用较为广泛的数据库连接池组件有C3P0 和Proxool.

4.并行替代串行

5. 负载均衡

跨JVM虚拟机,专门用于分布式缓存的框架--Terracotta, 使用Terracotta可以实现Tomcat的Session共享。

6. 时间换空间

7. 空间换时间

程序优化

1. 字符串优化处理

1)

     String str1 ="abc";String str2 ="abc";String str3 = new String("abc");System.out.println(str1==str2);  //trueSystem.out.println(str1==str3);  //falseSystem.out.println(str1==str3.intern()); //true

2) subString() 方法的内存泄漏

如果原字串很长,截取的字串却有比较短,使用以下方式返回:

new String(str1.substring(begin,end));

3) 字符串分割和查找

可以使用的方法:

split()方法   -- 最慢, 写法简单

StringTokenizer 方法  -- 较快,写法一般

indexOf()和subString() 方法 - 最快, 写法麻烦

package performance.program.string;import java.util.StringTokenizer;public class StringSplit {public static void splitMethod(String str) {long beginTime = System.currentTimeMillis();for (int i = 0; i < 10000; i++) {str.split(";");}long endTime = System.currentTimeMillis();System.out.println("splitMethod use " + (endTime - beginTime));}public static void tokenizerMethod(String str) {long beginTime = System.currentTimeMillis();StringTokenizer st = new StringTokenizer(str, ";");for (int i = 0; i < 10000; i++) {while (st.hasMoreTokens()) {st.nextToken();}st = new StringTokenizer(str, ";");}long endTime = System.currentTimeMillis();System.out.println("tokenizerMethod use " + (endTime - beginTime));}public static void IndexMethod(String str) {long beginTime = System.currentTimeMillis();String tmp = str;for (int i = 0; i < 10000; i++) {while (true) {String splitStr = null;int j = tmp.indexOf(";");if(j<0) break;splitStr = tmp.substring(0,j);tmp = tmp.substring(j+1);}tmp = str;}long endTime = System.currentTimeMillis();System.out.println("IndexMethod use " + (endTime - beginTime));}/*** @param args*/public static void main(String[] args) {// TODO Auto-generated method stubString orgStr = null;StringBuffer sb = new StringBuffer();for (int i = 0; i < 1000; i++) {sb.append(i);sb.append(";");}orgStr = sb.toString();splitMethod(orgStr);tokenizerMethod(orgStr);IndexMethod(orgStr);}}

4) 使用ChartAt 代替  startsWith 和 endsWith

性能要求比较高时,可以使用这条。

5) StringBuffer 和 StringBuilder

String result = "String" + "and" + "string"+"append";

这段看起来性能不高的代码在实际执行时反而会比StringBuilder 来的快。

原因是Java 编译器本身会做优化。

但是不能完全依靠编译器的优化, 还是建议显示地使用StringBuffer 或StringBuffer对象来提升系统性能。

StringBuffer 和 StringBuilder 的差异是:

StingBuffer 几乎所有的方法都做了同步

StringBuilder 并没有任何同步。

所以StringBuilder 的效率好于StringBuffer, 但是在多线程系统中,StringBuilder 无法保证线程安全。

另外,预先评估StringBuilder 的大小,能提升系统的性能。

2. 核心数据结构

1) List 接口

3种List实现: ArrayList,  Vector, 和LinkedList

ArrayList 和Vector 使用了数组实现, Vector 绝大部分方法都做了线程同步, ArrayList 没有对任何一个方法做线程同步。(ArrayList 和Vector 看上去性能相差无几)

使用LinkedList 对堆内存和GC的要求更高。

如果在系统应用中, List对象需要经常在任意位置插入元素,则可以考虑使用LinkedList 替代ArrayList

对于ArrayList从尾部删除元素时效率很高,从头部删除元素时相当费时,

而LinkedList 从头尾删除元素时效率相差无几,但是从中间删除元素时性能非常槽糕。

  头部 中间 尾部
ArrayList 6203 3125 16
LinkedList 15 8781 16

List 遍历操作

  ForEach 迭代器 for 循环
ArrayList 63 47 31
LinkedList 63 47 无穷大

2) Map 接口

实现类有: Hashtable, HashMap, LinkedHashMap和TreeMap

HashTable 和HashMap  的差异

HashTable大部分方法做了同步, HashTable 不允许key或者Value 使用null值;(性能上看起来无明显差异)

3) Set 接口

4) 优化集合访问代码

1. 分离循环中被重复调用的代码

for(int i=0;i<collection.size();i++)

替换成

int  count = collection.size();

for(int i=0;i<count;i++)

5). RandomAccess接口

3. 使用NIO 提升性能

在Java 的标准I/O中, 提供了基于流的I/O 实现, 即InputStream 和 OutputStream. 这种基于流的实现是以字节为单位处理数据, 并且非常容易建立各种过滤器。

NIO是 New I/O 的简称, 与旧式的基于流的 I/O 方法相对, 它表示新的一套Java I/O 标准。是在Java 1.4 中被纳入到JDK中的, 特性有

- 为所有的原始类型提供 Buffer 缓存支持

-  使用Java.nio.charset.Charset 作为字符集编码解码解决方案

- 增加通道对象(Channel), 作为新的原始I/O 抽象

- 支持锁和内存映射文件的文件访问接口

- 提供了基于Selector 的异步网络I/O

与流式的I/O 不同, NIO 是基于块(Block 的), 它以块为基本单位处理数据。

4. 引用类型。

强引用、软引用、弱引用和虚引用

强引用:

a)强引用可以直接访问目标对象

b) 强引用所指向的对象在任何时候都不会被系统回收

c)强引用可能导致内存泄漏

软引用:

软引用可以通过java.lang.ref.SoftReference来使用。 一个持有软引用的对象,不会被JVM很快回收, JVM会根据当前的使用状况来判断何时回收.当堆使用率临近阈值时,才会去回收软引用的对象。只要有足够的内存,软引用便可能在内存中存活相对长一段时间。因此,软引用可以用于实现对内存敏感的Cache.

弱引用:

在系统GC时,只要发现弱引用,不管系统堆空间是否足够,都会将对象进行回收。

虚引用

一个持有虚引用的对象,和没有引用几乎是一样的,随时都可能被垃圾回收器回收。但试图通过虚引用的get()方法去跌强引用时,总是会失败。并且,虚引用必须和引用队列一起使用,它的作用在于跟踪回收过程。

WeakHashMap类及其实现

WeakHashMap 是弱引用的一种典型应用,它可以作为简单的缓存表解决方案。

改善系统性能的技巧

1. 慎用异常

try catch 应用与循环体之外

2. 使用局部变量

调用方法时传递的参数以及在调用中创建的临时变量都保存在栈中,速度较快。

其他变量,如静态变量、实例变量等,都在堆中创建,速度较慢。

局部变量的访问速度远远高于类的成员变量。

3. 位运算代替乘除法

4. 替换Switch .

这一条应该注意的是一个新的思路问题, 使用不同的方式代替switch 语句。

这里的例子性能测试起来, switch 的性能反而更好一些。

package com.oscar999.performance.skill;public class SwitchReplaceSkill {/*** @param args*/protected void oldMethod() {int re = 0;for (int i = 0; i < 100000000; i++) {re = switchInt(i);}}public void newMethod() {int re=0;int[] sw= new int[]{0,3,6,7,8,10,16,18,44};for(int i = 0; i < 100000000; i++) {re = arrayInt(sw,i);}}protected int switchInt(int z) {int i = z % 10 + 1;switch (i) {case 1:return 3;case 2:return 6;case 3:return 7;case 4:return 8;case 5:return 10;case 6:return 16;case 7:return 18;case 8:return 44;default:return -1;}}protected int arrayInt(int[] sw,int z){int i=z%10+1;if(i>8||i<1){return -1;}else{return sw[i];}}public static void main(String[] args) {// TODO Auto-generated method stubSwitchReplaceSkill skill = new SwitchReplaceSkill();long begTime = System.currentTimeMillis();       skill.oldMethod();long endTime = System.currentTimeMillis();       System.out.println("Old Method use: "+(endTime-begTime));begTime = System.currentTimeMillis();  skill.newMethod();endTime = System.currentTimeMillis();    System.out.println("New Method use: "+(endTime-begTime));}}

5. 一维数组代替二维数组

直接看例子:

package com.oscar999.performance.skill;public class OneDime2TwoDime {protected void oldMethod(){int[][] array = new int[1000][1000];int re = 0;for(int k=0;k<100;k++)for(int i=0;i<array[0].length;i++)for(int j=0;j<array[0].length;j++)array[i][j] = i;for(int k=0;k<100;k++)for(int i=0;i<array[0].length;i++)for(int j=0;j<array[0].length;j++)re=array[i][j];       }protected void newMethod(){int[] array = new int[1000000];int re = 0;for(int k=0;k<100;k++)for(int i=0;i<array.length;i++)array[i] = i;for(int k=0;k<100;k++)for(int i=0;i<array.length;i++)re = array[i]; }   /*** @param args*/public static void main(String[] args) {// TODO Auto-generated method stubOneDime2TwoDime skill = new OneDime2TwoDime();long begTime = System.currentTimeMillis();     skill.oldMethod();long endTime = System.currentTimeMillis();       System.out.println("Old Method use: "+(endTime-begTime));begTime = System.currentTimeMillis();  skill.newMethod();endTime = System.currentTimeMillis();    System.out.println("New Method use: "+(endTime-begTime));}}

Old Method use: 453
New Method use: 281

一维数字用的时间少了很多。

6. 提取表达式

有些重复运算的部分可以提取出来。

package com.oscar999.performance.skill;public class ExtraExpression {protected void oldMethod(){double d = Math.random();double a = Math.random();double b = Math.random();double e = Math.random();double x,y;for(int i=0;i<10000000;i++){x = d*a*b/3*4*a;x = e*a*b/3*4*a;}}protected void newMethod(){double d = Math.random();double a = Math.random();double b = Math.random();double e = Math.random();double x,y,t;for(int i=0;i<10000000;i++){t = a*b/3*4*a;x = d*t;x = e*t;}}/*** @param args*/public static void main(String[] args) {// TODO Auto-generated method stubOneDime2TwoDime skill = new OneDime2TwoDime();long begTime = System.currentTimeMillis();        skill.oldMethod();long endTime = System.currentTimeMillis();       System.out.println("Old Method use: "+(endTime-begTime));begTime = System.currentTimeMillis();  skill.newMethod();endTime = System.currentTimeMillis();    System.out.println("New Method use: "+(endTime-begTime));}}

Old Method use: 109
New Method use: 79

7.  展开循环

展开循环是一种在极端情况下使用的优化手段,因为展开循可能会影响代码的可读性和可维护性, 所以取舍之间, 就要根据实际状况来看了。

看例子:

package com.oscar999.performance.skill;public class ExpandCycle {protected void oldMethod(){int[] array = new int[9999999];for(int i=0;i<9999999;i++){array[i]=i;}}protected void newMethod(){int[] array = new int[9999999];for(int i=0;i<9999999;i+=3){array[i]=i;array[i+1]=i+1;array[i+2]=i+2;}}/*** @param args*/public static void main(String[] args) {// TODO Auto-generated method stubExpandCycle skill = new ExpandCycle();long begTime = System.currentTimeMillis();        skill.oldMethod();long endTime = System.currentTimeMillis();       System.out.println("Old Method use: "+(endTime-begTime));begTime = System.currentTimeMillis();  skill.newMethod();endTime = System.currentTimeMillis();    System.out.println("New Method use: "+(endTime-begTime));}}

Old Method use: 78
New Method use: 47

8. 布尔运算代替位运算

虽然位运算的速度远远高于算术运算,但是在条件判断时,使用位运算替代布尔运算却是非常错误的选择。

对于"a&&b&&c", 只要有一项返回 false, 整个表达式就返回 false.

package com.oscar999.performance.skill;public class BooleanBit {protected void oldMethod(){boolean a = false;boolean b = true;int d = 0;for(int i=0;i<10000000;i++)if(a&b&"Java_Perform".contains("Java"))d = 0;}protected void newMethod(){boolean a = false;boolean b = true;int d = 0;for(int i=0;i<10000000;i++)if(a&&b&&"Java_Perform".contains("Java"))d = 0;      }/*** @param args*/public static void main(String[] args) {// TODO Auto-generated method stubBooleanBit skill = new BooleanBit();long begTime = System.currentTimeMillis();      skill.oldMethod();long endTime = System.currentTimeMillis();       System.out.println("Old Method use: "+(endTime-begTime));begTime = System.currentTimeMillis();  skill.newMethod();endTime = System.currentTimeMillis();    System.out.println("New Method use: "+(endTime-begTime));}}

Old Method use: 265
New Method use: 32

9. 使用 arrayCopy()

Java API 提高了数组复制的高效方法: arrayCopy().

这个函数是 native 函数, 通常native 函数的性能要优于普通的函数, 仅出于性能考虑, 在软件开发时, 应尽可能调用native 函数。

package com.oscar999.performance.skill;public class ArrayCopy {protected void oldMethod(){int size = 100000;int[] array = new int[size];int[] arraydst = new int[size];for(int i=0;i<array.length;i++){array[i]=i;}for(int k=0;k<1000;k++)for(int i=0;i<size;i++)arraydst[i]=array[i];}protected void newMethod(){int size = 100000;int[] array = new int[size];int[] arraydst = new int[size];for(int i=0;i<array.length;i++){array[i]=i;}for(int k=0;k<1000;k++)System.arraycopy(array, 0, arraydst, 0, size);}/*** @param args*/public static void main(String[] args) {// TODO Auto-generated method stubArrayCopy skill = new ArrayCopy();long begTime = System.currentTimeMillis();      skill.oldMethod();long endTime = System.currentTimeMillis();       System.out.println("Old Method use: "+(endTime-begTime));begTime = System.currentTimeMillis();  skill.newMethod();endTime = System.currentTimeMillis();    System.out.println("New Method use: "+(endTime-begTime));}}

Old Method use: 156
New Method use: 63

10. 使用 Buffer 进行 I/O 操作

除NIO 外, 使用Java 进行I/O 操作有两种基本方式

1. 使用基于InputStream 和 OutoutStream的方式

2. 使用Writer 和Reader

无论使用哪种方式进行文件I/O , 如果能合理地使用缓冲, 就能有效提高I/O 的性能

package com.oscar999.performance.skill;import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;public class BufferStream {protected void oldMethod() {int count = 10000;try {DataOutputStream dos = new DataOutputStream(new FileOutputStream("testfile.txt"));for (int i = 0; i < count; i++)dos.writeBytes(String.valueOf(i) + "\r\n");dos.close();} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}protected void newMethod(){int count = 10000;try{DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("testfile.txt"))); for (int i = 0; i < count; i++)dos.writeBytes(String.valueOf(i) + "\r\n");dos.close();}catch(Exception e){e.printStackTrace();}}/*** @param args*/public static void main(String[] args) {// TODO Auto-generated method stubBufferStream skill = new BufferStream();long begTime = System.currentTimeMillis();skill.oldMethod();long endTime = System.currentTimeMillis();System.out.println("Old Method use: " + (endTime - begTime));begTime = System.currentTimeMillis();skill.newMethod();endTime = System.currentTimeMillis();System.out.println("New Method use: " + (endTime - begTime));}}

Old Method use: 516
New Method use: 0

11. 使用clone() 代替new

对于重量级对象, 优于对象在构造函数中可能会进行一些复杂且耗时额操作, 因此, 构造函数的执行时间可能会比较长。Object.clone() 方法可以绕过对象构造函数, 快速复制一个对象实例。由于不需要调用对象构造函数, 因此, clone 方法不会受到构造函数性能的影响, 快速生成一个实例。

12. 静态方法替代实例方法

使用static 关键字描述的方法为静态方法, 在Java 中, 优于实例方法需要维护一张类似虚函数表的结构,以实现对多态的支持。 与静态方法相比, 实例方法的调用需要更多的资源。 因此,对于一些常用的工具类方法,没有对其进行重载的必要,那么将它们声明为static, 便可以加速方法的调用。

Java 性能优化系列之1[设计与程序优化]相关推荐

  1. 【MySQL性能优化系列】百万数据limit分页优化

    背景 众所周知,在使用limit分页过程中,随着前端传过来的PageSize越来越大,查询速度会越来越慢.那有什么优化的办法呢? 本文将通过百万数据表进行演示和优化, 欲知详情,请看下文分解. lim ...

  2. iOS性能优化系列篇之“列表流畅度优化”工具篇

    这一篇文章是iOS性能优化系列文章的的第二篇,主要内容是关于列表流畅度的优化.在具体内容的阐述过程中会结合性能优化的总体原则进行分析,所以建议大家在阅读这篇文章前先阅读一下上一篇文章:iOS性能优化系 ...

  3. 百度App网络深度优化系列(一):DNS优化

    一.前言 网络优化是客户端几大技术方向中公认的一个深度领域,所以百度App给大家带来网络深度优化系列文章,其中包含系列<一>DNS优化,系列<二>连接优化,系列<三> ...

  4. Sql优化系列之(1)__where子句条件优化

    1.为什么要进行SQL优化 系统优化中一个很重要的方面就是SQL语句的优化.对于海量数据,劣质SQL语句和优质SQL语句之间的速度差别可以达到上百倍,可见对于一个系统不是简单地能实现其功能就可,而是要 ...

  5. 为JAVA性能而设计(一)

    为JAVA性能而设计(一)   发布时间:2007-1-9 15:51:42     来源:JavaWorld    作者:Brian Go- 为JAVA性能而设计(二)   发布时间:2007-1- ...

  6. 赠书:《Java性能优化实践》,众多业内大佬推荐阅读

    没有捷径可走的 Java 性能优化 多年来,用 Google 搜索 Java performance tuning,出现的三篇最热门文章之一是于 1997 年到 1998 年左右发表的文章,这篇文章在 ...

  7. 新书上市 | 《Java性能优化实践》,众多业内大佬推荐阅读

    没有捷径可走的 Java 性能优化 多年来,用 Google 搜索 Java performance tuning,出现的三篇最热门文章之一是于 1997 年到 1998 年左右发表的文章,这篇文章在 ...

  8. 调整了一下JVM内存,程序快了700%,Java程序优化笔记

    Java程序性能优化是一个永远逃不开的话题,优化无处不在!一个偶然间,小编得到了一份Java程序性能优化的笔记,小编读完这份笔记后大为惊叹,实在是写的太好了,不忍独乐乐,于是把这份笔记免费分享出来,供 ...

  9. WPF性能调试系列 – 内存监测

    原文:WPF性能调试系列 – 内存监测 WPF性能调试系列文章: WPF页面渲染优化:Application Timeline WPF页面业务加载优化:Ants Performance Profile ...

最新文章

  1. docker学习笔记(六)docker-compose
  2. SpringBoot入门教程(一)详解intellij idea搭建SpringBoot
  3. Android存储数据方式
  4. axure怎么做5秒倒计时_罗胖60秒:怎么做一个课程?
  5. 【java】为什么HashMap桶中节点个数超过8才转为红黑树?
  6. Microsoft visual studio关闭安全检查
  7. Lua中实现类似C#的事件机制
  8. php.ini添加的变量读取,php用ini_get获取php.ini里变量值的方法
  9. nginx优化配置(转)
  10. 开启本地git权限_Git入门使用和常见操作
  11. 电脑tf卡检测不到_电脑不认TF卡,有什么方法
  12. 生命游戏(Anylogic实现)
  13. Handler 机制简介
  14. 计算机网络的最大优点,什么是计算机网络最突出的优点
  15. Safari安装使用JsonView插件
  16. 市场的结构,各种投资方法的此消彼长,没有所谓圣杯
  17. oracle中dlink使用,create dlink(oracle)
  18. 传输层协议TCP—RTTM(11)
  19. linux命令行测网速
  20. 联想E440打开VT(虚拟化)

热门文章

  1. angularJS限制 input-text 只能输入数字
  2. ListT清除重复某一项
  3. JavaScript:windows关机效果
  4. windows下SVN日志反馈中文乱码的解决方法
  5. windows下安装phpcms html/ 文件夹不可写的一种错误以及解决方法
  6. IMP-00009: 导出文件异常结束 imp
  7. 【实践】强化学习在招聘推荐冷启动优化中的应用实践
  8. 【报告分享】2020年母婴未来消费新趋势报告.pdf(附下载链接)
  9. 【报告分享】快消产业互联网案例研究报告.pdf(附下载链接)
  10. 国内git clone报错问题解决办法