也是最近,很多服务遇到了gc问题,影响服务的可用性,虽然知道大致的减少对象的创建等,但没有一个较为系统的认知,这里就稍微整理下,辅助以后自己代码的性能(逼格)。
String对象
最容易想到的就是减少String对象的创建,因为String对象为final类型,在项目里,可能会出现String+String的操作。在这里,会先生成一个StringBuilder对象,最后StringBuilder.toString()再生成一个String对象,所以这种a+b的操作会额外生成两个对象。但是JVM对+法的重载用StringBuilder来实现了,所以仅当出现A = a+b;B = A+c的形式出现,
String result = "a" + "b";
result += "c";
System.out.println(result);
当然这种代码如果放在循环中,就会有不可控多个对象的生成。
可以这样写:
StringBuilder result = new StringBuilder("a");
result.append("b").append("c");
System.out.println(result);
当然不在循环里也可以这样
String result = a + b + c;
System.out.println(result);
尽量给数组一个初始容量
ArrayList和其他一些集合类,底层都是使用Object[]数组实现,当一个数据初始化的时候,系统是不知道你需要多少长度,所以会有一个默认值,当数组长度不足时,它就会被一个全新的,长度更长的数据代替,之前的就会被垃圾回收。所以如果在循环中,这里就会有多次的新数组的分配和更多的旧数组需要被回收
~# 多次扩容,多次拷贝
~# 内存碎片
所以在初始化的时候,尽量给数组一个初始值,或者估算一个值
拆装箱
在使用List、Map、Set之类的集合类的时候,编译器是通过自动拆装箱来实现的,把原始数据类型装箱到一个对应的对象中,这个对象是可被回收的。也就是每次集合类的一次add操作,都可能会额外分配一个对象来存储原始类型。
解决方法:有兴趣的同学可以去了解下Trove
· 提供开放选址的map集合类,效率更高,结构更小
· 不需要自动装箱操作,直接保存基础类型数据
循环中集合的返回
当方法返回一个集合类,通常会填充到一个大的集合对象中,过程中每次方法调用返回会产生大量的临时集合
List<Item> items = new ArrayList<Item>();
for (FileData fileData : fileDatas)
{
// 每一次调用都会创建一个存储内部临时数组的临时的列表
items.addAll(readFileItem(fileData));
}
解决方法:
List<Item> items = new ArrayList<Item>(fileDatas.size() * avgFileDataSize * 1.5);
for (FileData fileData : fileDatas)
{
readFileItem(fileData, items); // 在内部添加记录
}
这样可以节省N个List的临时对象
对象作用域
尽可能缩小对象的作用域,生命周期
如果可以在方法内声明的局部变量,就不要声明为实例变量
static变量尽量用在单例或者不变的对象上
Immutable Objects
当Old对象对Young对象引用或者释放的时候,会把Young对象标记为Dirty,不可变对象明显可以较少扫描时候的消耗。同时,也减少了生产环境中一些可变对象可能会引发的bug
网友评论