java性能优化:Java性能的优化



Java性能优化


Java性能优化

(来源:http://www.ccw.com.cn)

Java在 9十年代中期出现以后在赢得赞叹同时也引来了些批评赢得赞叹主要是Java跨平台操作性即所谓”WriteOnce,RunAnywhere”.但由于Java性能和运行效率同C相比仍然有很大差距从而引来了很多批评
对于服务器端应用由于不大涉及到界面设计和频繁重启Java性能问题看似不大明显从而些Java技术如JSP,Servlet,EJB等在服务器端编程方面得到了很大应用但实际上Java性能问题在服务器端依然存在下面我将分 4个方面来讨论Java性能和执行效率以及提高Java性能些思路方法
.有关性能基本知识
1.性能定义
在我们讨论怎样提高Java性能的前我们需要明白“性能“真正含义我们般定义如下 5个方面作为评判性能标准
1)运算性能----哪个算法执行性能最好
2)内存分配----需要分配多少内存运行时效率和性能最高
3)启动时间----启动需要多少时间
4)可伸缩性-----在用户负载过重情况下表现
5)性能认识------用户怎样才能认识到性能
对于区别应用对性能要求也区别例如大部分应用在启动时需要较长时间从而对启动时间要求有所降低;服务器端应用通常都分配有较大内存空间所以对内存要求也有所降低但是这并不是所这两方面性能可以被忽略其次算法性能对于那些把商务逻辑运用到事务性操作应用来讲非常重要来讲对应用要求将决定对各个性能优先级
2.怎样才能提高JAVA性能
提高JAVA性能般考虑如下 4个主要方面:
(1)设计思路方法和模式
个良好设计能提高性能点不仅适用于JAVA也适用也任何编程语言它充分利用了各种资源如内存CPU,高速缓存Cache对象缓冲池及多线程从而设计出高性能和可伸缩性强系统
当然为了提高性能而改变原来设计是比较困难但是性能重要性常常要高于设计上带来变化因此在编程开始的前就应该有个好设计模型和思路方法
(2)JAVA布署环境
JAVA布署环境就是指用来解释和执行JAVA字节码技术般有如下 5种即解释指令技术(InterpreterTechnology)及时编译技术(JustInTimeCompilierTechnology),适应性优化技术(AdaptiveOptimizationTechnology),动态优化提前编译为机器码技术(DynamicOptimization,AheadOfTimeTechnology)和编译为机器码技术(TranslatorTechnology).
这些技术般都通过优化线程模型调整堆和栈大小来优化JAVA性能在考虑提高JAVA性能时首先要找到影响JAVA性能瓶颈(BottleNecks)在确认了设计合理性后应该调整JAVA布署环境通过改变些参数来提高JAVA应用性能具体内容见第 2节
(3)JAVA应用实现
当讨论应用性能问题时大多数员都会考虑代码这当然是对当更重要是要找到影响性能瓶颈代码为了找到这些瓶颈代码我们般会使用些辅助工具如Jprobe,Optimizit,Vtune以及些分析工具如TowerJPerformance等这些辅助工具能跟踪应用中执行每个或思路方法所消耗掉时间从而改善性能
(4)硬件和操作系统
为了提高JAVA应用性能而采用跟快CPU和更多内存并认为这是提高性能思路方法但事实并非如此实战经验和事实证明只有遭到了应用性能瓶颈从而采取适当得思路方法如设计模式布署环境操作系统调整才是最有效
3.中通常性能瓶颈
所有应用都存在性能瓶颈为了提高应用性能就要尽可能减少瓶颈以下是在JAVA中经常存在性能瓶颈


