最近面试被问到这个问题,当时是比较懵的,后来查了一些资料以及自己尝试了一些,现在做一个总结。
避免过深的类层次结构和过深的方法调用。其实平常在写一些算法题的时候能用迭代就不会用递归(虽然Java中可以用Lambda的尾调用机制解决栈溢出)。以后如果有机会写大型项目,一定要注意这一点。
对象只有在使用的时候才实例化,在类的构造器中不要初始化过多对象。以及使用单例模式避免创建过多实例。
消除过期对象的引用,这也是避免内存泄漏的方式,即使用完毕后把对象的引用赋值null。尤其是一些对象作为成员变量的时候要注意这点。
对于Java语言来说,有常量池的存在,所以赋值的时候能用常量就不需要new,(其实就这点来说我们平常在写程序时也很少对String用new赋值的,这里尝试了一下举例并观察内存,但发现举得例子有点儿蠢就不贴出来了)
但是!!!使用常量时也要注意常量是不能修改的,比如s1+s2是生成新的字符串常量,然后引用指向新常量。所以如果在你的程序中要对字符串进行多次修改,使用StringBuffer(线程安全)或者 StringBuilder s1.append()。下面放几张自己测试的结果图。(也就是说如果要对字符串多次修改,用append避免创建新常量也属于内存优化,但是用append实际上对时间的优化更明显,因为即使s1+s2创建新常量,也会通过GC机制回收)
字符串直接相加耗时
StringBuilder的append()耗时
顺便对比一下StringBuffer的append()耗时,用了synchronized关键字所以会慢一些。
尽量避免使用static变量,以及缩小局部变量的作用范围
面试官提醒的缓存容器,按照一定的算法淘汰不需要继续缓存的对象,比如ehcache,oscache等(过了考试周一定去试试)
还有自己想到的一个小的优化点,即遇到-统计数组中只出现一次的元素 这样的问题时,用bitmap代替hashmap或整型数组,具体在不同的语言中有不同的实现,比如Java用BitSet。
面试官还问到了查看线程状态的问题,因为平时确实对这方面实践少,所以没了解过。后来发现JDK本身自带了一些查看工具,比如Jstack跟踪堆栈,JConsole和JVisualvm都是可视化监控JVM的工具,感觉很好用。截几张图吧:
作者:日出斯图加特
内存优化
优化