关于【禁止new对象时在for循环内定义申明变量】

文章目录

  • 简介
  • 代码分析
  • 反编译之后对比
  • 性能测试
  • 内存与垃圾回收情况
  • JDK和常用框架怎么写
  • 总结
  • 依赖

简介

不知道是谁最先提出了一个不要将变量定义在循环内。

然后我们在代码扫描中有一项是:【禁止new对象时在for循环内定义申明变量】

我也好奇为什么不能?

是影响了性能?还是影响了内存?还是影响了垃圾回收?

我们站在制定这条规则的人的角度来想一想可能的原因:

  1. 变量定义在for循环内,每次都要为变量引用分配空间,效率低
  2. 变量定义在for循环内,有引用,不利于内存回收

真的如此吗?我们先从代码分析一下。

代码分析

我们先从代码的角度分析,局部变量定义在循环外和循环内的区别。

不熟悉局部变量和字节码的朋友可以先看一看:
JVM字节码与局部变量

示例代码如下:

import java.util.HashMap;
import java.util.Map;

public class Main {

    private static final int limit = 1000_0000;

    public static void main(String[] args) {

    }

    public void out() {
        Map<String, String> base;
        for (int i = 0; i < limit; i++) {
            base = new HashMap<>();
            base.put("1", "哦啊好");
            System.out.println(base);
        }
    }

    public void in() {
        for (int i = 0; i < limit; i++) {
            Map<String, String> base = new HashMap<>();
            base.put("1", "哦啊好");
            System.out.println(base);
        }
    }
}

我们可以:

# 编译class
javac Main.java

# 查看字节码
javap -v Main.class

在这里插入图片描述

我们可以看到,无论变量定义在循环外还是循环内,base变量在局部变量表都只占据了一个slot。

自然,也就没有每次循环都要给变量引用分配空间的问题。至于base堆上的内存空间,无论变量定义在循环内外,肯定都要分配,没有区别。

至于垃圾回收,无论变量base在循环内还是循环外,局部变量base引用的都是最新new出来的Map对象,旧的对象不被引用,自然可以被回收,一点也不影响。

基本一模一样,不信我吗可以看看反编译之后的代码。

反编译之后对比

//
// Source code recreated from a .class file
// (powered by FernFlower decompiler)
//

package vip.meet;

import java.util.HashMap;
import java.util.Map;

public class Main {
    private static final int limit = 10000000;

    public Main() {
    }

    public static void main(String[] args) {
    }

    public void out() {
        for(int i = 0; i < 10000000; ++i) {
            Map<String, String> base = new HashMap();
            base.put("1", "哦啊好");
            System.out.println(base);
        }

    }

    public void in() {
        for(int i = 0; i < 10000000; ++i) {
            Map<String, String> base = new HashMap();
            base.put("1", "哦啊好");
            System.out.println(base);
        }

    }
}

我们可以看到反编译之后,out方法的变量被放在循环中了,变得和in一模一样了。

有朋友肯能说,我看到了,他们字节码不一样啊,反编译出来的代码不能说明什么。

把out稍微改一下,将int i定义放在Map的base之前,字节码就一样了。

public void out() {
    int i = 0;
    Map<String, String> base;
    for (; i < limit; i++) {
        base = new HashMap<>();
        base.put("1", "哦啊好");
        System.out.println(base);
    }
}

如果,还觉得有疑问,我们下面来做点性能测试。

性能测试

我们先用benchmark做一些性能测试:

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.TimeValue;

import java.util.HashMap;
import java.util.Map;

public class BenchmarkLocalVarTest {

    private static final int limit = 10000_0000;

    @Benchmark
    public void out() {
        Map<String, String> base;
        for (int i = 0; i < limit; i++) {
            base = new HashMap<>();
            base.put("1", "哦啊好");
        }
    }

    @Benchmark
    public void in() {
        for (int i = 0; i < limit; i++) {
            Map<String, String> base = new HashMap<>();
            base.put("1", "哦啊好");
        }
    }

    public static void main(String[] args) throws RunnerException {
        Options options = new OptionsBuilder()
                .include(BenchmarkLocalVarTest.class.getSimpleName())
                .forks(1)
                .timeout(TimeValue.seconds(10))
                .threads(1)
                .warmupIterations(1)
                .warmupTime(TimeValue.seconds(10))
                .measurementTime(TimeValue.seconds(2))
                .measurementIterations(5)
                .build();
        new Runner(options).run();
    }
}
Benchmark                   Mode  Cnt  Score   Error  Units
BenchmarkLocalVarTest.in   thrpt    5  0.349 ± 0.037  ops/s
BenchmarkLocalVarTest.out  thrpt    5  0.335 ± 0.039  ops/s

