作者 | java金融

来源 | java金融(ID:java4299)

头图 |  CSDN 下载自东方IC

引言

最近刷知乎的时候看到一个比较有意思的问题,变量声明在循环体内还是循环体外?这个问题有人认为应该定义循环体外,不应该定义在循环体内。很多java代码优化建议都有这么一条建议:循环内不要不断创建对象引用 例如:

for (int i = 1; i <= count; i++){Object obj = new Object();
}

这种做法会导致内存中有 countObject 对象引用存在,count 很大的话,就耗费内存了,建议为改为:

Object obj = null;
for (int i = 0; i <= count; i++) {obj = new Object();
}

这样的话,内存中只有一份 Object 对象引用,每次 new Object() 的时候, Object 对象引用指向不同的 Object 罢了,但是内存中只有一份,这样就大大节省了内存空间了。这条建议应该也出现过在很多公司的代码规范上了吧。下面我们就来分析下变量声明在循环体内和变量声明循环体外的情况。

效率对比

首先我们先来看看写在循环体内和询环体外的效率比对,测试代码如下:

/*** @author: 公众号【java金融】* @Date: * @Description:*/@BenchmarkMode(Mode.AverageTime) // 测试完成时间
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 2) // 预热 2 轮,每次 1s
@Measurement(iterations = 5) // 测试 5 轮,每次 1s
@Fork(1) // fork 1 个线程
@State(Scope.Thread)
public class ForEachBenchMark {public static void main(String[] args) throws RunnerException {Options opt = new OptionsBuilder().include(ForEachBenchMark.class.getSimpleName()).result("result.json").resultFormat(ResultFormatType.JSON).build();new Runner(opt).run();}@Param(value = {"10", "50", "100"})private int length;/*** 循环体外创建对象* @param blackhole*/@Benchmarkpublic void outsideLoop(Blackhole blackhole) {Object object = null;for (int i = 0; i < length; i++) {object = new Object();blackhole.consume(object);}}/*** 循环体内创建对象* @param blackhole*/@Benchmarkpublic void insideLoop(Blackhole blackhole) {for (int i = 0; i < length; i++) {Object object = new Object();blackhole.consume(object);}}
}

测试结果如下:

Benchmark                     (length)  Mode  Cnt    Score    Error  Units
ForEachBenchMark.insideLoop         10  avgt    5   58.629 ±  8.857  ns/op
ForEachBenchMark.insideLoop         50  avgt    5  293.726 ±  1.856  ns/op
ForEachBenchMark.insideLoop        100  avgt    5  587.185 ± 40.424  ns/op
ForEachBenchMark.outsideLoop        10  avgt    5   59.563 ±  5.057  ns/op
ForEachBenchMark.outsideLoop        50  avgt    5  305.829 ± 27.476  ns/op
ForEachBenchMark.outsideLoop       100  avgt    5  584.853 ± 20.289  ns/op

我们可以发现不管在循环外创建对象和循环内创建对象时间几乎都是一样的。

字节码对比

下面我们准备两个测试类

public class InsideTest {public static int count = 100;public List<Object> insideLoop() {List<Object> list = new ArrayList<>();int n = 0;for (; ; ) {if (n > count) {break;}Object o = new Object();list.add(o);}Object b = 2;return list;}
}
public class OutsideTest {public static int count = 100;
public List<Object> outsideLoop() {List<Object> list = new ArrayList<>();Object o = null;int n = 0;for (; ; ) {if (n > count) {break;}o = new Object();list.add(o);}Object b = 2;return list;}

这两个编译后字节码几乎一模一样,除了循环体外(OutsideTest )常量池多了一个Object o = null变量还有的话就是LocalVariableTable有点区别,变量在循环体内的话公用了一个变量槽(o和b变量) outsideLoop在stack frame中定义了4个slot, 而intsideLoop只定义了3个slot 在outsideLoop中,变量o和b分别占用了不同的slot,在intsideLoop中,变量o和b复用一个slot。所以outsideLoop的stack frame比intsideLoop多占用1个solt内存。执行以下命令就可以找到字节码中的LocalVariableTable。

javac -g  OutsideTest.java
javap -v  OutsideTest.class
   LocalVariableTable:Start  Length  Slot  Name   Signature28       8     3     o   Ljava/lang/Object;0      46     0  this   Lcom/workit/autoconfigure/autoconfigure/controller/InsideTest;8      38     1  list   Ljava/util/List;10      36     2     n   I44       2     3     b   Ljava/lang/Object;
LocalVariableTable:Start  Length  Slot  Name   Signature0      49     0  this   Lcom/workit/autoconfigure/autoconfigure/controller/OutsideTest;8      41     1  list   Ljava/util/List;10      39     2     o   Ljava/lang/Object;12      37     3     n   I47       2     4     b   Ljava/lang/Object;

这是比较极端的情况下有1个solt的差距,如果把上述的代码 Object b = 2;就不会存在 solt 复用了。

总结

整体看下来貌似内存和效率都差不多。从“「局部变量作用域最小化」”原则上来说,变量声明在循环体内更合适一点,这样代码的阅读性更好。

参考:https://www.zhihu.com/question/31751468

更多精彩推荐
☞图灵奖得主 John E. Hopcroft 等 300 余位 AI 学者“穿越”回宋代开国际 AI 大会,这场面你见过吗?
☞蚂蚁上市员工人均一套大 House,阿里程序员身价和这匹配吗?
☞Robust.ai 获得 1500 万美元融资,嘴炮 Gary Marcus 也难逃真香定律
☞面向全场景的鸿蒙操作系统能有多安全?☞阿里云资深技术专家易立:我对云原生软件架构的观察与思考☞赠书 | 四大通证类型:价值创新的源头
点分享点点赞点在看

Java变量声明在循环体内还是循环体外,你用哪一个?相关推荐

