众所周知,java的JVM有一套自己的垃圾回收机制。因此在许多情况下并不需要java程序开发人员操太多的心。然而也许也因为这样,往往会造成java程序员的过分依赖而致使开发出来的程序得不到很好的优化,或者说性能尚能提高。
问题的关键在于,不论JVM的垃圾回收机制做得多好,计算机的硬件资源是有限的。内存更是一项紧张资源。因此虽然JVM为我们完成了大部分的垃圾回收。但适当地注意编码过程中的内存管理还是很必要的。这样能让JVM回收得更顺利更高效。最大限度地提高程序的效率。
mark-1:避免在循环体内创建对象。
......
Object obj = null; //方式一
for(int i =0; i < 10000; i++){
//Object obj = new Object(); //方式二
obj = new Object();
}
......
可能许多人看方式一和二,觉得他们并没有什么区别。在循环次数比较少的时候还好,但一旦数量级达到一定的程度,他们之间的花费是截然不同的。方式一仅为该对象报讯一份引用而方式二却在产生大量的对象引用,浪费大量的内存。
mark-2:java的一个强制又隐式的规则:每个java类都有一个父类。
当你声明一个类K的时候 public class K 其实等同于 public class K extends java.lang.Object
mark-3:不要对一个对象初始化多次。
Public class K{
private Hashtable table = new Hashtable();
public K(){
//table = new Hashtable(); //这里将Hashtable对象table进行了第二次初始化,会带来较大的内存开销
}
}
mark-4:多注意使引用置null,方便JVM进行垃圾回收。
在看对书中对强引用(Strong Reference),软引用(soft Reference),弱引用(wreak Reference),虚引用(Phantom Reference),几大引用的时候,没怎么弄懂他想说什么。大概就是要我们在引用不用的时候注意置其为null。
mark-5:注意使用Static,将类中的稳定变量声明为静态变量。
public class K{
......
static Data weeks[] = { //static 将weeks声明为静态变量,对所有对象共享一个副本。
new Data(1, "Monday"),
......
new Data(7, "sunday")
};
......
}
假如有以下程序:
......
K kWeek;
for(int i = 0; i < 100000; i++){
kWeek = new K();
}
类K被创建了10W多个对象,如若没有将weeks声明为静态变量,其将产生10W多个weeks的副本,严重浪费资源。声明为static静态变量后只会产生一个weeks副本。
mark-6:对象重用问题--池
看到一个新名词--对象池。其实是和JDBC的连接池同一道理。
池主要解决的就是:经常使用的对象的创建装载注销的时间耗费问题。由于创建对象的时间耗费相对比较大。所以如果一个对象是经常使用而由于JVM的垃圾回收机制而出现反复创建注销的问题将带来时间效率低下的问题。
池的解决办法其实很简单。就是先创建一定数量的对象。保存下来,当要使用的时候分配出去,用完后回收回来但不注销。这样就解决了频繁创建注销的问题。
在使用池的时候。需要权衡一下利弊,在时间和空间上找到一个适合的点。
mark-7:JVM内存参数和指令
-Xverbosegc 可以在windows上查看JVM所管理的内存信息。
DOS下:java -verbosegc K
[GC 512k->91k(1984k),0.0027537 secs]
......
可以看到总共有1984kb的内存被回收,耗时0.0027537秒。
JVM内存相关的参数
-XX:NewSize (Set the Newgeneralnation heap size)
-XX:MaxNewSize(Set the Maximum Newgeneralnation heap size)
-XX:SurvivorRatio(Set New heap size ratios)
-Xms(Set minimum heap size)
-Xmx (Set maximum heap size)
-Xnoclassgc (取消垃圾回收)
-Xss (设置栈内存的大小)
例:java -XX:NewSize = 128m -XX:MaxNewSize = 128m - XX:SurvivorRatio = 8 -Xms 512m -Xmx 512m MyApplication
mark-8:不同编译方法的类大小
(1)默认编译方式:javac K.java
长度 = 代码 + 源文件信息 + 代码行序号表
(2)调试编译方式:javac -g K.java
长度 = 代码 + 源文件信息 + 代码行序号表 + 本地变量表
(3)代码编译方式:javax -g:none K.java
长度 = 代码
mark-9:经验之谈
1.尽早释放无用对象的引用(XX = null; )
2.尽量少使用finalize函数。
3.注意集合数据类型,如数组,树,图,链表等数据结构,这些数据结构对GC来说回收更复杂。
4.避免在类的默认构造器中创建大量的,初始化大量的对象。
5.避免强制系统做垃圾内存回收。
6.避免显式申请数组空间,不得不显式申请时,尽量准确估计其合理值。