关于java:Java-性能优化的-55-个细节珍藏版

42次阅读

共计 11278 个字符,预计需要花费 29 分钟才能阅读完成。

    • 1. 尽量在适合的场合应用单例
  • 2. 尽量避免随便应用动态变量
  • 3. 尽量避免过多过常的创立 Java 对象
  • 4. 尽量应用 final 修饰符
  • 5. 尽量应用局部变量
  • 6. 尽量解决好包装类型和根本类型两者的应用场合
  • 7. 慎用 synchronized,尽量减小 synchronize 的办法
  • 9. 尽量不要应用 finalize 办法
  • 10. 尽量应用根本数据类型代替对象
  • 11. 多线程在未产生线程平安前提下应尽量应用 HashMap、ArrayList
  • 12. 尽量正当的创立 HashMap
  • 13. 尽量减少对变量的反复计算
  • 14. 尽量避免不必要的创立
  • 15. 尽量在 finally 块中开释资源
  • 16. 尽量应用移位来代替 ’a/b’ 的操作
  • 17. 尽量应用移位来代替 ’a\*b’ 的操作
  • 18. 尽量确定 StringBuffer 的容量
  • 19. 尽量早开释无用对象的援用
  • 20. 尽量避免应用二维数组
  • 21. 尽量避免应用 split
  • 22. ArrayList & LinkedList
  • 23. 尽量应用 System.arraycopy () 代替通过来循环复制数组
  • 24. 尽量缓存常常应用的对象
  • 25. 尽量避免十分大的内存调配
  • 26. 慎用异样
  • 27. 尽量重用对象
  • 28. 不要反复初始化变量
  • 29. 在 java+Oracle 的利用零碎开发中,java 中内嵌的 SQL 语言应尽量应用大写模式,以缩小 Oracle 解析器的解析累赘。
  • 30. 在 java 编程过程中,进行数据库连贯,I/ O 流操作,在应用结束后,及时敞开以开释资源。因为对这些大对象的操作会造成零碎大的开销。
  • 31. 过分的创建对象会耗费零碎的大量内存,重大时,会导致内存透露,因而,保障过期的对象的及时回收具备重要意义。JVM 的 GC 并非非常智能,因而倡议在对象应用结束后,手动设置成 null。
  • 32. 在应用同步机制时,应尽量应用办法同步代替代码块同步 **。**
  • 33. 不要在循环中应用 Try/Catch 语句,应把 Try/Catch 放在循环最外层
  • 34. 通过 StringBuffer 的构造函数来设定他的初始化容量,能够显著晋升性能
  • 35. 正当应用 java.util.Vector
  • 38. 不必 new 关键字创建对象的实例
  • 39. 不要将数组申明为:public static final
  • 40. HaspMap 的遍历:
  • 41. array(数组) 和 ArrayList 的应用
  • 42. 单线程应尽量应用 HashMap, ArrayList, 除非必要,否则不举荐应用 HashTable,Vector,她们应用了同步机制,而升高了性能。
  • 43. StringBuffer,StringBuilder 的区别在于:java.lang.StringBuffer 线程平安的可变字符序列。一个相似于 String 的字符串缓冲区,但不能批改。StringBuilder 与该类相比,通常应该优先应用 StringBuilder 类,因为她反对所有雷同的操作,但因为她不执行同步,所以速度更快。为了取得更好的性能,在结构 StringBuffer 或 StringBuilder 时应尽量指定她的容量。当然如果不超过 16 个字符时就不必了。雷同状况下,应用 StringBuilder 比应用 StringBuffer 仅能取得 10%~15% 的性能晋升,但却要冒多线程不平安的危险。综合思考还是倡议应用 StringBuffer。
  • 44. 尽量应用根本数据类型代替对象。
  • 45. 应用具体类比应用接口效率高,但构造弹性升高了,但古代 IDE 都能够解决这个问题。
  • 46. 思考应用静态方法,如果你没有必要去拜访对象的内部,那么就使你的办法成为静态方法。她会被更快地调用,因为她不须要一个虚构函数导向表。这共事也是一个很好的实际,因为她通知你如何辨别办法的性质,调用这个办法不会扭转对象的状态。
  • 47. 应尽可能防止应用外在的 GET,SET 办法。
  • 48. 防止枚举,浮点数的应用。
  • 49、防止在循环条件中应用简单表达式
  • 50、为 ’Vectors’ 和 ‘Hashtables’ 定义初始大小
  • 51、在 finally 块中敞开 Stream
  • 52、应用 ’System.arraycopy ()’ 代替通过来循环复制数组, 例子:
  • 53、让拜访实例内变量的 getter/setter 办法变成”final”
  • 54、对于常量字符串,用 ’String’ 代替 ‘StringBuffer’
  • 55、在字符串相加的时候,应用 ‘ ‘ 代替 ” “,如果该字符串只有一个字符的话

