一、 Java对象的引用种类
Java内存管理包括内存分配和内存回收, 这个动作都是由JVM自动完成,所以过多的内存分配增加了内存的消耗,且垃圾回收线程的不断运行会给后台增加压力,降低系统的性能。
1.1 对象在内存中的状态
· 可达状态: 当一个对对象被创建后,有一个以上的引用变量引用它,在它处于可达状态。
·可恢复状态: 如果程序中的某个对象不再有任何引用变量引用它,它将先进入可恢复状态。
· 不可达状态: 当对象的所有关联都被切断,且系统调用所有对象的finalize方法依然没有使该对象变成可达状态,它就将永远失去引用,等待被回收。
1.2 强引用
java程序创建一个对象,并把这个独享赋值给一个引用变量,这个引用变量就是强引用。
JVM是肯定不会回收强引用所引用的Java对象。
1.3 软引用
软引用需要通过SoftReference实现,对于只有软引用的对象而言,当系统的内存空间足够时,它不会被系统回收,程序也可以使用该对象;当系统的内存空间充足时,系统不会回收它。 当系统的内存空间不足时,系统将会回收它。
当程序需要大量创建某个类的新对象时,而且有可能重新访问已创建老对象时,可以充分使用软引用来解决内存紧张的问题。
class Person { String name; int age; public Person(String name , int age) { this.name = name; this.age = age; } public String toString() { return "Person[name=" + name + ", age=" + age + "]"; } } public class SoftReferenceTest { public static void main(String[] args) throws Exception { SoftReference<Person>[] people = new SoftReference[100000]; for (int i = 0 ; i < people.length ; i++) { people[i] = new SoftReference<Person>(new Person( "名字" + i , (i + 1) * 4 % 100)); } System.out.println(people[2].get()); System.out.println(people[4].get()); //通知系统进行垃圾回收 System.gc(); System.runFinalization(); //垃圾回收机制运行之后,SoftReference数组里的元素保持不变 System.out.println(people[2].get()); System.out.println(people[4].get()); } }
上面程序创建了一个长度为100的SoftReference数组,程序使用这个数组来保存100个person对象,当系统内存足够时,即使系统进行垃圾回收,垃圾回收机制也不会回收这些Person对象所占的内存空间。
1.4 弱引用
弱引用与软引用有点相似,区别在于弱引用所引用对象的生存空间更短。 对于只有弱引用的对象而言,当系统的垃圾回收机制运行时,不管系统内存是否足够,总会回收该对象所占内存。
public class WeakReferenceTest { public static void main(String[] args) throws Exception { //创建一个字符串对象 String str = new String("疯狂Java讲义"); //创建一个弱引用,让此弱引用引用到"疯狂Java讲义"字符串 WeakReference<String> wr = new WeakReference<String>(str); //① //切断str引用和"疯狂Java讲义"字符串之间的引用 str = null; //② //取出弱引用所引用的对象 System.out.println(wr.get()); //③ //强制垃圾回收 System.gc(); System.runFinalization(); //再次取出弱引用所引用的对象 System.out.println(wr.get()); //④ } }
系统输出:
疯狂Java讲义
null
从上图可以看出: 此时的“疯狂Java讲义”字符串对象只有一个弱引用对象引用它,程序依然可以通过这个弱引用对象来访问该字符串常量; 但是,当启动垃圾回收机制,只有弱引用的对象就会被清理掉,系统在访问弱引用的空间,就会发现以为null,表明该对象已经被清理掉了。
与WeakReference功能类似的还有WeakHashMap,用于保存对个需要使用弱引用来引用的对象。
class CrazyKey { String name; public CrazyKey(String name) { this.name = name; } //重写hashCode()方法 public int hashCode() { return name.hashCode(); } //重写equals方法 public boolean equals(Object obj) { if (obj == this) { return true; } if (obj != null && obj.getClass() == CrazyKey.class) { return name.equals(((CrazyKey)obj).name); } return false; } //重写toString()方法 public String toString() { return "CrazyKey[name=" + name + "]"; } } public class WeakHashMapTest { public static void main(String[] args) throws Exception { WeakHashMap<CrazyKey , String> map = new WeakHashMap<CrazyKey , String>(); //循环放入10个key-value对 for (int i = 0 ; i < 10 ; i++) { map.put(new CrazyKey(i + 1 + "") , "value" + (i + 11)); } //垃圾回收之前,WeakHashMap与普通HashMap并无区别 System.out.println(map); System.out.println(map.get(new CrazyKey("2"))); //通知垃圾回收 System.gc(); //暂停当前线程50ms,让垃圾回收后台线程获得执行 Thread.sleep(50); //垃圾回收后,WeakHashMap里所有Entry全部清空 System.out.println(map); System.out.println(map.get(new CrazyKey("2"))); } }
可以看出,在垃圾回收机制前,WeakHashMap的功能与普通的HashMap并没有太大的区别,它们的功能完全相似。 而一旦垃圾回收机制被执行,WeakHashMap中的所有key-value都会被清空。
1.5 虚引用
虚引用不能单独使用,它的主要作用是跟踪对象被垃圾回收的状态,程序可以通过检查与虚引用关联的引用队列中是否包含指定的虚引用,从而了解虚引用所引用对象是否即将被回收。
二、 内存泄露
内存泄漏是指: 程序运行过程中不断的分配内存,那些不再使用的内存空间应该及时回收它们,从而保证系统可以再次使用这些内存,如果存在无用的内存没有回收回来,这就是内存泄露。
三、 内存管理技巧
1. 尽量使用直接量
2. 使用StringBuffer、StringBuilder进行字符串连接
3. 尽早释放无用对象 obj=null
4. 尽量少用静态变量
5. 避免在经常调用的方法、循环中创建java对象
for(int i=0;i<100;i++){ Object obj = new Object(); // 避免使用 }
6. 缓存经常使用的对象
7. 尽量不要使用finalize方法
8. 考虑使用SoftReferenct引用
注: 文章内容来自《疯狂Java:突破程序员基本功的16课》, 作者: 李刚