入门客AI创业平台(我带你入门,你带我飞行)
博文笔记

java性能调优(转载)

创建时间:2016-05-05 投稿人: 浏览次数:97
1.用new关键词创建类的实例时,构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口,我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。 

在使用设计模式(Design Pattern)的场合,如果用Factory模式创建对象,则改用clone()方法创建新的对象实例非常简单。例如,下面是Factory模式的一个典型实现: 
public static Credit getNewCredit() {return new Credit();}  改进后的代码使用clone()方法,如下所示:private static Credit BaseCredit = new Credit();public static Credit getNewCredit() {return (Credit) BaseCredit.clone();} 


面的思路对于数组处理同样很有用。 
2. 使用非阻塞I/O 

版本较低的JDK不支持非阻塞I/O API。为避免I/O阻塞,一些应用采用了创建大量线程的办法(在较好的情况下,会使用一个缓冲池)。这种技术可以在许多必须支持并发I/O流的应用中见到,如Web服务器、报价和拍卖应用等。然而,创建Java线程需要相当可观的开销。 

3. 慎用异常 

异常对性能不利。抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本地(Native)方法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,VM就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。异常只能用于错误处理,不应该用来控制程序流程。 

4. 不要重复初始化变量 

默认情况下,调用类的构造函数时, Java会把变量初始化成确定的值:所有的对象被设置成null,整数变量(byte、short、int、long)设置成0,float和double变量设置成0.0,逻辑值设置成false。当一个类从另一个类派生时,这一点尤其应该注意,因为用new关键词创建一个对象时,构造函数链中的所有构造函数都会被自动调用。

5. 尽量指定类的final修饰符 

带有final修饰符的类是不可派生的。在Java核心API中,有许多应用final的例子,例如java.lang.String。为String类指定final防止了人们覆盖length()方法。另外,如果指定一个类为final,则该类所有的方法都是final。Java编译器会寻找机会内联(inline)所有的final方法(这和具体的编译器实现有关)。此举能够使性能平均提高50%。 

6. 尽量使用局部变量 

调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快。其他变量,如静态变量、实例变量等,都在堆(Heap)中创建,速度较慢。另外,依赖于具体的编译器/JVM,局部变量还可能得到进一步优化。 

7. 乘法和除法 

考虑下面的代码: 
for (val = 0; val < 100000; val +=5) { alterX = val * 8; myResult = val * 2; }  用移位操作替代乘法操作可以极大地提高性能。下面是修改后的代码:for (val = 0; val < 100000; val += 5) { alterX = val << 3; myResult = val << 1; } 


修改后的代码不再做乘以8的操作,而是改用等价的左移3位操作,每左移1位相当于乘以2。相应地,右移1位操作相当于除以2。值得一提的是,虽然移位操作速度快,但可能使代码比较难于理解,所以最好加上一些注释。 
3. 选择合适的引用机制 

在典型的JSP应用系统中,页头、页脚部分往往被抽取出来,然后根据需要引入页头、页脚。当前,在JSP页面中引入外部资源的方法主要有两种:include指令,以及include动作。 

include指令:例如 
<%@ include file="copyright.html" %> 


该指令在编译时引入指定的资源。在编译之前,带有include指令的页面和指定的资源被合并成一个文件。被引用的外部资源在编译时就确定,比运行时才确定资源更高效。 

include动作:例如 
<jsp:include page="copyright.jsp" /> 


该动作引入指定页面执行后生成的结果。由于它在运行时完成,因此对输出结果的控制更加灵活。但时,只有当被引用的内容频繁地改变时,或者在对主页面的请求没有出现之前,被引用的页面无法确定时,使用include动作才合算。 

4. 在spring中对orm层的动作设置只读属性 

将 (只对数据库进行读取的操作) 设置只读属性 
10. Servlet与内存使用 

许多开发者随意地把大量信息保存到用户会话之中。一些时候,保存在会话中的对象没有及时地被垃圾回收机制回收。从性能上看,典型的症状是用户感到系统周期性地变慢,却又不能把原因归于任何一个具体的组件。如果监视JVM的堆空间,它的表现是内存占用不正常地大起大落。解决这类内存问题主要有二种办法。第一种办法是,在所有作用范围为会话的Bean中实现HttpSessionBindingListener接口。这样,只要实现valueUnbound()方法,就可以显式地释放Bean使用的资源。 

另外一种办法就是尽快地把会话作废。大多数应用服务器都有设置会话作废间隔时间的选项。另外,也可以用编程的方式调用会话的setMaxInactiveInterval()方法,该方法用来设定在作废会话之前,Servlet容器允许的客户请求的最大间隔时间,以秒计算。 