  1. Java 变量声明和赋值

    题目描述 定义一个byte类型的变量b.short类型的变量s.int类型的变量i.long类型的变量a.float类型的变量f.double 类型的变量d.char类型的变量c.boolean类型的 ...

  2. ES6——let-const的变量声明以及模板字符串

    目录 一.let和const 1.let a.阻止变量提升 b.块级作用域 c.不能重复声明 2.const 二.模板字符串 ES5语言有很多地方存在不合理以及不足,比如变量的提升.内置对象的方法不灵 ...

  3. while循环以及for循环的区别

    while: 在while循环语句中:continue的意义是结束本次循环而进入判断条件 break的意义是跳出循环 eg: #include"stdio.h" int main( ...

  4. 循环结构(1):while循环、for循环

    while循环 语法结构: while(表达式) 循环语句: while循环的break: #include<stdio.h> int main() {int i = 0;while (i ...

  5. C语言,if循环 for 循环 while循环 switch循环 do...while()循环

    目录 一.if循环 二. switch循环 break语句 default子句 三.while循环 break在while循环中的作用 continue在while循环中的作用就是 四.for循环 b ...

  6. 使用控制结构——循环语句——基本循环

    在pl/sql中最简单的循环语句时基本循环语句,这种循环语句以loop开始,以endloop结束, 注意,当编写基本循环时,一定要包含exit 语句,否则pl/sql 快会陷入死循环:另外,当使用基本 ...

  7. Java 中把声明变量的语句如果写在循环体内,每次执行时栈内存中的变量和数据是如何变化的?

    问题一:如下面的代码示例 1,JVM 是不是会反复回收旧的变量 a 再重新创建新的变量 a 呢?还是旧的变量 a 一直保留在栈内,只是反复赋值 0 而已呢? 代码示例 1: while (true) ...

  8. C语言循环时无故释放变量吗,C语言 - while循环体内变量重新声明,陷入***循环。...

    今天写一个实验代码时,用到了while(exp)循环.发现exp已经为false.但是依然死循环下去. float T = (float)work[srcNode] / (workAll/6); in ...

  9. java 循环里声明变量赋值_在Java中声明变量外部Foreach循环

    有人可以请赐教我以下事项: public class Loopy { public static void main(String[] args) { int[] myArray = {7,6,5,4 ...

最新文章

  1. android 布局翻页,安卓APP_ 布局(8) —— 基于 RecyclerView 的 ViewPager2翻页
  2. 数值计算领域的“圣经”,图灵出了新版本 | 11月书讯
  3. appium框架之bootstrap
  4. python语言怎么输入-python如何用input输入数组
  5. Java三大特性之封装
  6. windo.open 全攻略
  7. 别名、浅复制与深复制
  8. golang rsa密钥_如何在Golang的地图中检查密钥是否存在?
  9. Python(十七)- Excel操作:xlsxwriter绘制图表
  10. 渗透测试工具Nmap从初级到高级
  11. Python 量化投资实战教程(5) — A股回测KDJ 策略
  12. 苹果系统升级服务器,苹果操作系统升级 正式挥别PowerPC
  13. java获取用户地理位置_Java获取用户访问IP及地理位置的方法详解
  14. 阿里老兵深度雄文:不懂这些,你的复盘都是白费功夫!
  15. 海尔互联网转型成功了吗?
  16. 静电放电防护设计规范和指南
  17. oracle字符串类型的时间常用操作
  18. 如何衡量和提高测试效率
  19. python列表for循环 加入新列表_关于python:使用for循环在列表中添加值
  20. kafka核心技术与实战 思维导图

热门文章

  1. 【loj6029】「雅礼集训 2017 Day1」市场 线段树+均摊分析
  2. POJ2155 Matrix 二维线段树
  3. normalize.css 中文版
  4. ES6的开发环境搭建
  5. 质因数分解(0)P2012_1
  6. 【软工3】迭代二 心得体会及感想
  7. VS.NET的Bug
  8. docker删除所有容器和镜像
  9. CentOS 删除OpenJDK并安装OracleJDK
  10. WIndows10下 MySQL 5.7(社区版)卸载