如果还有朋友觉得这个测试只能说明性能,不能看出内存和垃圾回收,那我们在来看一下内存和垃圾回收。

内存与垃圾回收情况

变量定义在循环内gc情况:
变量定义在循环内gc情况
变量定义在循环内内存情况:
变量定义在循环内内存情况
变量定义在循环外gc情况:
变量定义在循环外gc情况
变量定义在循环外内存情况:
变量定义在循环外内存情况
我们可以看到,没有太大区别,如果觉得上面逻辑太简单,可以自己试一下更复杂的情况。

JDK和常用框架怎么写

如果这样还不放心,那我们再看一下其他大神怎么写的:

JDK LinkedBlockingDeque:
JDK-LinkedBlockingDeque

Spring ResourceUrlProvider:
Spring-ResourceUrlProvider
apache io FileUtils:
apache io FileUtils

guava Files:
guava Files

总结

综上所述,【禁止new对象时在for循环内定义申明变量】并没有什么意义。

甚至,还带来了负面效果:

  1. 让变量作用域变大,存在被无意引用的风险
  2. 更容易产生变量名冲突
  3. 可读性变差

依赖

<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>1.36</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>1.36</version>
</dependency>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/875757.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

e冒泡排序---复杂度O(X^2)

排序原理: 1.比较相邻的元素。如果前一个元素比后一个元素大&#xff0c;就交换这两个元素的位置。 2.对每一对相邻元素做同样的工作,从开始第一对元素到结尾的最后一对元素。最终最后位置的元素就是最大值, public class 冒泡排序 {public static void main(String[] args) {I…

学习使用LangGraph x GPT-Researcher构建一个多智能体架构的AI自主研究助理

原文&#xff1a;学习使用LangGraph x GPT-Researcher构建一个多智能体架构的AI自主研究助理 - 百度智能云千帆社区 本文为大家剖析一个通过多智能体协作来完成的AI研究助理&#xff0c;可以用来帮助进行各种综合的在线研究任务并输出报告。该应用基于LangGraph以及开源的GPT-…

electron有关mac构建

针对 Mac M1/2/3 芯片的设备&#xff0c;proces.archarm64. 执行下面命令&#xff0c;检查下按照的 node.js 版本是不是 intel x64 指令集&#xff0c;如果是的话安装下 arm64 指令集的 node.js终端中执行以下命令&#xff1a;node -p process.arch 对应的node版本也是arm版 …

