性能优化
优化分为:设计调优,代码调优,JVM调优,数据库调优,操作系统调优
设计优化:
软件的结构对于系统的整体性能有着重要的影响。优秀的设计结构可以规避很多潜在的性能问题,对系统性能的影响可能远远大于代码的优化。因此需要了解常用的设计模式,组件和设计方法。
善用设计模式
单例模式
确保系统中一个类只产生一个实例,好处是:
对于频繁使用的对象,可以省略创建对象所花费的时间,这对于重量级对象而言,是非常可观的一笔开销;new的次数减少,对系统内存的使用频率也会降低,将减轻GC的压力,缩短GC停顿的时间。
简单的实例:
public class Singleton{ private Singleton(){ System.out.println("Singleton is created");//创建单例的过程可能会比较慢,因为多 //是重量级或占资源较多的对象 } private static Singleton instance = new Singleton(); public static Singleton getInstance(){ return instance; } }
单例类中必须要有private的构造函数。instance和get方法必须是static的。
上面这种单例模式实现方式非常简单,而且十分可靠。它唯一的不足是无法对instance实例做延迟加载。假如创建单例的过程非常慢,而由于instance成员变量是static的,因此在JVM加载单例类时,单例对象就会被建立,如果此时单例类在系统中还扮演其他的角色,那么在任何使用这个单例类的地方都会初始化这个单例变量,而不管是否会被用到,当创建实例很耗时时,会降低系统的性能。例如,单例类作为String工厂,用于创建一些字符串(既用于创建单例singleton,又用于创建String对象)
public class Singleton{ private Singleton(){ System.out.println("Singleton is created"); } private static Singleton instance = new Singleton(); public static Singleton getInstance(){ return instance; } public static void createString(){ System.out.println("createString in Singleton"); } }
当调用createString()时,输出为:
Singleton is created
createString in Singleton
为了让instance在我们需要的时候创建出来,并以此提高系统在相关函数调用时的反应速度,就需要引入延迟加载机制。
public class LazySingleton{ private LazySingleton(){ System.out.println("LazySingleton is created"); } private LazySingleton instance = null; public static synchronized LazySingleton getInstance(){ if(null == instance){ instance=new LazySingleton(); } return instance; } }
上面的单例实现,虽然实现了延迟加载的功能,但和第一种方法比,它引入了同步关键字,因此在多线程的环境中,它的耗时要远远大于第一种。
public void run(){ long beginTime = System.currentTimeMillis(); for(int i=0;i<100000;i++){ Singleton.getInstance(); //LazySingleton.getInstance(); } System.out.println(System.currentTimeMillis()-beginTime); }
开启5个线程运行,在我的电脑上,第一个要接近0ms,第二个接近100ms。
为了使用延迟加载引入的同步关键字反而减低了系统的性能,但可以进行如下的改进:
public class StaticSingleton{ private StaticSingleton(){ System.out.println("StaticSingleton is created"); } private static class SingeltonHolder{ private static StaticSingleton instance = new StaticSingleton(); } public static StaticSingleton getInstance(){ return SingeltonHolder.instance; } }
这个实现是使用内部类来维护单例的实例,当StaticSingleton被加载时,其内部并不会被初始化,所以当JVM加载StaticSingleton时,单例类并不会被实例化,但调用getInstance被调用的时候,才会加载SingeltonHolder,从而初始化实例。由于实例是在类加载是完成的,所以对线程是友好的。这是比较完善的实现。
基本上,用上面的方法可以实现单例。但有例外情况,如利用反射强行调用单例类的私有构造函数,生成多个单例。不过这里会有另外一种情况。
public class SerSingleton implements java.io.Serializable{ String name; private SerSingleton(){ System.out.println("SerSingleton is created"); name="SerSingleton"; } private static SerSingleton instance = new SerSingleton(); public static SerSingleton getInstance(){ return instance; } public static void createString(){ System.out.println("createString in SerSingleton"); } private Object readResolve(){ //组织生成新的实例,总是返回当前的对象 return instance; } }
测试代码:
@Test public void test() throws Exception{ SerSingleton s1 = null; SerSingleton s = SerSingleton.getInstance(); FileOutputStream fos = new FileOutputStream("SerSingleton.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(s); oos.flush(); oos.close(); FileInputStream fis = new FileInputStream("SerSingleton.txt"); ObjectInputStream ois = new ObjectInputStream(fis); s1 = (SerSingleton)ois.readObject(); Assert.assertEquals(s,s1); }
当去掉readResolve()方法时,测试会出错,说明s和s1指向了不同的实例。在反序列化后,生成多个实例。
而加上readResolve()方法后,程序正常退出,说明即使经过反序列化,仍然保持了单例的特征。
事实上,在实现了私有的readResolve后,readObject()就已经形同虚设,直接使用readResolve()来替换原来的返回值,从而在形式上构造了单例。
序列化和反序列化可能会破坏单例。一般来说,对单例进行序列化和反序列化的场景不多见,若存在,要多加注意。