相信很多小伙伴在实际研发工作中大多数都是根据业务逻辑来实施研发,经常遇到循环体中集合的使用或者实体的使用场景,在大数据量下经常会遇到性能问题(效率和资源消耗),很多新人都不太注重研发时性能问题,今天就针对该场景下的优化记录下。(个人研究,有误留言)
场景:很多业务都会有循环体中进行集合或者对象的使用,如下(方式一):
public class ListForObject {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
outsideLoop();
System.out.println("时间消耗: " + (System.currentTimeMillis()-startTime)+"M");
}
//
public static void outsideLoop() {
List
上述代码逻辑很简单,就是将map数据封装入list中后续操作。
性能问题:
相信大家都看得出来,最大的问题就是循环内初始化,大数据量循环体中初始化会很吃内存以及性能消耗。
那很多人说了,是不是把初始化放到循环外面就好了呢,减少了对象的引用创建,就可以提高效率了?比如像这样(方式二):
public static void outsideLoop() {
List
网上很多博客都在表述,这样的写法虽然依旧在循环内容初始化,但是减少了对象引用的创建和使用,应该是能够提高性能的(效率和资源),那真的是这样吗?
那我们一起来探讨下:
- 上述说到的优化是否能提高效率和介绍内存使用呢?
- 针对上述的代码要怎么优化呢?
那就开始!
1、问题一探讨先说结果:其实两个性能基本差不多,内存消耗也基本异常,但是还是推荐第一种方式,探讨过程如下:
要想知道两种写法究竟有什么区别,能不能起到优化效果,很简单看字节码罗。
(1)先看看方式一的字节码(javap -v +.class):
字节码结果:
防止有人看不明白字节码,我这边进行非常详细的备注,看到着是不是发现了什么?
是的,在16行的时候,开始进入的循环体,随后开始了map的初始化,astore_2 将map引用存入局部变量2号位置,然后79结束,goto到10行继续循环,这里可以看到 再次执行new Map()的时候,并没有创建新的引用地址,而是继续astore_2,也就是说这里将新map对象引用地址替换掉了原来的局部变量位置,此时栈里面已经没有之前的map对象的引用地址了,但是list会存储这个地址,所以map对象不会被回收.....,直到最后一个map加入list之后,此时改map对象会有2个引用指向分别是栈里面引用指向 以及 list。
所以根据上述的探讨发现,好像我们想要减少引用的创建来简约空间完全是多余的想法,jvm并没有创建新的栈空间去存储新的map引用地址,而是重复使用的空间。
(2)再来看看方式二的写法字节码
从字节马上可以看出来, 这种方式比方式一进入循环前多了着两步操作,就是aconst_null astore_2,就是创建一个null对象引用并赋值为map,这里可有看出来创建的只是null对象的引用,所以并不会占用对空间。
问题一总结:从底层字节码角度来分析,就可以看出来方式一和方式二两个写法基本上就是一样的,没什么性能差异,所以也不要误以为方式二可以来提高性能了,因为jvm已经做了引用复用了。
2、问题二探讨那上述场景怎么优化?下篇讲述:
JAVA程序性能优化 - 循环内集合处理优化2



