JAVA性能优化
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
返回
返回
Java程序在执行的过程中就是一个初始化对象和调用其方法的过程,其中对方 法的调用花费了很多资源,这些资源都用来转移线程控制,传递参数,返回结果 和创建用于存放本地变量及中间结果的栈(stack)。这部分优化有下面三点 内联(Inlining)方法: 由于方法的调用需要消耗大量的资源,因此,Java编译器可以将一些方法调用转化为代 码嵌入,就是将一段代码对一个方法的调用转化为将该方法的代码在编译时嵌入到调用 处,这样,由于减少了方法的调用,就可以大大提高代码的性能,当将一个方法声明为final, static,private时,编译器就会自动的使用代码嵌入技术将该方法代码在编译时嵌入到调 用处。此举能够使性能平均提高50%。例如如果有两个方法A和B,A方法中多次调用B 方法,如果B没有被指定为final,则B不会被内联到A中,那么你每调用一次方法B,就 要多花费一些资源。 值得注意的是JAVA虚拟机能够自动判断你的程序是否需要内联。 注意:假如方法B中有很多循环嵌套语句,系统会认为这些语句给程序的正确执行带来 干扰,将不会把它设置为内联。测试见例2.1 同步(Synchronized)方法: 在多线程访问共享数据时,为了保证数据的一致性,就必然要使用同步技术,但使用同 步方法比使用非同步方法的性能要低,见例1.5。因此,我们应尽量少使用同步方法,并 且调用同步方法的代码本身就不需要再同步了。 局部变量: 尽量使用局部变量,因为调用方法时传递的参数以及在调用中创建的临时变量都保 存在栈(Stack)中,而且栈的存取速度较快。其他变量,如静态变量、实例变量在堆 (Heap)中创建,速度较慢。
Hashtable和HashMap 它们的性能方面的比较类似 Vector和ArrayList,比如Hashtable的方法是同步的,而 HashMap的不是。 当它们中的元素超过它的初始大小时,都会将它的容量翻倍。
ArrayList和LinkedList
对于处理一列数据项,Java提供了两个类ArrayList和LinkedList,ArrayList的内部实现是基 于内部数组Object[],所以从概念上讲,它更象数组,但LinkedList的内部实现是基于一组连 接的记录,所以,它更象一个链表结构,所以,它们在性能上有很大的差别。 (1)在ArrayList的前面或中间插入数据时,你必须将其后的所有数据相应的后移,这样必 然要花费较多时间,所以,当你的操作是在一列数据的后面添加数据而不是在前面或中间, 并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能。 (2)访问链表中的某个元素时,就必须从链表的一端开始沿着连接方向一个一个元素地 去查找,直到找到所需的元素为止,所以,当你的操作是在一列数据的前面或中间添加或删 除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList了。 注意:在Java集合框架中的大部分类的大小是可以随着元素个数的增加而相应的增加的, 我们似乎不用关心它的初始大小,但如果我们考虑类的性能问题时,就一定要考虑尽可能地 设置好集合对象的初始大小,这将大大提高代码的性能,比如,Hashtable缺省的初始大小为 11,载入因子为0.75,即如果其中的元素个数超过7个,它就必须增加大小并重新组织元素, 所以,如果你知道在创建一个新的Hashtable对象时就知道元素的确切数目如为12,那么,就应 将其初始大小设为12/0.75=16,这样,就可以避免重新组织内存并增加大小。(默认Vector ArrayList 10个大小,Hashtable 11,HashMap 16)
循环(Loop)中的优化技巧
因为循环中的代码会被反复的执行,所以循环中经常是寻找有关性能问题的地方,嵌 套的循环更容易产生性能问题, 在循环中,我们应该注意如下问题: 1、循环常量,在循环中它的值不会改变,因此,它的值应该在循环外先计算出来。 循环条件,在不做编译优化的情况下,循环条件会被反复计算,如果不使用复杂表达式, 而使循环条件值不变的话,程序将会运行的更快。如下例所示 class CEL { void method (Vector vector) { for (int i = 0; i < vector.size (); i++) } } 应更改为: class CEL_fixed { void method (Vector vector) { int size = vector.size () for (int i = 0; i < size; i++) } } 计算 size的时间大概有0.000001s,如果表达式更复杂时间消耗更多,尽量将表达式的计 算放在循环外来作。 2、由于在方法中使用本地变量比使用对象的属性消耗较少的资源,但在循环中却不 一样, 因为循环中的代码要反复地被运行,因此,尽量少地在循环中创建对象和变量。 3、尽早结束循环,如果循环体在满足一定条件就可以结束,就应尽快结束。
JAVA性能优化
2009/04/25 王景男
JAVA优化技巧 类的方法内联,方法的同步及局部变量 的使用 创建类的实例优化策略 建立对象池提升性能策略 JAVA垃圾回收机制及引用优化策略
String与StringBuffer的使用技巧
1、字符串在JAVA中被广泛的使用,但是由于String 对象是不可改变的, 所以如果 我们试图将两个String对象相加的时候,它实际的执行是产生一个中间对象StringBuffer, 并调用它的append ()法来进行相加的,最后调用StringBufffer的toString()方法来返回一 个String的对象,如果只是一般的相加差别不大,但是 如果是在循环中,性能差距就较
返回
返回
Java对象的内存是自动管理的,但并不意味着程序员不用担心内存的使用,内存的使用会 给系统带来很大的负担,比如,Java并不阻止程序占用过多的内存,当对象向堆所请求的内存不 足时,垃圾收集器(Garbage Collector)就会自动启动,释放那些引用数为零的对象所占用的内存 ,Java不会自动释放无用的对象的引用,如果程序忘记释放指向对象的引用,则程序运行时的内存 随着时间的推移而增加,发生所谓内存泄漏,创建对象不但消耗CPU的时间和内存,同时,为释 放对象内存JVM需不停地启动垃圾收集器,这也会消耗大量的CPU时间。 策略: 尽量避免在被经常调用的代码中创建对象。比如前面讲的不要在For循环中创建对象和变 量,也不应该在频繁被调用的方法中创建对象和变量,如果需要可以做为方法的参数传 递进去。 当只是访问一个类的某个方法时,不要创建该类的对象,而是将该方法设计成一个static的方 法。
3、由于在创建一个StringBuffer对象时, StringBuffer的构造器会创建一个默认大小(
通常是16)的字符数组。在使用中,如果超出这个大小,就会重新分配内存,创建一个 更大的数组,并将原先的数组复制过来,再丢弃旧的数组。在大多数情况下,如果可 以的话 ,在创建StringBuffer的时候应指定大小,这样就避免了在容量不够的时候自动
增长,以提高性能。如例1.3所示
返回
返回
返回
乘法和除法的优化技巧
考虑下面的代码
for (val = 0; val < 100000; val +=5) {
alterX = val * 8; myResult = val / 2; } 用移位操作替代乘法操作可以极大地提高性能。下面是修改后的代码: for (val = 0; val < 100000; val += 5) { alterX = val << 3; myResult = val >> 1; } 修改后的代码不再做乘以8的操作,而是改用等价的左移3位操作,每左移1位相当 于乘以2。相应地,右移1位操作相当于除以2。值得一提的是,虽然移位操作速度快, 但可能使代码比较难于理解,所以最好加上一些注释。
注:factory模式是将创建实例的工作与使用实例的工作分开, 也就是说,让创建实例所 需要的大量初始化工作从类的构造函数中分离出去,对于继承自同一父类的多个子类, 可根据实际的需要产生子类的实例,在子类中重写类工厂方法. 另外: 1.如果无法实现Clone,则应该尽量简化类的继承关系和设计简单的构造函数。
尽量使用系统已实现的函数来完成需要的功能
在需要实现某一项功能的时候,要尽量来使用系统已定义好的函数,例如使用 System.arraycopy ()要比通过循环来复制数组快的多。测试见例1.4
集合类优化问题
集合类在此Java编程中被广泛地使用,一个集合类就是将一组对象组装成一个对象, Java的集合类框架由一些接口(如Collection、List、Set、Map)和一些为通用目的而实现 的类(如Vector,ArrayList、Hashtable等等)组成,这些类里,有些提供了某种排序算法,有 的提供了同步的方法,有如此多的集合类,在具体使用过程中,我们如何根据自己的需要
明显(注:这个问题在 JDK1.6的版本中已被优化)如例1.1所示
注:String s = “a” + “b” + “c”,实际上在编译后是String s=“abc”,执行时不存在相加问题
2、在字符串相加的时候,如果该字符串只有一个字符的话 如:String str = s + “d” 应该换作 string = s + „d‟来执行。如例1.2所示
用new关键词创建类的实例时,构造函数链中的所有构造函数都会被自动调用。但如果一个 对象实现了Cloneable接口,我们可以调用它的clone()方法。clone()方法不会调用任何类构造 函数。 在使用设计模式(Design Pattern)的场合,如果用Factory模式创建对象,则改用 clone()方法创建新的对象实例非常简单。例如,下面是Factory模式的一个典型实现: public static Credit getNewCredit() { return new Credit(); } 改进后的代码使用clone()方法,如下所示 private static Credit BaseCredit = new Credit(); public static Credit getNewCredit() { return (Credit) BaseCredit.clone(); }
2.JAVA的Clone还分深层复制和浅层复制,浅层复制实际是指,如果一个对象的一个属
性是另一个对象的引用,那么如果在Clone这个对象的时候,我们Clone出来的那个对 象的属性和原对象的属性都是指向同一个对象的。
返回
Biblioteka Baidu
Java对象的生命周期大致包括三个阶段:对象的创建,对象的使用,对象的清除。 Java对象是通过构造函数来创建的,在这一过程中,该构造函数链中的所有构造函 数也都会被自动调用。另外,默认情况下,调用类的构造函数 时,Java会把变量初始化 成确定的值:所有的对象被设置成null,整数变量(byte、short、int、long)置为0,float 和 double变量设置成0.0,逻辑值设置成false。所以用new关键字来新建一个对象的时间 开销是很大的,如下所示。 运算操作 示例 标准化时间 本地赋值 i=n 1.0 实例赋值 this.i = n 1.2 方法调用 Funct() 5.9 新建对象 New Object() 980 新建数组 New int[10] 3100 从上面可以看出,新建一个对象需要980个单位的时间,是本地赋值时间的980倍,是方 法调用时间的166倍,而若新建一个数组所花费的时间就更多了。 因此,如果要改善应用程序的性能,应尽量减少创建新对象的次数,而这可以通过对 象池技术来实现。 具体对象池技术的基本原理及实现 对象池技术基本原理的核心有两点:缓存和共享,即对于那些被频繁使用的对象,在 使用完后,不立即将它们释放,而是将它们缓存起来,以供后续的应用程序重复使用, 从而减少创建对象和释放对象的次数,进而改善应用程序的性能。事实上,由于对象池 技术将对象限制在一定的数量,也有效地减少了应用程序内存上的开销
选择合适的集合类,将对程序的性能产生很大的影响,下面将一些常用的类进行比较:
Vector和ArrayList Vector和ArrayList在使用上非常相似,都可用来表示一组数量可变的对象应用的集
合,并且可以随机地访问其中的元素。它们的区别如下:
1、Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe),而ArrayList 的方法不是,由于线程的同步必然要影响性能,因此,ArrayList的性能比Vector好. 2、当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而 ArrayList只增加50%的大小,这样ArrayList就有利于节约内存空间。如例1.5