在 JAVA 程序中,性能问题的大部分起因并不在于 JAVA 语言,而是程序自身。养成良好的编码习惯十分重要,可能显著地晋升程序性能。

1. 尽量在适合的场合应用单例

应用单例能够加重加载的累赘,缩短加载的工夫,进步加载的效率,但并不是所有中央都实用于单例,简略来说,单例次要实用于以下三个方面:

第一,管制资源的应用,通过线程同步来管制资源的并发拜访;

第二,管制实例的产生,以达到节约资源的目标;

第三,管制数据共享,在不建设间接关联的条件下,让多个不相干的过程或线程之间实现通信。

2. 尽量避免随便应用动态变量

要晓得,当某个对象被定义为 static 变量所援用,那么 GC 通常是不会回收这个对象所占有的内存,如

public class A{private static B b = new B(); 
}

此时动态变量 b 的生命周期与 A 类同步,如果 A 类不会卸载,那么 b 对象会常驻内存,直到程序终止。

3. 尽量避免过多过常的创立 Java 对象

尽量避免在常常调用的办法,循环中 new 对象,因为零碎不仅要花费工夫来创建对象,而且还要花工夫对这些对象进行垃圾回收和解决,在咱们能够管制的范畴内,最大限度的重用对象,最好能用根本的数据类型或数组来代替对象。

4. 尽量应用 final 修饰符

带有 final 修饰符的类是不可派生的。在 JAVA 外围 API 中,有许多利用 final 的例子,例如 java.lang.String,为 String 类指定 final 避免了使用者笼罩 length() 办法。另外,如果一个类是 final 的,则该类所有办法都是 final 的。java 编译器会寻找机会内联(inline)所有的 final 办法(这和具体的编译器实现无关)。此举可能使性能均匀进步 50%。

如:让拜访实例内变量的 getter/setter 办法变成”final:

简略的 getter/setter 办法应该被置成 final,这会通知编译器,这个办法不会被重载,所以,能够变成”inlined”, 例子:

class MAF {public void setSize (int size) {_size = size;} 
     private int _size; 
}

更正

class DAF_fixed {final public void setSize (int size) {_size = size;} 
     private int _size; 
}

5. 尽量应用局部变量

调用办法时传递的参数以及在调用中创立的长期变量都保留在栈(Stack)中,速度较快。其余变量,如动态变量,实例变量等,都在堆(Heap)中创立,速度较慢。

6. 尽量解决好包装类型和根本类型两者的应用场合

尽管包装类型和根本类型在应用过程中是能够互相转换,但它们两者所产生的内存区域是齐全不同的,根本类型数据产生和解决都在栈中解决,包装类型是对象,是在堆中产生实例。在汇合类对象,有对象方面须要的解决实用包装类型,其余的解决提倡应用根本类型。

7. 慎用 synchronized,尽量减小 synchronize 的办法

都晓得,实现同步是要很大的零碎开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。synchronize 办法被调用时,间接会把以后对象锁 了,在办法执行完之前其余线程无奈调用以后对象的其余办法。所以 synchronize 的办法尽量小,并且应尽量应用办法同步代替代码块同步。

9. 尽量不要应用 finalize 办法

