2.2常用优化组件和方法
2.2.1缓冲Buffer
缓冲区是一块特定的内存区域。开辟缓冲区的目的是通过缓解应用程序上下层之间的差异,
提高系统的性能。在日常生活中,缓冲的一个典型应用是漏斗。
上层系统效率很高,比如茶壶的出水速度很快。
漏斗,相当于缓冲区。漏斗口很大,可以及时处理茶壶倒出的水
下层系统效率很低,如瓶口很细,无法及时将水倒入瓶中。
对应的瓶口就是性能瓶颈哦。漏斗的初始口很大,并且拥有一定的容量。
只要漏斗的容量足够大,茶壶里的水很快就能倒完。至此,上层系统完成工作,可以去处理其他业务逻辑。
而此时,水并未完全进入瓶中,而大部分被积累在漏斗中。这就可以由下层系统慢慢处理,直到水完全进入瓶中。
漏斗被清空。
缓冲可以协调上层组件和下层组件的性能差。当上层组件性能优于下层组件时,
可以有效减少上层组件对下层组件的等待时间。
缓冲最常用的场景就是提高I/O的速度。比如:
加上缓冲区后,动画的显示要比之前清晰了许多,并且没有抖动和白光的出现。
除了性能上的优化,缓冲区还可以作为上层组件和下层组件的一种通信工具。从而,
将上层组件和下层组件进行解耦,优化设计结构。
由于I/O操作很容易成为性能瓶颈,所以,尽可能在I/O读写中加入缓冲组件,以提高系统性能。
2.2.2缓存Cache
缓存也是一块为提升系统性能而开辟的内存空间。缓存的只要作用是缓存数据处理结果,并提供下次访问使用。
混存的作用就是将这些来之不易的数据处理结果暂存起来,当再有同样的请求操作时,可以直接从缓存中获取结果,
以提高系统的响应时间,节约cpu等的资源。
最简单的缓存可以直接使用HashMap实现。缺点:何时清理无效数据,如何防止缓存数据过多而导致内存溢出等。
一个稍好的代替方案是直接使用WeakHashMap,它使用弱引用维护一张哈希表,从而避免了潜在的内存溢出问题,
但是,作为专业的缓存,它的功能也略有不足。
幸运的是,目前有很多基于Java的缓存框架,比如EHCache,OSCache和JBossCache等。
EHCache案例:
2.2.3对象复用---池
对象池化,是目前非常常用的一种系统优化技术。它的核心思想是,如果一个类被频繁请求使用,
那么不必每次都声称一个实例,可以将这个类的一些实例保存在一个池中,待需要使用的时候直接
从池中获取。这个池成为对象池。在实现细节上,它可能是一个数组,一个链表或者任何集合类。
比较常用的比如线程池,数据库连接池。在程序中使用数据库连接池和线程池,可以有效地改善
系统在高并发下的性能。这是两个非常重要的性能组件。任何对性能敏感的系统,都需要考虑合理
配置这两个组件。
目前应用较为广泛的数据库连接池组件有C3P0和Proxool。还有温少的Druid。其中C3P0经常跟Hibernate
一起使用。
1.在hibernate.xfg.xml中配置
2.代码
在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。
只有对重量级对象使用对象池技术才能提高系统性能,
对轻量级的对象使用对象池,可能反而会降低系统性能。
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空间换时间
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空间换时间