java性能调优(转载)
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
在使用设计模式(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。
- 上一篇:没有了
- 下一篇: jsp 的四个作用域 :page、request、session和application的区别 .