java程序性能优化---2--2.2常用优化组件和方法

2.2常用优化组件和方法
  2.2.1缓冲Buffer
     缓冲区是一块特定的内存区域。开辟缓冲区的目的是通过缓解应用程序上下层之间的差异,
     提高系统的性能。在日常生活中,缓冲的一个典型应用是漏斗。
     上层系统效率很高,比如茶壶的出水速度很快。
                                                漏斗,相当于缓冲区。漏斗口很大,可以及时处理茶壶倒出的水
     下层系统效率很低,如瓶口很细,无法及时将水倒入瓶中。
     对应的瓶口就是性能瓶颈哦。漏斗的初始口很大,并且拥有一定的容量。
     只要漏斗的容量足够大,茶壶里的水很快就能倒完。至此,上层系统完成工作,可以去处理其他业务逻辑。
     而此时,水并未完全进入瓶中,而大部分被积累在漏斗中。这就可以由下层系统慢慢处理,直到水完全进入瓶中。
     漏斗被清空。
     缓冲可以协调上层组件和下层组件的性能差。当上层组件性能优于下层组件时,
     可以有效减少上层组件对下层组件的等待时间。
     
     缓冲最常用的场景就是提高I/O的速度。比如:
     public int CIRCLE = 100_000;
     Writer writer = new FileWriter(new File("file.txt"));
     long begin = System.currentTimeMills();
     for(int i=0;i<CIRCLE;i++){
        writer.write(i);        //写入文件
     }
     writer.close();
     System.out.println("testFileWriter speed:"+(System.currentTimeMills()-begin));


     为进行I/O优化,可以为FIleWriter加上缓冲:
     Writer writer = new BufferedWriter(new FileWriter(new File("file.txt")));//增加了缓冲
     long begin = SYstem.currentTimeMills();
     for(int i=0;i<CIRCLE;i++){
        writer.write(i);
     }
     writer.close();
     System.out.println("testFileWriterBuffer speed:"+(System.currentTimeMills()-begin));




    public class NoBufferMovingCircle extends JApplet implements Runnable{
      Image screenImage = null;
      Thread thread;
      int x= 5;
      int move = 1;


      public void init(){
         screenImage = createImage(230,160);
      }


      public void start(){
         if(thread == null){
	    thread = new Thread(this);
	    thread.start();
	 }
      }


      public void run(){
          try{
	    while(true){
	      x+= move;
	      if((x>105)||(x<5))
	         move *=-1;
              repaint();
	      Thread.sleep(10);
	    }
	  }catch(Exception e){
	     
	  }
      }


      public void drawCircle(Graphics gc){
          Graphics2D g = (Graphics2D) gc;
	  g.setColor(Color.GREEN);
	  g.fillRect(0,0,200,100);
	  g.setColor(Color.red);
	  g.fillOval(x,5,90,90);
      }


      public void paint(Graphics g){             //画一个圆,这里没有缓冲
          g.setColor(Color.white);
	  g.fillRect(0,0,200,100);
	  drawCircle(g);
      }
    }
   
    优化后的
    public class BufferMovingCircle extends NoBufferMovingCircle{
       Graphics doubleBuffer = null; //缓冲区
       public void init(){
         super.init();
	 doubleBuffer = screenImage.getGraphics();
       }


       public void paint(Graphics g){//使用缓冲区,优化原有的paint()方法
         doubleBuffer.setColor(Color.white); //现在内存中画图
	 doubleBuffer.fillRect(0,0,200,100);
	 drawCircle(doubleBuffer);
	 g.drawImage(screenImage,0,0,this); //将buffer一次性显示出来
       }
    }


    
    加上缓冲区后,动画的显示要比之前清晰了许多,并且没有抖动和白光的出现。
    除了性能上的优化,缓冲区还可以作为上层组件和下层组件的一种通信工具。从而,
    将上层组件和下层组件进行解耦,优化设计结构。
    由于I/O操作很容易成为性能瓶颈,所以,尽可能在I/O读写中加入缓冲组件,以提高系统性能。


  2.2.2缓存Cache
     缓存也是一块为提升系统性能而开辟的内存空间。缓存的只要作用是缓存数据处理结果,并提供下次访问使用。
     混存的作用就是将这些来之不易的数据处理结果暂存起来,当再有同样的请求操作时,可以直接从缓存中获取结果,
     以提高系统的响应时间,节约cpu等的资源。
     最简单的缓存可以直接使用HashMap实现。缺点:何时清理无效数据,如何防止缓存数据过多而导致内存溢出等。
     一个稍好的代替方案是直接使用WeakHashMap,它使用弱引用维护一张哈希表,从而避免了潜在的内存溢出问题,
     但是,作为专业的缓存,它的功能也略有不足。
     幸运的是,目前有很多基于Java的缓存框架,比如EHCache,OSCache和JBossCache等。
     EHCache案例:
     public class EHCacheUtil{
          CacheManager manager;
          static{
	    try{
	       //载入EHCache的配置文件,创建CacheManager
	       manager = CacheManager.create(EHCacheUtil.class.getClassLoader().getResourceAsStream(configfile));
	    }catch(CacheException e){
	       e.printStackTrace();
	    }
	  }


	  public static void put(String cachename,Serializable key,Serializable value){
	     manager.getCache(cachename).put(new Element(key,value));
	  }


	  public static Serializable get(String cachename,Serializable key){
	     try{
	       Element e = manager.getCache(cachename).get(key);
	       if(e==null)  return null;
	       return e.getValue(); //取得缓存中的数据
	     }catch(IllegalStateException e){
	       e.printStackTrace();
	     }carch(CacheException e){
	       e.printStackTrace();
	     }
	     return null;
	  }
     }
     使用动态代理无需修改一个逻辑方法的代码,便可以为它加上缓存功能,提高其性能。
     假设有一个可能被频繁调用的方法,它用于对一个整数做因式分解。
     pubic class HeavyMethodDemo{
       public String heavyMethod(int num){
          StringBuffer sb = new StringBuffer();
	  //对 num 进行因式分解,将结果保存在sb中
	  return sb.toString();
       }
     }
     public class CglibHeavyMethodInterceptor implements MethodInterceptor{
       HeavyMethodDemo real = new HeavyMethodDemo();
       @Override
       public Object intercept(Object object,Method method,Object[] arg,MethodProxy proxy)
          throws Throwable{
	    String value = (String)EHCacheUtil.get("cache1",(Serializable) arg[0]);//查询缓存
	    if(value == null){
	       value = real.heavyMethod((Integer)arg[0]); //缓存中未找到结果
	       EHCacheUtil.put("cache1",(Integer)arg[0],value);//缓存计算结果
	    }
	    return value;
	  }
	  .......
     }
     
     public static HeavyMethodDemo newCacheHeavyMethod(){//生成带有缓存功能的类
        Enhancer enhancer = new Enhancer();
	enhancer.setSuperclass(HeavyMethodDemo.class);
	enhancer.setCallback(new CglibHeavyMethodInterceptor());//设置缓存逻辑
	HeavyMethodDemo cglibProxy = (HeavyMethodDemo) enhancer.create();
	return cglibProxy;
     }对比
     public static HeavyMethodDemo newHeavyMethod(){//不带有缓存功能
       return new HeavyMethodDemo();
     }
     public static void main(String[] args){
        HeavyMethodDemo m = newCacheHeavyMethod(); //使用缓存
	long begin = System.currentTimeMills();
	for(int i=0;i<100000;i++)
	    m.heavyMethod(2147483646);//使用缓存时,只需要计算一次
	System.out.println("cache method speed:"+(System.currentTimeMills() - begin));


	m = newHeavyMethod(); //不使用缓存
	begin = System.currentTimeMills();
	for(int i=0;i<100000;i++)
	   m.heavyMethod(2147483646);//不使用缓存,每次都要计算
        System.out.println("no cache method speed:"+(System.currentTimeMills() - begin));


     }






  2.2.3对象复用---池
     对象池化,是目前非常常用的一种系统优化技术。它的核心思想是,如果一个类被频繁请求使用,
     那么不必每次都声称一个实例,可以将这个类的一些实例保存在一个池中,待需要使用的时候直接
     从池中获取。这个池成为对象池。在实现细节上,它可能是一个数组,一个链表或者任何集合类。
     比较常用的比如线程池,数据库连接池。在程序中使用数据库连接池和线程池,可以有效地改善
     系统在高并发下的性能。这是两个非常重要的性能组件。任何对性能敏感的系统,都需要考虑合理
     配置这两个组件。
     目前应用较为广泛的数据库连接池组件有C3P0和Proxool。还有温少的Druid。其中C3P0经常跟Hibernate
     一起使用。
     1.在hibernate.xfg.xml中配置
     2.代码
     public static void main(String[] args){
       try{
         Class.forName("com.mysql.jdbc.Driver");
	 DataSource unpooled = DataSources.unpooledDataSource(
	     "jdbc:mysql://127.0.0.1:3306/test","root","root");
	 DataSource pooled = Datasources.pooledDataSource(unpooled);
	 Connection con = null;
	 Statement stmt = null;
	 ResultSet rs = null;


	 con = pooled.getConnection();            //第一次取得数据库连接
	 System.out.println("con Class Type is:"+con.getClass().getName());
	 Object o1 = getInnter(con);   //取得内部的实际数据库连接
	 System.out.println("Inner con Class Type is:"+o1.getClass().getName());


	 stmt = con.createStatement();
	 rs = stmt.exrcuteQuery("SELECT * FROM user");
	 while(rs.next())
	    System.out.println("Data from DB:"+rs.getString(1));
         rs.close();
	 stmt.close();
	 con.close();


	 Thread.sleep(1000);       //等待连接返回池中
	 con = pooled.getConnection(); //第二次取得数据库连接
	 Object o2 = getInnter(con);
	 if(o1 == o2)
	    System.out.println("o1 and o2 is same object."); //相同,则说明数据库连接被复用
	 stmt = con.createStatement();
	 rs = stmt.executeQuery("SELECT * FROM user");
	 while(rs.next())
	    System.out.println("Data from DB:"+rs.getString(1));
         rs.close();
	 stmt.close();
	 con.close();
       }catch(Exception e){
         e.printStackTrace();
       }
     }
     public static Object getInnter(Object con){
       Object re = null;
       Field f;
       try{
         f = con.getClass().getDeclaredField("inner");
	 f.setAccessible(true);
	 re = f.get(con); //取得内部包装的Connection
	 f.setAccessible(false);
       }catch(Exception e){
       
       }
       return re;
     }


     在JDK中,new操作的效率是相当高的,不需要担心频繁的new操作对系统有性能影响。
     但是new操作时调用的类构造函数可能是非常耗时的,对于这些对象,可以考虑池化。
     在实际开发中,开发人员完全不必自行开发对象池。在Apache中,已经 提供了一个
     Jakarta Commons Pool对象池组件,可以直接使用。
     public interface ObjectPool<T>{
        T borrowObject(); //从对象池中取得一个对象。
void returnObject(T borrowed);//将对象返回给对象池。
     }
     另一个重要的接口是PoolableObjectFactory,它告诉对象池如何创建一个对象,
     如何销毁一个对象.知道对象池在对象的生命周期中如何管理这些对象。
     public interface PoolableObjectFactory<T>{
        T makeObject();  //创建一个新的对象实例。
void activateObject(T obj);//在对象从对象池取出前,会激活这个对象。
void passivateObject(T obj);//在对象返回对象池时被调用。
boolean validateObject(T obj);//判断对象是否可用
void destoryObject(T obj); //对象从对象池中被销毁时,会执行这个方法。
     }
     在Jakarta Commons Pool中,已经内置定义了3个对象池。分别为StackObjectPool,
     GenericObjectPool和SoftReferenceObjectPool。