了解了这些瓶颈后就可以有针对性减少这些瓶颈从而提高JAVA应用性能
4.提高JAVA性能步骤
为了提高JAVA性能需要遵循如下 6个步骤
a)明确对性能具体要求
在实施个项目的前必须要明确该项目对于性能具体要求如:这个应用要支持5000个并发用户并且响应时间要在5秒钟的内但同时也要明白对于性能要求不应该同对其他要求冲突
b)了解当前性能
你应该了解你应用性能同项目所要求性能的间差距通常指标是单位时间内处理数和响应时间有时还会比较CPU和内存利用率
c)找到性能瓶颈
为了发现性能瓶颈通常会使用些分析工具如:TowerJApplicationPerformanceAnalyzer或VTune来察看和分析堆栈中各个元素消耗时间从而正确找到并改正引起性能降低瓶颈代码从而提高性能这些工具还能发现诸如过多异常处理垃圾回收等潜在问题
d)采取适当措施来提高性能
找到了引起性能降低瓶颈代码后我们就可以用前面介绍过提高性能 4个方面即设计模式JAVA代码实现布署JAVA环境和操作系统来提高应用性能具体内容将在下面内容中作详细介绍说明
e)只进行某方面修改来提高性能
次只改变可能引起性能降低方面然后观察性能是否有所提高而不应该次改变多个方面这样你将不知道到底哪个方面改变提高了性能哪个方面没有即不能知道瓶颈在哪
f)返回到步骤c,继续作类似工作直达到要求性能为止



2.JAVA布署环境和编译技术
 开发JAVA应用首先把JAVA编译为和平台无关字节码这些字节码就可以被各种基于JVM技术所执行这些技术主要分为两个大类即基于解释技术和基于提前编译为本地码技术其示意图如下:


具体可分为如下 5类:  
a)解释指令技术
其结构图和执行过程如下:


 JAVA编译器首先把JAVA源文件编译为字节码这些字节码对于JAVA虚拟机(JVM)来讲就是机器指令码然后JAVA解释器不断循环取出字节码进行解释并执行
 这样做优点是可以实现JAVA语言跨平台同时生成字节码也比较紧凑JAVA些优点如安全性动态性都得保持;但缺点是省生成字节码没有经过什么优化同全部编译好本地码相比速度比较慢
b)及时编译技术(JustInTime)
  及时编译技术是为了解决指令解释技术效率比较低速度比较慢情况下提出其结构图如下所示


其主要变化是在JAVA执行的前又JIT编译器把JAVA字节码编译为机器码从而在运行时直接执行机器码而不用对字节码进行解释同时对代码也进行了部分优化
这样做优点是大大提高了JAVA性能同时由于编译结果并不在运行间保存因此也节约了存储空间了加载时间;缺点是由于JIT编译器对所有代码都想优化因此也浪费了很多时间
IBM和SUN公司都提供了相关JIT产品
c)适应性优化技术(AdaptiveOptimizationTechnology)
同JIT技术相比适应性优化技术并不对所有字节码进行优化它会跟踪运行成个过程从而发现需要优化代码对代码进行动态优化对优化代码采取80/20策略从理论上讲运行时间越长代码就越优化其结构图如下:


其优点是适应性优化技术充分利用了执行时信息发行性能瓶颈从而提高性能;其缺点是在进行优化时可能会选择不当发而降低了性能
其主要产品又IBM,SUNHotSpot.
d)动态优化提前编译为机器码技术(DynamicOptimization,AheadOfTime)
动态优化技术充分利用了JAVA源码编译字节码编译动态编译和静态编译技术其输入时JAVA原码或字节码而输出是经过高度优化可执行代码和个来动态库混合(Window中是DLL文件UNIX中是共享库.a.so文件)其结构如下:


其优点是能大大提高性能;缺点是破坏了JAVA可移植性也对JAVA安全带来了隐患
其主要产品是TowerJ3.0.