实际上,将资源清理放在 finalize 办法中实现是十分不好的抉择,因为 GC 的工作量很大,尤其是回收 Young 代内存时,大都会引起应用程序暂停,所以再抉择应用 finalize 办法进行资源清理,会导致 GC 累赘更大,程序运行效率更差。

10. 尽量应用根本数据类型代替对象

String str = “hello”;

下面这种形式会创立一个“hello”字符串,而且 JVM 的字符缓存池还会缓存这个字符串;

String str = new String(“hello”);

此时程序除创立字符串外,str 所援用的 String 对象底层还蕴含一个 char[] 数组,这个 char[] 数组顺次寄存了 h,e,l,l,o

11. 多线程在未产生线程平安前提下应尽量应用 HashMap、ArrayList

HashTable、Vector 等应用了同步机制,升高了性能。

12. 尽量正当的创立 HashMap

当你要创立一个比拟大的 hashMap 时,充分利用这个构造函数

public HashMap(int initialCapacity, float loadFactor);

防止 HashMap 屡次进行了 hash 重构, 扩容是一件很消耗性能的事,在默认中 initialCapacity 只有 16,而 loadFactor 是 0.75,须要多大的容量,你最好能精确的预计你所须要的最佳大小,同样的 Hashtable,Vectors 也是一样的情理。

13. 尽量减少对变量的反复计算

如:

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

应该改为

for(int i=0,len=list.size();i<len;i++)

并且在循环中应该防止应用简单的表达式,在循环中,循环条件会被反复计算,如果不应用简单表达式,而使循环条件值不变的话,程序将会运行的更快。

14. 尽量避免不必要的创立

如:

A a = new A();

if(i==1){list.add(a);}

应该改为

if(i==1){A a = new A();

  list.add(a);

}

15. 尽量在 finally 块中开释资源

程序中应用到的资源该当被开释,以防止资源透露。这最好在 finally 块中去做。不论程序执行的后果如何,finally 块总是会执行的,以确保资源的正确敞开。

16. 尽量应用移位来代替 ’a/b’ 的操作

“/” 是一个代价很高的操作,应用移位的操作将会更快和更无效

int num = a / 4;

int num = a / 8;

应该改为

int num = a >> 2;

int num = a >> 3;

但留神的是应用移位应增加正文,因为移位操作不直观,比拟难了解

17. 尽量应用移位来代替 ’a*b’ 的操作

同样的,对于 ’*’ 操作,应用移位的操作将会更快和更无效

int num = a * 4;

int num = a * 8;

应该改为

int num = a << 2;

int num = a << 3;

18. 尽量确定 StringBuffer 的容量

StringBuffer 的结构器会创立一个默认大小(通常是 16)的字符数组。在应用中,如果超出这个大小,就会从新分配内存,创立一个更大的数组,并将原先的数组复制过去,再 抛弃旧的数组。在大多数状况下,你能够在创立 StringBuffer 的时候指定大小,这样就防止了在容量不够的时候主动增长,以进步性能。

如:

StringBuffer buffer = new StringBuffer(1000);

19. 尽量早开释无用对象的援用

大部分时,办法部分援用变量所援用的对象 会随着办法完结而变成垃圾,因而,大部分时候程序无需将部分,援用变量显式设为 null。

例如:

Java 代码

Public void test(){Object obj = new Object(); 

  …… 

  Obj=null; 

}

下面这个就没必要了,随着办法 test() 的执行实现,程序中 obj 援用变量的作用域就完结了。然而如果是改成上面:

Java 代码

Public void test(){Object obj = new Object(); 

  …… 

  Obj=null; 

  // 执行耗时,耗内存操作;或调用耗时,耗内存的办法

  …… 

}

这时候就有必要将 obj 赋值为 null,能够尽早的开释对 Object 对象的援用。

20. 尽量避免应用二维数组

二维数据占用的内存空间比一维数组多得多,大略 10 倍以上。

21. 尽量避免应用 split

除非是必须的,否则应该防止应用 split,split 因为反对正则表达式,所以效率比拟低,如果是频繁的几十,几百万的调用将会消耗大量资源,如果的确须要频繁的调用 split,能够思考应用 apache 的 StringUtils.split(string,char),频繁 split 的能够缓存后果。