YoloV10 训练自己的数据集(推理,转化,C#部署)

目录 一、下载 三、开始训练 train.py detect.py export.py 超参数都在这个路径下 四、C#读取yolov10模型进行部署推理 如下程序是用来配置openvino 配置好引用后就可以生成dll了 再创建一个控件&#xff0c;作为显示 net framework 4.8版本的 再nuget工具箱里下载 …

春之学习:SpringBoot在线教育平台构建

第三章 系统分析 3.1 系统设计目标 在线视频教育平台主要是为了用户方便对首页、个人中心、用户管理、教师管理、课程信息管理、课程类型管理、我的收藏管理、系统管理、订单管理等信息进行查询&#xff0c;也是为了更好的让管理员进行更好存储所有数据信息及快速方便的检索功能…

僵尸网络开发了新的攻击技术和基础设施

臭名昭著的 Quad7 僵尸网络&#xff08;也称为 7777 僵尸网络&#xff09;不断发展其运营&#xff0c;最近的发现表明其目标和攻击方法都发生了重大变化。 根据 Sekoia.io 的最新报告&#xff0c;Quad7 的运营商正在开发新的后门和基础设施&#xff0c;以增强僵尸网络的弹性&a…

K8s利用etcd定时备份集群结合钉钉机器人通知

如何通过脚本的方式进行K8s集群的备份 查看K8s中master节点中etcd集群的状态 kubectl get pods -n kube-system | grep etcd由于使用的etcd服务是K8s搭建时自身携带的,并不是独立搭建的etcd集群信息。使用 K8s 搭建集群时,etcd 是 Kubernetes 集成的一个重要组件因此需要查…

DDR3AXI4接口读写仿真

前文已经介绍了DDR3和AXI4总线的相关知识&#xff0c;我们知道MIG ip核除了可以生成native接口还能生成AXI4接口&#xff0c;今天就练习一下将AXI4接口的DDR3打包成FIFO。首先我们生成一个AXI4接口的MIG ip核&#xff0c;其余步骤与Native接口的ip核相同&#xff0c;如果我们勾…

vue3.0 使用echarts与echarts-gl 实现3D饼图

效果 安装echarts npm install echarts npm install echarts-gl 3d饼图组件&#xff1a; <template><div style"width: 100%; height: 100%" ref"echart"></div> </template><script setup> import { reactive, ref, onMou…

质量追溯管理在MES系统中举足轻重

1. 质量追溯管理概述 质量追溯管理是指通过记录和监控产品在生产过程中的关键信息&#xff0c;确保在产品出现质量问题时&#xff0c;能够迅速追踪到问题源头&#xff0c;并采取相应措施的一种管理方法。在现代制造业中&#xff0c;质量追溯管理对于保障产品质量、提升客户满意…

关于 vue/cli 脚手架实现项目编译运行的源码解析

1.vue项目运行命令解析 在日常开发中&#xff0c;vue 项目通过vue-cli-service脚手架包将项目运行起来&#xff0c;常用的命令例如&#xff1a; npm run serve npm run build 上述执行命令实际一般对应为项目中 package.json 文件的 scripts属性中编写的脚本命令&#xff0c;在…

Python 课程5-NumPy库

在数据处理和科学计算中&#xff0c;NumPy 是一个非常强大且基础的库。除了基本的创建数组功能之外&#xff0c;NumPy 提供了许多强大的函数和方法&#xff0c;用于执行高级的矩阵运算、统计分析、逻辑操作等。以下是一些常用且非常有用的 NumPy 指令&#xff0c;涵盖了创建数组…

java: 程序包org.junit.jupiter.api不存在

明明idea没有报错&#xff0c;引用包也没问题&#xff0c;为啥提示java: 程序包org.junit.jupiter.api不存在&#xff1f; 配置&#xff01;还TMD是配置&#xff01; 如果是引用包的版本不对或者其他&#xff0c;直接就是引用报错或者pom里面飘红了。 这个应该是把generat…

设置使用阿里云服务器DNS

由于云服务器是从腾讯云迁移到阿里云&#xff0c;然后使用ssl验证时一直无法使用dns验证&#xff0c;也无法创建三级域名&#xff0c;原来需要把阿里云服务器改成阿里云的dns使用 如果使用其他服务器DNS会下面会显示当前DNS服务器&#xff0c;

冯诺依曼体结构与系统

冯诺依曼结构 我们的计算机&#xff0c;以及服务器&#xff0c;还有我我们日常使用的洗衣机都遵循冯诺依曼体结构。 以我们日常使用qq聊天时举例&#xff0c;冯诺依曼体结构可以这样画 截至目前&#xff0c;我们所认识的计算机&#xff0c;都是有一个个的硬件组件组成 输入单元…

基于SpringBoot+Vue+MySQL的美术馆管理系统

系统展示 用户前台界面 管理员后台界面 系统背景 随着文化艺术产业的蓬勃发展&#xff0c;美术馆作为展示与传播艺术的重要场所&#xff0c;其管理工作变得日益复杂。为了提升美术馆的运营效率、优化参观体验并加强艺术品管理&#xff0c;我们开发了基于SpringBootVueMySQL的美…

SAP B1 营销单据 - 单据字段介绍(中)

背景 营销单据&#xff0c;SAP B1 中一群神秘的单据&#xff0c;在官方说明文档中并未指明【营销单据】范围&#xff0c;却经常使用这一说法。它们结构相似&#xff0c;在 用户定义字段(UDF) 功能里统一受【营销单据】部分增加字段的影响&#xff0c;可以相互复制&#xff08;…

POI生成Excel文件增加数据验证(下拉序列)

POI版本为5.2.2 正常的如果不超过255字符的数据验证可以参照如下代码&#xff1a; /*** <p>设置某列的数据验证</p>* param Sheet 作用于哪一个sheet* param colIndex 需要增加数据验证的列的索引* String[] names 数据验证的序列&#xff0c;就是excel下拉列表的内…

codesys将自定义的功能块或者函数保存到本地库

将通过ST代码实现的自定义功能保存到codesys的本地库&#xff0c;其他project可以直接实现调用。提高灵活性和效率。 1、创建库工程 这里可能会提示涉及个别库没有安装或版本更新&#xff0c;根据提示安装对应库或更新即可。 2、添加功能块和函数 3、编写功能块和函数的参数定…

【Linux】查看操作系统开机时初始化的驱动模块列表的一个方法

这个方法是摸索出来的&#xff0c;也不一定对&#xff1a; 1、驱动层module_init(module_init_function)作为模块初始化&#xff0c;并且提供模块内部初始化的函数名&#xff1b; 2、找到所有驱动目录drivers下所有module_init(module_init_function)&#xff0c;在内核6.9.0…