3.优化JAVA设计和编码提高JAVA性能些思路方法
通过使用些前面介绍过辅助性工具来找到瓶颈然后就可以对瓶颈部分代码进行优化般有两种方案:即优化代码或更改设计思路方法我们般会选择后者不去以下代码要比些优化代码更能提高性能个设计良好能够精简代码从而提高性能
下面将提供些在JAVA设计和编码中为了能够提高JAVA性能而经常采用些思路方法和窍门技巧
1.对象生成和大小调整
JAVA设计中个普遍问题就是没有好好利用JAVA语言本身提供从而常常会生成大量对象(或例子)由于系统不仅要花时间生成对象以后可能还需花时间对这些对象进行垃圾回收和处理因此生成过多对象将会给性能带来很大影响
例1:有关String,StringBuffer+和append
JAVA语言提供了对于String类型变量操作但如果使用不当会给性能带来影响如下面语句:
Stringname=String(“HuangWeiFeng”);
.out.prln(name+”ismyname”);
看似已经很精简了其实并非如此为了生成 2进制代码要进行如下步骤和操作
(1)生成新String(STR_1);
(2)复制该
(3)加载串常量”HuangWeiFeng”(STR_2);
(4)构架器(Constructor);
(5)保存该串到中(从位置0开始)
(6)从java.io.PrStream类中得到静态out变量
(7)生成新串缓冲变量StringBuffer(STR_BUF_1);
(8)复制该串缓冲变量
(9)串缓冲构架器(Constructor);
(10)保存该串缓冲到中(从位置1开始)
(11)以STR_1为参数串缓冲(StringBuffer)类中append思路方法
(12)加载串常量”ismyname”(STR_3);
(13)以STR_3为参数串缓冲(StringBuffer)类中append思路方法
(14)对于STR_BUF_1执行toString命令
(15)out变量中prln思路方法输出结果
由此可以看出这两行简单代码就生成了STR_1,STR_2,STR_3,STR_4和STR_BUF_1 5个对象变量这些生成例子般都存放在堆中堆要对所有类超类例子进行同时还要类极其每个超类构架器而这些操作都是非常消耗系统资源因此对对象生成进行限制是完全有必要
经修改上面代码可以用如下代码来替换
StringBuffername=StringBuffer(“HuangWeiFeng”);
.out.prln(name.append(“ismyname.”).toString);
系统将进行如下操作
(1)生成新串缓冲变量StringBuffer(STR_BUF_1);
(2)复制该串缓冲变量
(3)加载串常量”HuangWeiFeng”(STR_1);
(4)串缓冲构架器(Constructor);
(5)保存该串缓冲到中(从位置1开始)