22. ArrayList & LinkedList

一个是线性表,一个是链表,一句话,随机查问尽量应用 ArrayList,ArrayList 优于 LinkedList,LinkedList 还要挪动指针,增加删除的操作 LinkedList 优于 ArrayList,ArrayList 还要挪动数据,不过这是理论性剖析,事实未必如此,重要的是了解好 2 者得数据结构,隔靴搔痒。

23. 尽量应用 System.arraycopy () 代替通过来循环复制数组

System.arraycopy() 要比通过循环来复制数组快的多

24. 尽量缓存常常应用的对象

尽可能将常常应用的对象进行缓存,能够应用数组,或 HashMap 的容器来进行缓存,但这种形式可能导致系统占用过多的缓存,性能降落,举荐能够应用一些第三方的开源工具,如 EhCache,Oscache 进行缓存,他们根本都实现了 FIFO/FLU 等缓存算法。

25. 尽量避免十分大的内存调配

有时候问题不是由过后的堆状态造成的,而是因为调配失败造成的。调配的内存块都必须是间断的,而随着堆越来越满,找到较大的间断块越来越艰难。

26. 慎用异样

当创立一个异样时,须要收集一个栈跟踪 (stack track),这个栈跟踪用于形容异样是在何处创立的。构建这些栈跟踪时须要为运行时栈做一份快照,正是这一部分开销很大。当须要创立一个 Exception 时,JVM 不得不说:先别动,我想就您当初的样子存一份快照,所以临时进行入栈和出栈操作。栈跟踪不只蕴含运行时栈中的一两个元素,而是蕴含这个栈中的每一个元素。

如果您创立一个 Exception,就得付出代价。好在捕捉异样开销不大,因而能够应用 try-catch 将核心内容包起来。从技术上讲,您甚至能够随便地抛出异样,而不必破费很大的代价。导致性能损失的并不是 throw 操作——只管在没有事后创立异样的状况下就抛出异样是有点不寻常。 真正要花代价的是创立异样 。侥幸的是,好的编程习惯已教会咱们,不应该不管三七二十一就抛出异样。异样是为异样的状况而设计的,应用时也应该牢记这一准则。

27. 尽量重用对象

特地是 String 对象的应用中,呈现字符串连贯状况时应应用 StringBuffer 代替,因为零碎不仅要花工夫生成对象,当前可能还须要花工夫对这些对象进行垃圾回收和解决。因而生成过多的对象将会给程序的性能带来很大的影响。

28. 不要反复初始化变量

默认状况下,调用类的构造函数时,java 会把变量初始化成确定的值,所有的对象被设置成 null,整数变量设置成 0,float 和 double 变量设置成 0.0,逻辑值设置成 false。当一个类从另一个类派生时,这一点尤其应该留神,因为用 new 关键字创立一个对象时,构造函数链中的所有构造函数都会被主动调用。这里有个留神,给成员变量设置初始值但须要调用其余办法的时候,最好放在一个办法比方 initXXX() 中,因为间接调用某办法赋值可能会因为类尚未初始化而抛空指针异样,如:public int state = this.getState();

另外搜寻公众号 Java 后端栈回复关键字 ” 面试”获取一份惊喜礼包。

29. 在 java+Oracle 的利用零碎开发中,java 中内嵌的 SQL 语言应尽量应用大写模式,以缩小 Oracle 解析器的解析累赘。

30. 在 java 编程过程中,进行数据库连贯,I/ O 流操作,在应用结束后,及时敞开以开释资源。因为对这些大对象的操作会造成零碎大的开销。

31. 过分的创建对象会耗费零碎的大量内存,重大时,会导致内存透露,因而,保障过期的对象的及时回收具备重要意义。JVM 的 GC 并非非常智能,因而倡议在对象应用结束后,手动设置成 null。

32. 在应用同步机制时,应尽量应用办法同步代替代码块同步。

33. 不要在循环中应用 Try/Catch 语句,应把 Try/Catch 放在循环最外层

