我们经常看到一些Java性能优化的书或者概念,说变量不要定义在循环内部,会占用太多内存,影响性能,而应该定义在循环外。由于接触Java这么久,相信很多Java程序员都被这种代码性能优化策略误导过。看下面两个例子,例1定义了循环外的变量,例2定义了循环内的变量。/***在循环外定义变量*/privatestaticvoidouter(){Javastackjavastack=null;for(inti=0;i<10;i++){javastack=newJavastack();}}/***在循环内定义变量*/privatestaticvoidinner(){for(inti=0;i<10;i++){Javastackjavastack=newJavastack();}}先分析这两个例子。在循环外定义变量在循环外定义变量,变量循环中的每次引用都指向不同的对象实例,每次循环改变对象实例,最后指向的对象都会被销毁,直到最后一次循环。这样,循环结束后,这个变量仍然存在并指向循环中的最后一个对象实例,其他所有对象都被销毁。这样,本应在循环内部的生命周期变量就扩散到了循环外部。如果这个变量还在循环外使用,将会在后续业务中产生不可预知的后果。这种问题在笔者的工作中经常遇到,看下面的例子。/***在循环外定义变量*/privatestaticvoidouter(){Javastackjavastack1=null;for(inti=0;i<10;i++){javastack1=newJavastack();}Javastackjavastack2=userDao.getUser(10);}定义上面我创建了一个javastack2,如果我在后面的代码中出错或者传递给其他方法,使用了javastack1,这时候不就出问题了吗?这只是一方面,如果使用了相同的变量名,那么在重用这个变量时就会出现异常。原来的异常应该是空值,但是结果是前面循环体中的值。在循环内定义变量在循环内定义变量和在循环外定义变量的区别在于每次都会创建一个新的局部变量指向一个新的对象实例,并且每个变量和对象的生命周期都限定在循环内循环体,而每次循环结束时,局部变量和对象实例都会随着循环体结束而销毁,所以不存在占用更多内存的情况。总结一下,两种用法都会创建相同数量的对象实例,但是会在循环中重复创建相同数量的局部变量,栈内存垃圾回收的频率会更高,但是堆垃圾回收的性能影响而变量生命周期的影响对于未来的业务影响来说,栈内存的性能影响可以忽略不计。所以建议在循环内定义变量,将变量的生命周期限制在循环体范围内,不会因为业务复用变量而造成严重问题。