(6)从java.io.PrStream类中得到静态out变量
(7)加载STR_BUF_1;
(8)加载串常量”ismyname”(STR_2);
(9)以STR_2为参数串缓冲(StringBuffer)例子中append思路方法
(10)对于STR_BUF_1执行toString命令(STR_3)
(11)out变量中prln思路方法输出结果
由此可以看出经过改进后代码只生成了 4个对象变量:STR_1,STR_2,STR_3和STR_BUF_1.你可能觉得少生成个对象不会对性能有很大提高但下面代码段2执行速度将是代码段12倍代码段1生成了 8个对象而代码段2只生成了 4个对象
代码段1:
Stringname=StringBuffer(“HuangWeiFeng”);
name”ismy”;
name”name”;
代码段2:
StringBuffername=StringBuffer(“HuangWeiFeng”);
name.append(“ismy”);
name.append(“name.”).toString;
因此充分利用JAVA提供来优化对提高JAVA性能时非常重要.其注意点主要有如下几方面;
(1)尽可能使用静态变量(StaticClassVariables)
如果类中变量不会随他例子而变化就可以定义为静态变量从而使他所有例子都共享这个变量
例:
publicfoo
{
SomeObjectso=SomeObject;
}
就可以定义为:
publicfoo
{
SomeObjectso=SomeObject;
}
(2)不要对已生成对象作过多改变
对于些类(如:String类)来讲宁愿在重新生成个新对象例子而不应该修改已经生成对象例子
例:
Stringname=”Huang”;
name=”Wei”;
name=”Feng”;
上述代码生成了 3个String类型对象例子而前两个马上就需要系统进行垃圾回收处理如果要对串进行连接操作性能将得更差系统将不得为此生成更多得临时变量如上例1所示
(3)生成对象时要分配给它合理空间和大小
JAVA中很多类都有它默认空间分配大小对于StringBuffer类来讲默认分配空间大小是16个如果在中使用StringBuffer空间大小不是16个那么就必须进行正确
(4)避免生成不太使用或生命周期短对象或变量
对于这种情况因该定义个对象缓冲池以为管理个对象缓冲池开销要比频繁生成和回收对象开销小
(5)只在对象作用范围内进行
JAVA允许在代码任何地方定义和化对象这样就可以只在对象作用范围内进行从而节约系统开销
例:
SomeObjectso=SomeObject;
If(x1)then
{
Foo=so.getXX;
}
可以修改为:
(x1)then
{
SomeObjectso=SomeObject;
Foo=so.getXX;
}
2.异常(Exceptions)
JAVA语言中提供了try/catch来发方便用户捕捉异常进行异常处理但是如果使用不当也会给JAVA性能带来影响因此要注意以下两点
(1)避免对应用逻辑使用try/catch
如果可以用,while等逻辑语句来处理那么就尽可能不用try/catch语句
(2)重用异常
在必须要进行异常处理时要尽可能重用已经存在异常对象以为在异常处理中生成个异常对象要消耗掉大部分时间
3.线程(Threading)
个高性能应用般都会用到线程线程能充分利用系统资源在其他线程等待硬盘或网络读写而时能继续处理和运行但是对线程运用不当也会影响性能
例2:正确使用Vector类
Vector主要用来保存各种类型对象(包括相同类型和区别类型对象)但是在些情况下使用会给带来性能上影响这主要是由Vector类两个特点所决定Vector提供了线程安全保护功能即使Vector类中许多思路方法同步但是如果你已经确认你应用是单线程这些思路方法同步就完全不必要了第 2在Vector查找存储各种对象时常常要花很多时间进行类型匹配而当这些对象都是同类型时这些匹配就完全不必要了因此有必要设计个单线程保存特定类型对象类或集合来替代Vector类.用来替换如下(StringVector.java):
publicStringVector
{
privateStringdata;
privatecount;
publicStringVector{this(10);//defaultsizeis10}
publicStringVector(initialSize)
{
data=String[initialSize];
}
publicvoidadd(Stringstr)
{
//ignorenulls
(strnull){;}
ensureCapacity(count+1);
data[count]=str;
}

privatevoidensureCapacity(minCapacity)
{
oldCapacity=data.length;
(minCapacity>oldCapacity)
{
StringoldData=data;
Capacity=oldCapacity*2;
data=String[Capacity];
..gif' />copy(oldData,0,data,0,count);


}
}
publicvoidremove(Stringstr)
{
(strnull){//ignorenullstr}
for(i=0;i<count;i)
{
//checkforamatch
(data[i].equals(str))
{
..gif' />copy(data,i+1,data,i,count-1);//copydata
//allowpreviouslyvalid.gif' />elementbegc\'d
data[--count]=null;
;
}
}
}
publicfinalStringgetStringAt(index){
(index<0){null;}
(index>count)
{
null;//indexis>#s
}
{data[index];//indexisgood}
}
/****************StringVector.java*****************/
因此代码:
VectorStrings=Vector;
Strings.add(“One”);
Strings.add(“Two”);
StringSecond=(String)Strings.elementAt(1);
可以用如下代码替换:
StringVectorStrings=StringVector;
Strings.add(“One”);
Strings.add(“Two”);
StringSecond=Strings.getStringAt(1);
这样就可以通过优化线程来提高JAVA性能用于测试如下(TestCollection.java):
importjava.util.Vector;
publicTestCollection
{
publicvoid(Stringargs)
{
TestCollectioncollect=TestCollection;
(args.length0)
{
.out.prln(
\"Usage:javaTestCollection[vector|vector]\");
.exit(1);
}
(args[0].equals(\"vector\"))
{
Vectorstore=Vector;
longstart=.currentTimeMillis;
for(i=0;i<1000000;i)
{
store.addElement(\"\");
}
longfinish=.currentTimeMillis;
.out.prln((finish-start));
start=.currentTimeMillis;
for(i=0;i<1000000;i)
{
Stringresult=(String)store.elementAt(i);
}
finish=.currentTimeMillis;
.out.prln((finish-start));
}
(args[0].equals(\"vector\"))
{
StringVectorstore=StringVector;
longstart=.currentTimeMillis;
for(i=0;i<1000000;i){store.add(\"\");}
longfinish=.currentTimeMillis;
.out.prln((finish-start));
start=.currentTimeMillis;
for(i=0;i<1000000;i){
Stringresult=store.getStringAt(i);
}
finish=.currentTimeMillis;
.out.prln((finish-start));
}
}
}
/****************TestCollection.java*****************/
测试结果如下(假设标准时间为1越小性能越好):


有关线程操作要注意如下几个方面
(1)防止过多同步
如上所示不必要同步常常会造成性能下降因此如果是单线程定不要使用同步
(2)同步思路方法而不要同步整个代码段
   对某个思路方法或进行同步比对整个代码段进行同步性能要好
(3)对每个对象使用多”锁”机制来增大并发
般每个对象都只有个”锁”这就表明如果两个线程执行个对象两个区别同步思路方法时会发生”死锁”即使这两个思路方法并不共享任何资源为了避免这个问题可以对个对象实行”多锁”机制如下所示:
foo
{
privatevar1;
privateObjectlock1=Object;
privatevar2;
privateObjectlock2=Object;
publicvoidincrement1


{
synchronized(lock1)
{
var1;
}
}
publicvoidincrement2
{
synchronized(lock2)
{
var2;
}
}
}
4.输入和输出(I/O)
输入和输出包括很多方面但涉及最多是对硬盘网络或数据库读写操作对于读写操作又分为有缓存Cache和没有缓存Cache;对于数据库操作又可以有多种类型JDBC驱动器可以选择但无论怎样都会给性能带来影响因此需要注意如下几点:
(1)使用输入输出缓冲
   尽可能多使用缓存Cache但如果要经常对缓存Cache进行刷新(flush),则建议不要使用缓存Cache
(2)输出流(OutputStream)和Unicode
   当时用OutputStream和Unicode串时Write类开销比较大它要实现Unicode到字节()转换.因此如果可能话,在使用Write类的前就实现转换或用OutputStream类代替Writer类来使用
(3)当需序列化时使用transient
   当序列化个类或对象时对于那些原子类型(atomic)或可以重建原素要表识为transient类型这样就不用每次都进行序列化如果这些序列化对象要在网络上传输小小改变对性能会有很大提高  
(4)使用高速缓存Cache(Cache)
   对于那些经常要使用而又不大变化对象或数据可以把它存储在高速缓存Cache中这样就可以提高访问速度点对于从数据库中返回结果集尤其重要
(5)使用速度快JDBC驱动器(Driver)
   JAVA对访问数据库提供了 4种思路方法这其中有两种是JDBC驱动器种是用JAVA外包本地驱动器;另种是完全JAVA驱动器具体要使用哪种得根据JAVA布署环境和应用本身来定
5.些其他经验和窍门技巧
(1)使用局部变量
(2)避免在同个类中动过或思路方法(get或)来设置或变量
(3)避免在循环中生成同个变量或(参数变量也样)
(4)尽可能使用,final,private等关键字
(5)当复制大量数据时使用..gif' />copy命令





Tags:  性能优化 为提高性能而优化 java性能优化

延伸阅读

最新评论

发表评论