Error 是获取零碎谬误的类,或者说是虚拟机谬误的类。不是所有的谬误 Exception 都能获取到的,虚拟机报错 Exception 就获取不到,必须用 Error 获取。

34. 通过 StringBuffer 的构造函数来设定他的初始化容量,能够显著晋升性能

StringBuffer 的默认容量为 16,当 StringBuffer 的容量达到最大容量时,她会将本身容量减少到以后的 2 倍 +2,也就是 2 *n+2。无论何时,只有 StringBuffer 达到她的最大容量,她就不得不创立一个新的对象数组,而后复制旧的对象数组,这会节约很多工夫。所以给 StringBuffer 设置一个正当的初始化容量值,是很有必要的!

35. 正当应用 java.util.Vector

Vector 与 StringBuffer 相似,每次扩大容量时,所有现有元素都要赋值到新的存储空间中。Vector 的默认存储能力为 10 个元素,扩容加倍。vector.add(index,obj) 这个办法能够将元素 obj 插入到 index 地位,但 index 以及之后的元素顺次都要向下挪动一个地位(将其索引加 1)。除非必要,否则对性能不利。同样规定实用于 remove(int index) 办法,移除此向量中指定地位的元素。将所有后续元素左移(将其索引减 1)。返回此向量中移除的元素。所以删除 vector 最初一个元素要比删除第 1 个元素开销低很多。删除所有元素最好用 removeAllElements() 办法。如果要删除 vector 里的一个元素能够应用 vector.remove(obj);而不用本人检索元素地位,再删除,如 int index = indexOf(obj);vector.remove(index);

38. 不必 new 关键字创建对象的实例

用 new 关键词创立类的实例时,构造函数链中的所有构造函数都会被主动调用。但如果一个对象实现了 Cloneable 接口,咱们能够调用她的 clone() 办法。clone() 办法不会调用任何类构造函数。上面是 Factory 模式的一个典型实现:

public static Credit getNewCredit() 
{return new Credit(); 
}

改良后的代码应用 clone() 办法:

private static Credit BaseCredit = new Credit(); 
public static Credit getNewCredit() 
{return (Credit)BaseCredit.clone();}

39. 不要将数组申明为:public static final

40. HaspMap 的遍历:

Map<String, String[]> paraMap = new HashMap<String, String[]>(); 
for(Entry<String, String[]> entry : paraMap.entrySet()) 
{String appFieldDefId = entry.getKey(); 
    String[] values = entry.getValue(); 
}

利用散列值取出相应的 Entry 做比拟失去后果,获得 entry 的值之后间接取 key 和 value。

41. array(数组) 和 ArrayList 的应用

array 数组效率最高,但容量固定,无奈动静扭转,ArrayList 容量能够动静增长,但就义了效率。

42. 单线程应尽量应用 HashMap, ArrayList, 除非必要,否则不举荐应用 HashTable,Vector,她们应用了同步机制,而升高了性能。

43. StringBuffer,StringBuilder 的区别在于:java.lang.StringBuffer 线程平安的可变字符序列。一个相似于 String 的字符串缓冲区,但不能批改。StringBuilder 与该类相比,通常应该优先应用 StringBuilder 类,因为她反对所有雷同的操作,但因为她不执行同步,所以速度更快。为了取得更好的性能,在结构 StringBuffer 或 StringBuilder 时应尽量指定她的容量。当然如果不超过 16 个字符时就不必了。雷同状况下,应用 StringBuilder 比应用 StringBuffer 仅能取得 10%~15% 的性能晋升,但却要冒多线程不平安的危险。综合思考还是倡议应用 StringBuffer。

44. 尽量应用根本数据类型代替对象。

45. 应用具体类比应用接口效率高,但构造弹性升高了,但古代 IDE 都能够解决这个问题。

46. 思考应用静态方法,如果你没有必要去拜访对象的内部,那么就使你的办法成为静态方法。她会被更快地调用,因为她不须要一个虚构函数导向表。这共事也是一个很好的实际,因为她通知你如何辨别办法的性质,调用这个办法不会扭转对象的状态。

