java内存优化实践

一般在java程序中,内存是个比较头痛的话题。虽然jvm能够通过GC机制很智能地回收资源,但是由于内存的释放都是jvm在进行操作,不恰当的使用会导致java的程序内存持续增大,直至最终OOM(out of memery) 
那么,如何对java进行内存优化呢?一方面可以通过调整jvm的一些配置(内存,GC等),从jvm层优化配置;另一方面,从java程序角度,在代码层次上进行优化。

近期,做了些在java程序方面内存优化实践,积累了一些心得,总结如下:

    1. 对于资源消耗较大的对象(如数据库连接,Stream,大的collection等),尽早进行释放,还得考虑到异常情况

      对于这种情况,就没必要释放obj了,因为在执行完func后,GC会自动回收资源 
      Public void func(){ 
      Object obj =new Object(); 
      …… 
      Obj=null; 

      但是对于这类操作就有必要提前释放了,特别是一些连接资源,大内存对象等 
      Public void func(){ 
      Object obj =new Object(); 
      …… 
      Obj=null; 
      xxxx; // 耗时操作 

      而且对于数据库资源/stream,在释放的时候,要考虑到出现exception的情况,建议在finalize中释放连接 
      public static Properties loadProperties(String fileName) throws IOException { 
      FileInputStream stream = new FileInputStream(fileName); 
      try { 
      Properties props = new Properties(); 
      props.load(stream); 
      return props; 

      finally { 
      stream.close(); 

      }

    2. 优化逻辑,避免不必要对象的创建,且尽量使用局部变量 
      由于局部变量是在stack上进行分配的,速度较快,且生命周期是固定的,同时可以优化程序的逻辑,避免一些不必要的逻辑的创建,如

      A a = new A(); 
      if(i==1){list.add(a);}

      应该改为 
      if(i==1){ 
      A a = new A(); 
      list.add(a); 

      还有,比如: 
      FileInputStream stream = new FileInputStream(fileName); 
      if (xxx) { 
      return; 

      stream.xxx; 
      应该改成: 
      if (xxx) { 
      return; 

      FileInputStream stream = new FileInputStream(fileName); 
      stream.xxx;

    3. 优化循环逻辑,减少循环逻辑里对象的创建和调用

      for (int i = 0; i < xxx.size(); i++) { 
      xxx 

      这样xxx.size()会被调用多次,效率较低,可以使用: 
      for (int i = 0, len = xxx.size(); i < len; i++) { 
      xxx 

      同样要在循环里面对于对象的创建要慎重,如 
      for (int i = 0; i < 1000; ++i) { 
      Map myMap = new HashMap<>(); 
      myMap.xxx; 

      应该改成 
      Map myMap = new HashMap<>(); 
      for (int i = 0; i < 1000; ++i) { 
      myMap.clear(); 
      myMap.XXX(); 
      }

    4. 使用基本类型替代对象类型 
      比如使用int替代Integer,long替代Long等,数组替代vector等,减少对象创建。而且函数中,基本类型是存放于栈上创建和使用效率较高。

    5. 使用stringBuffer和stringBuilder替代多次String对象 
      因为String对象是定长的,任何关于String的变更操作都会导致新的String对象创建。因此可以在合适的时机使用stringBuferr或者stringBuilder替代String对象,单线程使用StringBuilder,多线程情况下使用StringBuffer。

    6. 提前分配stringBuffer,数组,array,vector等容量 
      对于需要连续分配的对象,最好先提前分配容量,一般默认的初始容量都比较小,如stringBuffer的容量是只有16,如果按照默认的容量很容易在容量满了之后重新分配一块大的资源,耗时费内存。因此可以通过提前分配资源的方式避免这个情况

    7. 缓存经常要使用的对象和数据 
      对于经常需要使用的对象和数据,可以使用缓存存起来(比如内存中的共享内存,或者redis,memcached等remote cache server),减少一些对象的重复创建计算代价

    8. 慎用exception 
      当创建一个异常时,需要收集一个栈跟踪(stack track),这个栈跟踪用于描述异常是在何处创建的。构建这些栈跟踪时需要为运行时栈做一份快照,正是这一部分开销很大。因此要慎用异常,一般而言,异常是只有当前处理不了的异常才往上层抛,否则可以通过函数返回值(bool false,object=null)等方式向上呈现结果。

    9. 数组复制时,使用System.arraycopy()命令。 
      arrayCopy命令比循环复制数组快很多

posted @ 2015-08-18 14:31  SailorXiao  阅读(433)  评论(0编辑  收藏  举报