Java的内存回收——内存管理小技巧

4、内存管理小技巧

4、1 尽量使用直接量

       当需要使用字符串,还有Byte、Short、Integer、Long、Float、Double、Boolean、Character包装类的实例时,程序不应该采用new的方式来创建对象,而应该直接采用直接量来创建它们。
       例如,程序需要"hello"字符串,应该采用如下代码:
     String str = "hello";
      上面方式会创建一个"hello"字符串,而且JVM的字符串缓存池还会缓存这个字符串。但是如果程序使用如下代码:
     String str = new String("hello");
      此时程序同样创建了一个缓存在字符串缓存池中的"hello"字符串。除此之外,str所引用的String对象底层还包含了一个char[]数组,这个char[]数组里依次存放了h、e、l、l、o等字符串。

4、2 使用StringBuilder和StringBuffer进行字符串连接

       String、StringBuilder、StringBuffer都可以代表字符串,其中String代表字符序列不可变的字符串,而StringBuilder、StringBuffer都代表字符串序列可变的字符串。
       如果程序使用多个String对象进行字符串连接运算,在运行时将生成大量的临时字符串,这些字符串会保存在内存中从而导致程序性能下降。

4、3 尽早释放无用对象的引用

       大部分时候,方法的局部引用变量所引用的对象会随着方法的结束而变成垃圾,因为局部变量的生存期限很短,当方法运行结束时,该方法内的局部变量就结束了生存期限。因此,大部分时候程序无须将局部变量显示设为null,但是有时候涉及到消耗内存的部分则需要显示的设置为null。

4、4 尽量少用静态变量

       从理论上来说,Java对象何时被回收由垃圾回收机制决定,对程序员来说是不确定的。由于垃圾回收机制判断一个对象是否是垃圾的唯一标准就是该对象是否有引用变量引用它,因此推荐尽早释放对象的引用。
       最坏的情况是,某个对象被static变量所引用,那么垃圾回收机制通常是不会回收这个对象所占的内存。示例如下:
     class Person {
         static Object obj = new Object();
     }
       对于上面的Object对象而言,只要obj变量还引用到它,它就不会被垃圾回收机制所回收。obj变量是Person类的静态变量,因此它的生命周期与Person类同步。在Person类不被卸载的情况下,Person类对应的Class对象会常驻内存,直到程序运行结束。因此,obj所引用的Object对象一旦创建,也会常驻内存,直到程序运行结束。
提示:根据分代回收机制,JVM会将程序中Person类的信息存入Permanent代。也就是说,Person类、obj引用变量都将存在Permanent代里,这将导致obj对象一直有效,从而使得obj所引用的Object得不到回收。

4、5 避免在经常调用的方法、循环中创建Java对象

       经常调用的方法和循环有一个共同的特征:这些代码段会被多次重复调用。
     public class Test {
        public static void main(String[] args) {
           for(int i = 0 ; i < 10 ; i ++) {
              Object obj = new Object();
              //执行其他操作...
           }
        }
     }
       上面代码在循环中创建10个Object对象,虽然上面程序中的obj变量都是代码块的局部变量,当循环执行结束时这些局部变量都会失效,但由于这段循环导致Object对象会被创建10次,因此系统需要不断地为10个对象分配内存空间并执行初始化操作。这10个对象的生存时间并不长,接下来系统又需要回收它们所占用的空间。在这种不断的分配、回收操作中,程序的性能受到巨大的影响。

4、6 缓存经常使用的对象

       如果有些对象需要被经常使用,则可以考虑把这些对象用缓存池保存起来,这样当下次需要时就可以直接拿出这些对象来使用。典型的缓存就是数据连接池,数据连接池里缓存了大量的数据库连接,每次程序需要访问数据库时都可以直接取出数据库连接。
       除此之外,如果系统中还有一些常用的基础信息,比如信息化信息里包含的员工信息等。实现缓存时通常由两种方式。
  • 使用HashMap进行缓存。
  • 直接使用某些开源项目。
       如果直接使用HashMap进行缓存,程序员需要手动控制HashMap容器里的key-value对不至于太多,因此当key-value对太多时将导致HashMap占用过大的内存,从而导致系统性能下降。如果使用一些开源的缓存项目进行缓存,这些缓存项目都会主动分配一个具有一定大小的缓存容器,再按照一定算法来淘汰容器中不需要继续缓存的对象。这样一方面开源通过缓存已用过的对象来提高系统的运行效率,另一方面又可以控制缓存容器的无限制扩大,从而减少系统的内存占用。对于这种开源的缓存实现有很多选择,如OSCache、Ehcache等,大多实现了FIFO、MRU等常见的缓存算法。

4、7 尽量不要使用finalize方法

       在一个对象失去引用之后,垃圾回收器准备回收该对象之前,垃圾回收机制会先调用该对象的finalize()方法来执行资源清理。出于这种考虑,可能会考虑使用finalize()方法来进行资源清理。
       实际上,将资源清理放在finalize()方法中完成不是一个好选择,在垃圾回收器本身已经严重制约应用程序性能的情况下,如果再选择使用finalize()方法进行资源清理,无疑是火上浇油的行为,这将导致垃圾回收器负担更大,导致程序运行效率更差。

4、8 考虑使用SoftReference

       当程序需要创建长度很大的数组时,可以考虑使用SoftReference来包装数组元素,而不是直接让数组元素来引用对象。SoftReference是一个很好的选择:当内存足够时,它的功能等同于普通引用;当内存不够时,会牺牲自己,释放软引用所引用的对象。
       使用软引用引用对象时不要忘记引用的不确定性。程序通过软引用所获取的对象有可能为null。当系统内存紧张时,SoftReference所引用的Java对象将被释放。由于通过SoftReference获取的对象可能为null,因此应用程序取出SoftReference所应用的Java对象之后,应该显示判断该对象是否为null;当该对象为null时,应重建该对象。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章