11. HTTP Keep-Alive 

Keep-Alive功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接。市场上的大部分Web服务器,包括iPlanet、IIS和Apache,都支持HTTP Keep-Alive。对于提供静态内容的网站来说,这个功能通常很有用。但是,对于负担较重的网站来说,这里存在另外一个问题:虽然为客户保留打开的连接有一定的好处,但它同样影响了性能,因为在处理暂停期间,本来可以释放的资源仍旧被占用。当Web服务器和应用服务器在同一台机器上运行时,Keep-Alive功能对资源利用的影响尤其突出。 

12. JDBC与Unicode 

想必你已经了解一些使用JDBC时提高性能的措施,比如利用连接池、正确地选择存储过程和直接执行的SQL、从结果集删除多余的列、预先编译SQL语句,等等。除了这些显而易见的选择之外,另一个提高性能的好选择可能就是把所有的字符数据都保存为Unicode(代码页13488)。Java以Unicode形式处理所有数据,因此,数据库驱动程序不必再执行转换过程。但应该记住:如果采用这种方式,数据库会变得更大,因为每个Unicode字符需要2个字节存储空间。另外,如果有其他非Unicode的程序访问数据库,性能问题仍旧会出现,因为这时数据库驱动程序仍旧必须执行转换过程。 
13. JDBC与I/O 

如果应用程序需要访问一个规模很大的数据集,则应当考虑使用块提取方式。默认情况下,JDBC每次提取32行数据。举例来说,假设我们要遍历一个5000行的记录集,JDBC必须调用数据库157次才能提取到全部数据。如果把块大小改成512,则调用数据库的次数将减少到10次。在一些情形下这种技术无效。例如,如果使用可滚动的记录集,或者在查询中指定了FOR UPDATE,则块操作方式不再有效。 


