一、概述
对于代码级的优化,不管用哪种程序语言编程,首先都应该注意编码规范和风格。不同的公司或者团队以至程序员个人都有自己的编码风格,但目的是让程序代码可读、简洁、高效、易于重用。养成良好的编码习惯是每个程序员的必需的。最开始,我们可以针对自己编写的代码进行自己检查,看看有没有什么改进的地方,包括注释是否适当,命名是否恰当,程序是否最优化等等,然后可以进行同行评审一下,针对意见进行思考,改进自己编写代码。如果公司有条件,譬如有专门的测试规范、技术配置与管理等等,可以利用他们提供的方法与工具进行单元测试与代码检查。譬如用
jtest
工具,可以根据编码规范编制一套规则,用它来检测自己的程序是否符合规范。
在养成良好的编码习惯与编码规范之后,我们接下来要针对程序的本身进行优化。首先检查自己的代码是不是使用的较优的算法,在设计上是否符合逻辑,有没有更好实现方式。这一点可能有点难度,随着编程的经验与思考的程度增加,会提高自己程序设计的能力。这些方面可以多看点这方面的书,譬如《代码大全
第二版》等等。
最后,针对程序设计语言的本身特点进行优化,包括变量的创建、内存的管理等等。譬如在
JAVA
中,应该尽可能少的创建对象。
另外一个方面,我们可以从大师级那里去经,阅读大师写的代码。参看一些前人的经验,特别是设计模式。设计模式应该可以说是精华的提研,但同时不要受其束缚,当自己感觉有些不同的时候,要大胆创新。
二、
Java
程序的设计风格
跳过阅读,详细风格参见本目录下的《软件编程规范—
Java.doc
》;
如果测试部能根据编码规范定义规则对代码进行单元测试,那就更好了。
三、内存管理
1.
垃圾回收
GC, Garbage Collection
,垃圾回收机制,一个对象创建后被放置在
JVM
的堆内存中,
当永远不再引用这个对象时,它将被
JVM
在堆内存中回收。
堆内存(
Heap
),堆内存在
JVM
启动的时候被创建,堆内存中存储的对象可以被
JVM
自动回收,但不能通过其他外部手段回收。
Heap
通常划分两个区域:新对象区域和老对象区域新对象区的对象超过生命周期,会转入到老对象区域,并且标记为垃圾对象,垃圾回收与对象的生命周期是紧紧联系在一起的。
优化:
(
1
)最好不要手动调用
GC
(
System.gc()
),推荐使用
obj = null
的方式来提醒
JVM
来调用
GC
Object obj = new Object();
… //
使用
obj = null ;
|
(
2
)
JVM
内存参数调整
JVM
的默认内存大小为
63M
,可以通过
Runtime.getRuntime().maxMemory()
查看到。在硬件可行的情况下,我们可以适当提高一下内参数,譬如对于
1G
以上的内存,如果是服务器使用,我们可以把
newsize
跟
maxsize
设成一样,最高达到
320M
,各参数配置如下:
-
Xms,
-
Xmx
一般设为同样大小。
800m
-
Xmn
是将
NewSize
与
MaxNewSize
设为一致。
320m
-
XX:PerSize 64m
-
XX:NewSize 320m
此值设大可调大新对象区,减少
Full GC
次数
-
XX:MaxNewSize 320m
-
XX:NewRato NewSize
设了可不设。
4
-
XX: SurvivorRatio 4
-
XX:userParNewGC
可用来设置并行收集
-
XX:ParallelGCThreads
可用来增加并行度
4
-
XXUseParallelGC
设置后可以使用并行清除收集器
-
XX
:
UseAdaptiveSizePolicy
与上面一个联合使用效果更好,利用它可以自动优化新域大小以及救助空间比值
|
Ps
:具体性能最优参数,可以通过性能测试把参数测试出来,相关性能测试方法与工具使用可以参考《
J2EE
性能测试》。
2. JVM
的生命周期
我们分析期生命周期的原因在于,我们可以观察每个阶段是不是有优化的可能性,从而进行优化。
(
1
)创建阶段:分配存储空间
-- >
构造对象
-- >
递归调用超类构造函数
-- >
象实例初始化与变量的初始化
-- >
行构造方法体
优化:
根据对象创建的应用规则进行优化:
a.
避免在循环体内创建对象,即使该对象占用的内存空间不大,应该采用如下方式
Object obj = null;
for( int i=0 ; i<1000 ; ++i ) {
obj = new Object();
… …
}
|
b.
不要一个对象初始化多次
Object obj = new Object();
//
这样写不正确,应该声明为空,在使用的时候再创建实例
//
正确写法
Object obj = null;
if ( … ) { obi = new Object();
}
|
c.
尽量及时使对象符合垃圾回收的标准
譬如使用
obi = null;
或者调用规则的方法,如
servlet
中
destroy()
方法。
d.
不要采用过深的继承层次
因为继承类的初始化会调用基类的默认构造函数,导致大量的开销。一般继承
结构不宜超过
3
层,另外,推荐使用接口。
e.
访问本地变量优于访问类中的变量
譬如
String
对象类型,如果使用
String.length()
方法超过两次,我们应该考虑
使用一个变量来替代,
int temp = String.length();
(
2
)应用阶段:
在应用阶段,系统至少维护着对象的强引用。
强应用(
Strong Reference
):指
JVM
内存管理器从根引用集合(
Root Set
)出发遍寻堆中所有到达对象的路径。
软应用(
Soft Reference
):具有较强的引用功能。可以用于实现一些常用的资源的缓存,实现
Cache
的功能,保证最大限度的使用内存而不引起
Out Of Memory
。软引用技术的引进使
Java
应用可以更好的管理内存,稳定系统,防止系统内存溢出。软引用使用方式:
A a = new A();
……
//
使用完
a
,将它设置为
soft
引用类型,并且释放强引用
;
SoftReference sr = new SoftReference(a);
a = null;
…
//
下次使用
if( sr != null) {
a = sr.get();
}else{
//GC
由于低内存,已释放
a
,因此需要重新装载
a = new A();
sr = new SoftRerence(a);
}
|
弱引用(
Weak Reference
):与
soft
引用对象不同之处在于:
GC
在进行回收时,需要通过算法检查是否回收
Soft
引用对象,而对于
Weak
对象,
GC
总是进行回收。
虚引用(
Phantom Reference
):辅助
finalize
函数的使用。
(
3
)其他阶段
不可视阶段
不可到达阶段
可收集阶段、终结阶段与释放阶段,
3.
构造函数与
Finalize
函数
构造函数会在类初始化实例的时候将会被执行,因此,我们在设计类的时候尽可能的避免在类的默认构造函数中创建、初始化大量的对象。
finalize
,类似
C++
中析构函数
4.
数组的创建
创建的时候,一般我们尽可能的使用隐式创建:
int [] intArray = obj.getIntArray();
如果碰到占用内存空间比较大的时候,我们通过软引用技术来应用数组。
5.
共享静态变量存储空间
静态变量可以节约大量的内存开销,使用情况:
(1)
变量做所包含的对象体积较大,占用内存较多
(2)
变量所包含的对象生命周期长
(3)
变量所包含的数据稳定
(4)
该类的对象实例有对该变量所包含对象的共享需求
6.
对象重用
缓存对象的应用,对象池(
ObjectPool
)
,
通过对其所保存对象的共享和重用,缩减了应用线程反复重建、装载对象所需要的时间,并且避免频繁的垃圾回收带来的巨大开销。
譬如数据库的连接池,对一个数据库连接池详细研究
参见文章《数据库连接池技术》
7.
瞬间值
实现
Java.lang.Serializable
接口,具体使用参见其
API
。
8.Java
程序设计其他经验
(1)
尽早释放无用引用对象,
obj = null ;
(2)
尽量少用
finalize
函数
(3)
如果经常使用图片,可以使用
soft
应用类型
(4)
注意集合数据类型,这些对
GC
来说,回收更复杂
注意一些全局变量,或者静态变量容易引起悬挂对象,造成对象浪费
(5)
尽量避免在类的默认构造器中创建、初始化大量的对象
(6)
尽量避免强制系统调用
GC
(
System.gc()
)
(7)
尽量避免显式申请数组空间
(8)
尽量在做
RMI
类应用开发时使用瞬间值(
transient
)变量
(9)
尽量在合适的场合下使用对象池技术以提高系统性能
参考资料:
《Java优化编程》