[关闭]
@SailorXiao 2015-08-18T14:29:54.000000Z 字数 2096 阅读 2042

JAVA 程序内存优化实践

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命令比循环复制数组快很多

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注