Java代码优化--尽可能地使用stack(栈)变量(方法内部的局部变量)            
Java程序包含了大量的对象,我们需要了解它们是从哪里被访问的,变量存储于何处对程序的性能有显著的影响--尤其是某些需要被频繁访问的变量。 
我们写一个Java类,在其内部方法中定义的局部变量或对象是存储在stack(堆栈)中的,且JVM是一种stack-based的,因此访问和操纵stack中的数据时性能最佳。而Java类的instance变量(这个类的field)和static变量是在constant pool(常量池)中存储和得到访问的。constant pool中保存了所有的符号引用(symbolic references),指向所有型别(types)、值域(field),以及每个型别所使用的所有函数(mothods)。访问instance和static变量时,由于它们存放于constant pool中,所以JVM需要使用更多更耗时的操作码(分析程序生成的bytecode可以看出来)来访问它们。 
下面给出一段代码示例,对比后说明怎么尽可能地使用stack变量: 
package test; 
public class StackVars { 
    private int x;    // instance变量 
    private static int staticX; //static 变量 
    public void stackAccess(int val) {  //访问和操作stack变量j 
        int j = 0; 
        for (int i = 0; i < val; i++) { 
            j += 1; 
        } 
    } 
    public void instanceAccess(int val) {//访问和操作instance变量x 
        for (int i = 0; i < val; i++) { 
            x += 1; 
        } 
    } 
    public void staticAccess(int val) {//访问和操作static变量staticX 
        for (int i = 0; i < val; i++) { 
            staticX += 1; 
        } 
    } 

经测试,发现运行instanceAccess()和staticAccess()方法的时间大约相同,但却比运行stackAccess()方法慢了2~3倍。因此我们对instanceAccess()、staticAccess()两个方法的代码作以下调整,以得到更快的性能: 
public void instanceAccess(int val) {//访问和操作instance变量x 
        int tempX=x; 
        for (int i = 0; i < val; i++) { 
            tempX += 1; 
        } 
        x=tempX; 
    } 
    public void staticAccess(int val) {//访问和操作static变量staticX 
        int tempStaticX=staticX; 
        for (int i = 0; i < val; i++) { 
            tempStaticX += 1; 
        } 
        staticX=tempStaticX; 
    } 
改善之处就是将instance和static变量放到循环之外,而用一个stack变量来完成多次局部运算,最后再将这个stack变量的值传回instance或static变量,从而提高了代码的性能。 



Sun JDK自带JVM内存使用分析工具HProf 
      使用Sun JDK自带JVM内存使用分析工具HProf可以分析JVM堆栈,从而找到占用内存较大的对象。这对应经常出现内存泄漏(OOM)的JAVA系统进行调优很有帮助。 

HProf使用方法 
•         在WeblogicServer启动脚本中增加-Xrunhprof:heap=sites,重新启动WeblogicServer。 
•         使用kill -3 <pid> 或退出WeblogicServer均会生成java.hprof.txt文件,直接打开此文件便可分析JVM的具体运行情况。 

从java.hprof.txt记录的JVM堆栈结果中可以发现JVM占用内存较大的对象: 
          percent         live       alloc"ed  stack class 
rank   self  accum    bytes objs   bytes objs trace name 
    1  4.57%  4.57%  2289696 47702 8392224 174838  4251 [C 
    2  3.99%  8.57%  2000016    1 2000016    1 12308 [C 
    3  3.65% 12.22%  1827552 9622 1852672 10082 43265 [C 
    4  2.58% 14.80%  1293912 53913 3929424 163726  4258 java.lang.String 
    5  2.05% 16.85%  1028664 7585 3207272 24923  4252 [C 
    6  2.03% 18.88%  1015816  159 1015816  159 18694 [B 
    7  1.88% 20.77%   942080  230 2740224  669 20416 [B 
    8  1.61% 22.37%   805752 2142 2150856 4635 45318 [B 
    9  1.60% 23.98%   802880  772  802880  772 24710 weblogic.servlet.utils.URLMatchMap$URLMatchNode 
   10  1.60% 25.57%   799400 19985 2781400 69535 45073 cnc.util.Field 
   11  1.36% 26.93%   679360 3805  679360 3805   494 [B 
   12  1.35% 28.28%   674856 28119 5181240 215885  2985 java.util.HashMap$Entry 
…… 
…… 
   96  0.19% 63.73%    94776 3112   94776 3112  9146 [C 
   97  0.19% 63.92%    93456 3894  123936 5164 23631 java.lang.String 
   98  0.19% 64.10%    93224 3884  123968 5165 23644 java.lang.String 
   99  0.19% 64.29%    93192 3883  123936 5164 23636 java.lang.String 
  100  0.18% 64.47%    89528  238  240264  520 33227 [B 
  101  0.17% 64.64%    86448 1901  103472 2255 18715 java.lang.Object 
  102  0.17% 64.81%    85464  676   85768  695 18715 [S 
  103  0.17% 64.98%    85184 1331   85184 1331 28266 weblogic.ejb20.internal.MethodDescriptor 
  104  0.17% 65.15%    84224  752   84224  752 24148 weblogic.servlet.internal.dd.ServletDescriptor 
  105  0.17% 65.32%    84136  528 50471136 348769    63 [C 
  106  0.16% 65.48%    79968 1428  388976 6946  5503 java.lang.reflect.Method 
  107  0.15% 65.63%    77520 1615   77520 1615 27967 weblogic.ejb20.deployer.mbimpl.MethodInfoImpl 
  108  0.15% 65.79%    77056 4816  469808 29363 20250 java.lang.Object 
  109  0.15% 65.94%    76960   74   76960   74 23695 [B 
  110  0.15% 66.09%    76104 3171  215040 8960 45071 cnc.util.FyCol 
  111  0.15% 66.24%    74688 3112   74688 3112  9152 java.util.Hashtable$Entry 
  112  0.15% 66.39%    74688 3112   74688 3112  9147 java.lang.String 
  113  0.15% 66.54%    74280   61  794328  788 45313 [C 
  114  0.14% 66.68%    72480 1510  436032 9084 45353 [C 
  115  0.14% 66.82%    70720   68   70720   68 25869 [B 
  116  0.14% 66.97%    70720   68   70720   68 27448 [B 
  117  0.14% 67.11%    70272 1279  142672 2439  5503 [C 
  118  0.14% 67.24%    69256   86   69256   86  6584 [S 
  119  0.13% 67.38%    67056   66   67056   66 28882 java.lang.Object 
  120  0.13% 67.51%    66176  752   66176  752 24170 weblogic.servlet.internal.dd.UIDescriptor 
  121  0.13% 67.64%    65688  715   65688  715 25389 [C 
  122  0.13% 67.77%    65600    4  885600   54 23939 [C 
  123  0.13% 67.90%    65600    4  623200   38 40639 [C 
  124  0.13% 68.03%    65576  367   65576  367 51686 [C 
  125  0.13% 68.17%    65568    2   65568    2 30610 java.util.HashMap$Entry 
  126  0.13% 68.30%    65568    2  130816   16 43271 java.util.HashMap$Entry 
  127  0.13% 68.43%    65552    1   65552    1 16617 [B 
  128  0.13% 68.56%    64600 1615   64600 1615 27969 java.util.HashMap 
  129  0.13% 68.68%    63888 2662   64032 2668 16951 java.util.HashMap$Entry 
  130  0.13% 68.81%    63888 2662   64032 2668 16997 java.util.HashMap$Entry 
  131  0.13% 68.94%    63888 2662   64032 2668 16996 weblogic.rmi.internal.ClientMethodDescriptor 
  132  0.13% 69.07%    63888 2662   99120 4130 16949 java.lang.String 
  133  0.13% 69.19%    63888 2662   64032 2668 16976 java.lang.String 
  134  0.13% 69.32%    63232  152   63232  152  9655 weblogic.utils.collections.ConcurrentHashMap$Entry 
  135  0.13% 69.45%    63232  152   63232  152  9704 weblogic.utils.collections.ConcurrentHashMap$Entry 
  136  0.12% 69.57%    62168 3885   82632 5164 23628 [B 
  137  0.12% 69.69%    61680  406   66904  468     1 [C 
  138  0.12% 69.82%    61504    4  246016   16 47372 [B 
  139  0.12% 69.94%    61144   36 91019160 23904    92 [B 
  140  0.12% 70.06%    61040  763   61040  763 24194 weblogic.servlet.internal.dd.ServletMappingDescriptor 
  141  0.12% 70.18%    60400 1510  363360 9084 45338 java.util.Hashtable 
  142  0.12% 70.30%    59544  827   59544  827 24746 weblogic.servlet.internal.ServletRuntimeMBeanImpl 
  143  0.12% 70.42%    59248 1058  484984 8664 33236 oracle.jdbc.ttc7.TTCItem 
  144  0.12% 70.53%    58152  232  187176  764   748 [C 
  145  0.12% 70.65%    57888 2412  161904 6746 16621 java.lang.String 
  146  0.11% 70.77%    57400 1435   57400 1435 16855 java.util.HashMap 
…… 
…… 

根据以上的结果,在java.hprof.txt中定位到导致分配大内存的操作如下: 
TRACE 63: 
java.lang.StringBuffer.expandCapacity(StringBuffer.java:202) 
java.lang.StringBuffer.append(StringBuffer.java:401) 
java.util.zip.ZipFile.getEntry(ZipFile.java:148) 
java.util.jar.JarFile.getEntry(JarFile.java:198) 
TRACE 92: 
java.util.zip.InflaterInputStream.<init>(InflaterInputStream.java:71) 
java.util.zip.ZipFile$1.<init>(ZipFile.java:240) 
java.util.zip.ZipFile.getInputStream(ZipFile.java:212) 
java.util.zip.ZipFile.getInputStream(ZipFile.java:183) 
再进一步分析则需要应用开发人员对应用代码做相应的分析定位。 

注意:使用HProf非常消耗资源,切记不要在生产系统使用。 






Java优化编程(第二版) 
2.1 垃圾回收堆内存  
内存管理的话题在C或C++程序设计中讨论得相对较多,因为在C与C++程序设计中需要开发人员自己申请并管理内存,开发人员可以申请/借用(Apply)系统内存并且负责释放/归还(Release)系统内存,如果“只借不还”就会造成系统内存泄露的问题。在Java程序设计中,这些工作由Java虚拟机(JVM)负责处理。所有内存的申请、分配、释放都由JVM负责完成。因此,开发人员就省去了这部分工作,不过这并不意味着开发人员可以完全依赖于JVM的内存管理功能,如果你这样想并在实际的应用开发中也这样做,你所开发的应用的性能,就有可能不是最优的。这是因为,无论配置多么优良的硬件环境其自身资源都是有限的,由于没有合理、科学的使用内存资源,即使是Java应用也会出现内存枯竭的现象。例如,我们经常会遇到的OutOfMemoryException。再者Java语言(其实不只Java语言)的性能极大程度上依赖于其运行的硬件环境资源,而内存又是硬件环境资源中重要的一部分,因此说,如果开发人员开发的Java应用没能有效、合理地使用系统内存,那么这个应用就不可能具备较高的性能,甚至会导致整个系统在运行一段时间后崩溃。本章将对Java应用开发中与内存管理相关的技术做详细的讲解。 
2.1  垃圾回收 
谈到Java内存管理的话题,就必然会提到垃圾回收的概念,垃圾回收的英文名称为Garbag
声明:该文观点仅代表作者本人,入门客AI创业平台信息发布平台仅提供信息存储空间服务,如有疑问请联系rumenke@qq.com。