47. 应尽可能防止应用外在的 GET,SET 办法。

48. 防止枚举,浮点数的应用。

以下举几个实用优化的例子:

一、防止在循环条件中应用简单表达式

在不做编译优化的状况下,在循环中,循环条件会被反复计算,如果不应用简单表达式,而使循环条件值不变的话,程序将会运行的更快。例子:

import java.util.Vector; 
class CEL {void method (Vector vector) {for (int i = 0; i < vector.size (); i++)   // Violation 
             ; // ... 
     } 
}

更正:

class CEL_fixed {void method (Vector vector) {int size = vector.size () 
         for (int i = 0; i < size; i++) 
             ; // ... 
     } 
}

二、为 ’Vectors’ 和 ‘Hashtables’ 定义初始大小

JVM 为 Vector 裁减大小的时候须要从新创立一个更大的数组,将原原先数组中的内容复制过去,最初,原先的数组再被回收。可见 Vector 容量的扩充是一个颇费工夫的事。

通常,默认的 10 个元素大小是不够的。你最好能精确的预计你所须要的最佳大小。例子:

import java.util.Vector;
public class DIC {public void addObjects (Object[] o) {
// if length > 10, Vector needs to expand
for (int i = 0; i< o.length;i++) {v.add(o);  // capacity before it can add more elements.
}
}
public Vector v = new Vector();  // no initialCapacity.}

更正:

本人设定初始大小。

public Vector v = new Vector(20); public Hashtable hash = new Hashtable(10);

三、在 finally 块中敞开 Stream

程序中应用到的资源该当被开释,以防止资源透露。这最好在 finally 块中去做。不论程序执行的后果如何,finally 块总是会执行的,以确保资源的正确敞开。

四、应用 ’System.arraycopy ()’ 代替通过来循环复制数组, 例子:

public class IRB
{void method () {int[] array1 = new int [100];
for (int i = 0; i < array1.length; i++) {array1 [i] = i;
}
int[] array2 = new int [100];
for (int i = 0; i < array2.length; i++) {array2 [i] = array1 [i]; // Violation
}
}
}

更正:

public class IRB
{void method () {int[] array1 = new int [100];
for (int i = 0; i < array1.length; i++) {array1 [i] = i;
}
int[] array2 = new int [100];
System.arraycopy(array1, 0, array2, 0, 100);
}
}

五、让拜访实例内变量的 getter/setter 办法变成”final”

简略的 getter/setter 办法应该被置成 final,这会通知编译器,这个办法不会被重载,所以,能够变成”inlined”, 例子:

class MAF {public void setSize (int size) {_size = size;}
private int _size;
}

更正:

class DAF_fixed {final public void setSize (int size) {_size = size;}
private int _size;
}

六、对于常量字符串,用 ’String’ 代替 ‘StringBuffer’

常量字符串并不需要动静扭转长度。

例子:

public class USC {String method () {StringBuffer s = new StringBuffer ("Hello");
String t = s + "World!";
return t;
}
}

更正:把 StringBuffer 换成 String,如果确定这个 String 不会再变的话,这将会缩小运行开销进步性能。

七、在字符串相加的时候,应用 ‘ ‘ 代替 ” “,如果该字符串只有一个字符的话

例子:

public class STR {public void method(String s) {
String string = s + "d"  // violation.
string = "abc" + "d"    // violation.
}
}

更正:

将一个字符的字符串替换成 ’ ‘

public class STR {public void method(String s) {
String string = s + 'd'
string = "abc" + 'd' 
}
}

以上仅是 Java 方面编程时的性能优化,性能优化大部分都是在工夫、效率、代码构造档次等方面的衡量,各有利弊,不要把下面内容当成教条,或者有些对咱们理论工作实用,有些不实用,还望依据理论工作场景进行取舍吧,活学活用,变通为宜。

你还有什么想要补充的吗?

来自:https://blog.csdn.net/guorui\_java/article/details/104107390/

本文应用 文章同步助手 同步

正文完
 0