public class PoolableObjectFactoryDemo implements PoolableObjectFactory{
        private static AtomicInteger counter = new AtomicInteger(0);


	public Object makeObject() throws Exception{ //创建对象
	    Object obj = String.valueOf(counter.getAndIncrement());
	    System.out.println("Create Object "+obj);
	    return obj;
	}


	public void activateObject(Object obj) throws Exception{
	    System.out.println("Before borrow "+obj); //在取出前被调用
	}


	public void passivateObject(Object obj) throws Exception{
	    System.out.println("return "+obj);  //当对象返回池中时被调用
	}


	public boolean validateObject(Object obj){
	    return true;
	}


	public void destoryObject(Object obj)  throws Exception{
	    System.out.println("Destroying Object "+obj);
	}
     }
     public class ObjectPoolDemo{
        static PoolableObjectFactory factory = new PoolableObjectFactoryDemo();
	static ObjectPool pool = new GenericObjectPool(factory);
	private sstatic AtomicInteger endcount = new AtomicInteger(0);
	public static class PoolThread extends Thread{
	  public void run(){
	      Object obj = null;
	      try{
	        for(int i=0;i<100;i++){
		   System.out.println("== "+i+" ==");
		   obj = pool.borrowObject(); //从池中得到对象
		   System.out.println(obj+" is get");//模拟使用对象
		   pool.returnObject(obj); //使用完成后,将对象返回池中
		}
	      }catch(Exception e){
	         e.printStackTrace();
	      }finally{
	         endcount.getAndIncrement();
	      }
	  }
	}
	public static void main(String[] args){
	   new PoolThread().start();
	   new PoolThread().start();
	   new PoolThread().start();
	   try{
	     while(true){
	       if(endcount.get() == 3){  //等待3个线程全部结束
	          pool.close();
		  break;
	       }
	     }
	   }catch(Exception e){}
	}
     }


     
     只有对重量级对象使用对象池技术才能提高系统性能,
     对轻量级的对象使用对象池,可能反而会降低系统性能。
  2.2.4并行代替串行
     Thread.runnerable.concurrent


  2.2.5负载均衡
     典型的Tomcat集群--session共享问题。
     均分session到各个服务器,一台宕机,信息丢失,不可取。
     复制。一台修改,广播n-1,缺点,容易引起网络繁忙,影响系统效率。
     解决方案:在java开源软件中,还有一款跨JVM虚拟机,专门用于分布式缓存的框架。
     Terracotta。使用Terracotta也可以实现Tomcat的Session共享。同时Terracotta也是一个
     成熟的高可用性系统解决方案。
     使用Terracotta,可以在多个Java应用服务器间共享缓存。并且在增加应用服务器时,不会
     像Tomcat那样引起网络风暴,系统负载可以线性增长,是一种可靠的负载均衡方案。
     它可以实现诸如分布式对象共享,分布式缓存,分布式Session等功能。可以作为负载均衡,
     高可用性的解决方案。
     terracotta的session共享。
     terracotta的分布式Cache使用。


  2.2.6时间换空间
     a = a+b;   
     b = a-b;  
     a = a-b;   
     性能优化的关键在于掌握各部分组件的性能平衡点。如果系统CPU资源有空闲,
     但是内存使用紧张,变可以考虑使用时间换取空间的策略,达到整体性能的改良。
     反之,CPU资源紧张,内存资源有空闲,则可以使用空间换时间的策略,提升整体性能。


  2.2.7空间换时间
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值