代码调优不是为了修复bug,而是对正确的代码进行修改,以提高其性能。 代码调优通常是针对小规模的变化,比如针对类,某条代码执行的路径,或者更简单的,几行代码的变化。 代码调优是最后做的工作,其他方面都无法再优化时,再考虑代码调优。 1.调优不会减少代码行数 代码行数与性能之间没有必然的联系 2.调优时不应猜测怎样会提高性能,而应该有明确的目标并衡量结果 通过度量发现热点和瓶颈,代码调优建立在对程序性能的精确度量基础上。 当程序做过某些调整之后,要重新profiling并重新了解需要优化的性能瓶颈,微小的变化能导致优化方向大不相同。在一个环境下提高性能的调优在另一个环境下可能降低性能。 3.不要边写程序边调优 在没有完整程序之前,无法获知性能瓶颈 在开发阶段进行调优,容易忽视其他更重要的质量指标 4.性能从不是追求的第一目标,正确性比性能更重要 调优不是代码性能优化的第一选择。
代码调优的过程: 备份→通过度量发现热点瓶颈→分析原因,评判代码调优的必要性→调优→每次调优后都要重新度量→若无效果或负效果,则回滚
很多。I/O,paging,operators,object creation,GC…
强制client只能创建一个object实例,避免因为new操作所带来的时空性能(尤其是GC)的损失,也便于复用。 优点:只能获取到一个实例 减小命名空间(?) 通过类中封装的代码确保对象的重复使用,客户端易于操作 重复使用意味着更好的性能: 节约了创建新对象的时间,节省内存
单例模式: 设置静态变量来存储单一实例对象 将构造器设置为private,从而client无法new 在构造器中new新实例 提供静态方法来获取单一实例对象
public class Singleton { private static final Singleton instance = new Singleton(); private Singleton() {...} public static Singleton getInstance() { return instance; } // other operations and data }在装载到程序时创建类
Lazy Load:在需要的时候再new,而非提前构造出来
public class Singleton { private static final Singleton instance = null; private Singleton() {...} public static Singleton getInstance() { if (instance == null) instance = new Singleton(); return instance; } // other operations and data }在应用中不同部分共享使用objects,降低大量objects带来的时空代价。 每个对象有内部特征和外部特征,外部特征需要在不同场合分别指派/计算其值。 ConcreteFlyweight里存储的状态是内部特征 FlyweightFactory:创建,管理flyweight对象。客户端请求flyweight对象时,FlyweightFactory返回或创建一个实例。 例: 定义“外特征”:
public enum Color {Red, Green, Blank, Blue, Yellow}可共享对象的抽象接口:
public interface IAlien { String Shape = null; //intrinsic state String getShape(); Color getColor(int madLevel); //extrinsic state }两种具有不同内特征的共享对象:
class LargeAlien implements IAlien{ private String shape = "Large Shape"; public String getShape() { return shape; } public Color getColor(int madLevel) { if (madLevel == 0) return Color.Green; else if (madLevel == 1) return Color.Red; else return Color.Blue; } } class LittleAlien implements IAlien { private String shape = "Little Shape"; public String getShape() { return shape; } public Color getColor(int madLevel) { if (madLevel == 0) return Color.Red; else if (madLevel == 1) return Color.Blue; else return Color.Green; } } public class AlienFactory { private Map<String, IAlien> list = new HashMap<>(); public void SaveAlien(String index, IAlien alien) { list.put(index,alien); } public IAlien GetAlien(String index) { return list.get(index); } }client:
AlienFactory factory = new AlienFactory(); factory.SaveAlien("LargeAlien", new LargeAlien()); factory.SaveAlien("LittleAlien", new LittleAlien()); IAlien a = factory.GetAlien("LargeAlien"); IAlien b = factory.GetAlien("LittleAlien"); System.out.println("Showing intrinsic states..."); System.out.println("Alien of type LargeAlien is " + a.getShape()); System.out.println("Alien of type LittleAlien is " + b.getShape()); System.out.println("Showing extrinsic states..."); System.out.println("Alien of type LargeAlien is " + a.getColor(0).toString()); System.out.println("Alien of type LargeAlien is " + a.getColor(1).toString()); System.out.println("Alien of type LittleAlien is " + b.getColor(0).toString()); System.out.println("Alien of type LittleAlien is " + b.getColor(1).toString()); FlyweightSingleton同一事物有不同表现形式统一用一个实例表示immutablemutable当直接new对象的时空代价高时,通过clone而不是new来创建object 对于普通ADT,clone不会带来性能的提升。 需要override clone()方法,并将可见性设为public Object.clone()是protected:它可以被同包(java.lang)下以及它(java.lang.Object)的子类访问。 自定义类无法直接使用Object.clone():没有访问权限(invisible),故需要override。 (??) 如果在没有实现Cloneable 接口的实例上调用 Object 的 clone 方法,则会导致抛出 CloneNotSupportedException 异常。
Shape clonedShape1 = (Shape) rectangle.clone(); Shape clonedShape2 = (Shape) square.clone();clone()方法返回的是object类型的对象,需要强转 拷贝: 引用拷贝:两个reference变量指向同一个对象 对象拷贝:两个reference变量指向不同对象,包括深拷贝和浅拷贝 浅拷贝:使用一个已知实例的成员变量对新创建实例的成员变量逐个赋值(缺省) 深拷贝:复制对象的所有非引用成员变量值,并为引用类型的成员变量创建新的实例,并且初始化为原对象的值。
对象复用 把一组已初始化的可用对象作为pool,而不是需要时创建对象,不需要时再回收对象。 客户端从pool中请求对象,在返回的对象上进行操作 操作结束后,将对象返回到pool中,而不是丢弃它 代价:原本可被GC的对象,现在要留在pool中,导致内存浪费——用空间换时间 Singleton和Flyweight本质也都是Object Pool
不保留对象的多个拷贝,仅保留少量Object 字符串常量池 例:只用到少量integer对象的情况:将对象规范化
public class IntegerManager { public static final Integer ZERO = new Integer(0); public static final Integer ONE = new Integer(1); public static final Integer TWO = new Integer(2); …… public static final Integer NINE = new Integer(9); public static final Integer TEN = new Integer(10); }如果用普通方式创建Integer对象,会重复创建很多对象,而且比较的时候要用integerValue()比较 规范化之后减少了new对象的次数和垃圾回收的次数,且可以通过地址比较
规范化的技术也常用于用int取代其他常量object 例:用这样的枚举类型取代字符串"male",“female”
public interface GENDER { public static final int FEMALE=1; public static final int MALE=2; }枚举类型的时空性能更好:占内存小,通过地址比较,比较块
canonicalization techniques:减少创建object的数量,避免GC的代价。 pooling technique:通过复用对象减少了创建对象,从而减少GC;持续使用已分配内存的对象,减少内存释放。 另一项技术:避免使用不需要的对象
String string = “55”; 在String pool中分配,无需GC int theInt = new Integer(string).intValue(); 创建一个Integer int theInt = Integer.parseInt(string); 无需创建对象尽可能使用简单数据类型,对类的成员变量也是如此。 内存中基本数据类型的变量也需要回收,但回收代价低:它和它所属于的对象同时被回收,所以影响小 例如:如果一个对象有一个int类型的成员变量,回收该对象的实例时GC一次;如果该对象有一个Integer类型的成员变量,回收该对象的实例时GC两次(Integer和这个对象本身)。 局部的简单数据类型的变量在栈中存储,不需GC。 使用简单数据类型替代复杂数据类型: 能用int存储诸如"1492", "1997"的数据就不要用String 用int/long代替Date对象 (但转换需要的计算可能花费更多时间) 其他建议: 1.减少临时对象的使用,尤其是在循环里 2.优先使用StringBuffer而不是用“+”连接字符串 3.分辨哪些方法直接在对象上操作,而哪些方法返回的是对象的复制 4.使用基本数据类型时,避免使用处理Object的类,例如,没有必要把ints包装成Integers,再使用Vector,而是可以实现一个直接存储ints的IntVector类
(前三点没有看懂,哪位读者朋友看懂了麻烦联系我一下给我讲讲) 对于无法避免创建大量对象的情况,可以把创建对象的时间转移到程序中有空闲的时候,并储存这些对象直到它们被使用。 Late (Lazy) Initialization:在第一次使用对象时才创建对象 通过检查该对象是否为空来判断它是否被初始化
public getSomething(){ if (something = = null) something = defaultSomething( ); return something; }字符串常量池是堆中的一块存储区域,保存对字符串对象的引用。 创建String对象的方式: 1.String s = “java”; JVM在字符串常量池中查找“java”,如果找到,取它的引用,如果没找到,在字符串常量池中创建“java”。 2.String s = new String(“java”); 在堆中创建对象。
String s1 = "java"; String s2 = "java"; String s3 = new String("java");如果字符串可以在编译阶段确定,使用“+”效率更高 如果字符串在运行阶段确定,使用StringBuffer效率更高 使用不复制字符串中字符的,效率比较高的方法 避免使用使用复制字符串中字符的,效率比较低的方法
1.在能得出答案时自动停止判断 (1)short-circuit evaluation:如“if ( x>5 && x<10 )”。也是条件操作的一部分,相当于:
if (x>5) { if (x <10 ) { …} }(2)循环中,如果已经得出结果,跳出循环 2.用查表替代复杂的逻辑判断 3.使用Lazy Evalution: avoids doing any work until the work is needed 4.将判断外提:循环里的分支判断会在循环每次执行时都执行,如果循环过程中分支判断不变,可以把判断提到循环外面 5.减少数组维度