精华内容
下载资源
问答
  • 循环外循环内定义变量的区别

    千次阅读 2020-08-10 17:48:38
    循环外定义的话,for循环一直用的是同一块内存空间,效率比较高,但是变量的作用域就大了,耗内存 所以,其实内外都可以的,总之就是空间和时间的权衡,看实际情况了,局部变量的数据类型、大小什么的都有关 下面...

    一、代码展示

    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @Package: raymond
     * @ClassName: Test
     * @Author: tanp
     * @Description: ${description}
     * @Date: 2020/8/10 14:12
     */
    public class Test {
    
        public static void main(String[] args) {
            TestThread2 testThread = new TestThread2();
            testThread.start();
        }
    
        static class TestThread extends Thread {
            @Override
            public void run() {
                for(int i=0; i<10; i++){
                    Map<String,Object> map = new HashMap<>();
                    map.put(i+"",i);
                }
            }
        }
    
        static class TestThread2 extends Thread {
            Map<String,Object> map;
            @Override
            public void run() {
                for(int i=0; i<10; i++){
                    map = new HashMap<>();
                    map.put(i+"",i);
                }
            }
        }
    }
    

    二、问题

    从上面的代码可以看到,TestThread和TestThread2的区别在于map的定义是放在for循环外面还是里面,那么就有一个问题,在写代码的时候到底是要将变量定义在外面还是里面呢。

    三、JVM运行时数据区

    首先我们要知道JVM运行时数据区中有几大块,其中的两块分别为栈和堆

    JVM

    JVM栈是线程私有的,每个线程创建的同时都会创建JVM栈,JVM栈中存放的为当前线程中局部基本类型的变量(java中定义的八种基本类型:boolean、char、byte、short、int、long、float、double)、部分的返回结果以及Stack Frame,非基本类型的对象在JVM栈上仅存放一个指向堆上的地址

    堆(Heap

    它是JVM用来存储对象实例以及数组值的区域,可以认为Java中所有通过new创建的对象的内存都在此分配,Heap中的对象的内存需要等待GC进行回收。

    (1)       堆是JVM中所有线程共享的,因此在其上进行对象内存的分配均需要进行加锁,这也导致了new对象的开销是比较大的

    (2)       Sun Hotspot JVM为了提升对象内存分配的效率,对于所创建的线程都会分配一块独立的空间TLAB(Thread Local Allocation Buffer),其大小由JVM根据运行的情况计算而得,在TLAB上分配对象时不需要加锁,因此JVM在给线程的对象分配内存时会尽量的在TLAB上分配,在这种情况下JVM中分配对象内存的性能和C基本是一样高效的,但如果对象过大的话则仍然是直接使用堆空间分配

    (3)       TLAB仅作用于新生代的Eden Space,因此在编写Java程序时,通常多个小的对象比大的对象分配起来更加高效。

    具体的jvm知识可以查看我的这篇博客https://blog.csdn.net/bird_tp/article/details/88560468

    四、循环内和循环外定义变量的本质区别

    结合上面的堆、栈信息,我们可以明白变量的定义是放在栈里面的,变量的实例化对象是放在堆里面的,所以我们前面两个线程中定义map的本质区别在于

    线程TestThread在栈声明了10个栈内存地址,10个堆内存地址,而TestThread2只声明了1个栈内存地址,10个堆内存地址。TestThread2线程在循环实例化map时,只是通过一个栈内存地址移动引用指向了的10个堆内存地址。

    总结起来就是
    1、在循环外面的定义的变量,只在栈中声明了一次;
    2、在循环里面的定义的变量,循环多少次就在栈中声明了多少次;

    五、循环时具体如何定义变量

    放在循环内部定义,确实会多次申请栈帧的内存空间,但其实话说回来,这样对性能的影响其实可以忽略不计,没什么大的问题。因为栈内存的分配非常之快,仅次于寄存器,所以,可以忽略不计。

    总的来说,循环内的话,每次循环内部的局部变量在每次进for循环的时候都要重新定义一遍变量,也就是执行申请内存空间,变量压入堆栈的过程。
    循环外定义的话,for循环一直用的是同一块内存空间,效率比较高,但是变量的作用域就大了,耗内存
    所以,其实内外都可以的,总之就是空间和时间的权衡,看实际情况了,局部变量的数据类型、大小什么的都有关

    下面粘贴下jdk7的hashmap的get方法,我们可以看到Object k就是定义在循环里面的。

    public V get(Object key) {
            if (key == null)
                return getForNullKey();
            int hash = hash(key.hashCode());
            for (Entry<K,V> e = table[indexFor(hash, table.length)];
                 e != null;
                 e = e.next) {
                Object k;
                if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                    return e.value;
            }
            return null;
        }

    六、总结

    在for循环外面和里面定义变量,对于内存,性能影响其实没有什么多大区别。看个人使用情况而言,对于我个人使用而言,还是习惯定义在外面

    展开全文
  • 在C++里面,我们通常会这样写for循环:for(int i = 0;i;i++) { cout ; }  还有另外一种写法:int i = 0; for(; i;i++) { cout ; }  首先是在内层循环中定义变量到底会不会存在重复分配的问题,这涉及到...

      在C++里面,我们通常会这样写for循环:

    for(int i = 0;i<10;i++)
    {
         cout << i;      
    }

      还有另外一种写法:

    int i = 0;
    for(; i<10 ;i++)
    {
          cout << i;     
    }

      首先是在内层循环中定义变量到底会不会存在重复分配的问题,这涉及到编译器的优化,不过主流编译器(如vs和gcc)这一块优化都比较好,不会反复分配变量。函数的定义是编译器的事情,运行的时候不存在什么定义,更没有什么开销。

       除非是类对象或者结构体对象, 在for循环里面与外面, 开销可能会不一样.基本数据类型, 那是一样的, 编译器肯定会优化这个东西。

      一般来说, 在进入函数时, 所有的栈变量都分配好空间了。所以那个for变量写在哪里都是一样的. 具体你可以看一下反汇编代码, 全部就展现在你眼前了。

      自己在编程的时候要注意不要让上一次的结果影响到下一次循环,比如上一次 a = 3, 当下一次循环在给a赋值的时候出了错误,而你捕获了错误,却没修正a的值,程序继续执行,那么这时候a=3还成立,可能就会有问题了,如果是每次都重新定义,那就不存在这样的问题。

      最后总结:

      1、对于使用int等基本数据类型作为循环变量,只要你用的优化方面足够给力的主流的编译器,完全不需要关心在循环外还是循环内定义循环变量。

      2、如果循环变量本身是复杂的对象,建议在循环外定义好,并且在for循环的赋值语句、判断语句中,都要避免重复创建对象。

    展开全文
  • 循环内创建对象和循环外创建对象

    千次阅读 2018-06-01 10:15:05
    我认为应该具体情况具体分析:1、如果循环内没有需要将对象添加到集合内等一些情况,则应该在循环外创建对象,这样可以节省内存,不用多次创建对象造成内存的过度使用。 String end = ""; for(int i = 0 ; ...
    对于在循环外创建对象比较好还是在循环内创建对象好问题,我认为应该具体情况具体分析:
    
    1、如果循环内没有需要将对象添加到集合内等一些情况,则应该在循环外创建对象,这样可以节省内存,不用多次创建对象造成内存的过度使用。
    String end = "";
    for(int i = 0 ; i<readfiles.size(); i++){
    End = end+readfiles.get(i);
    }

    2、如果循环内有将对象添加到集合内等情况,则需要在循环内创建对象。
    for(int i = 0 ; i<readfiles.size(); i++){
    file = readfiles.get(i);
    FileVO filevo = new FileVO();
    filevo.setId(ran.createID(20));
    list.add(filevo);
    }

    为什么会有这两种情况呢?这是因为集合添加对象的时候是添加对象的引用,而不是对象的本身。因为在循环外创建对象,所以对象只创建了一次,循环时对对象的赋值,都是对同一个引用代表的对象进行进行赋值。集合内添加的对象一直随着对象的改变而改变,结果就是集合里面所有的对象的都将是最后一次循环赋值的对象。

           

    在集合外创建对象:
    FileVO filevo = new FileVO();
    List<FileVO> lists = new ArrayList<FileVO>();
    for(int i = 0; i<4; i++){
    filevo.setId(RandomID.createID(8));  //RandomID.createID(8)生成8位随机数
    lists.add(filevo);
    }
    for(FileVO s : lists){
    System.out.println(s.getId());
    }
        输出结果:
        braRsJRr
        braRsJRr
        braRsJRr
        braRsJRr
    在集合内创建对象:
        List<FileVO> lists = new ArrayList<FileVO>();
    for(int i = 0; i<4; i++){
    FileVO filevo = new FileVO();
    filevo.setId(RandomID.createID(8));     //RandomID.createID(8)生成8位随机数
    lists.add(filevo);
    }
    for(FileVO s : lists){
    System.out.println(s.getId());
    }
        输出结果:
         7h3kvp56
        JxMv5LVz
        elar0K7L

        PFVa5zsM

    3、 对于某些不变的对象(比如String)不适合情况二,因为即使在循环外创建对象也不会造成集合内的对象随着对象值改变而改变,这种时候在循环外创建对象就行。

    展开全文
  • 在以行优先排序的编译器上,如果外循环是列索引,内循环是行索引,有可能会发生频繁地进行缺页,换页的操作(准确地说是n*m次),严重影响程序的性能。当然,这里只是可能,具体情况和你的数组大小,系统的页大小...

    ∇ \nabla 联系方式:

    e-mail: FesianXu@gmail.com

    QQ: 973926198

    github: https://github.com/FesianXu

    知乎专栏: 计算机视觉/计算机图形理论与应用

    微信公众号
    qrcode


    在图像处理相关的代码中,我们经常有类似于以下的代码,去遍历多维数组(张量)的每一个元素:

    #define LENGTH 10000
    void proc(){
    	uint8 datas[LENGTH][LENGTH];
    	int i, j;
    	long long sum = 0;
    	for (i = 0; i < LENGTH; i++){
    		for (j = 0; j < LENGTH; j++){
    			sum += datas[i][j];
    		}
    	}
    }
    

    其中的sum += datas[i][j];从语义上是和sum += datas[j][i];效果一致的,然而从性能上来说是否是一致的呢?答案是不是的,要看程序的编译优化程度。我们会发现,当循环变量处于内循环和外循环时,其性能是不一样的,即便是运算结果一致。


    我们知道不管是一维数组还是多维数组,在内存中都是线性排列的,以二维数组为例子,为了能将二维的数组拉成一维的,一般需要考虑编译器在编译代码时,其在内存中是行优先(row-major)排列还是列优先(colum-major)排列的。如下图所示,如果一个数组是行优先排列的,那么其在连续内存上的排列顺序如红色线的顺序。举个例子,比如现在有个数组int vars[3][3],如果是行优先排列的,那么有:(===的意思是等价, (vars+i)表示对以vars作为数组指针的前提下,偏移i个元素的地址,而*(vars+i)是对其进行取内容。)

    vars[0, 0] === *(vars+0);
    vars[0, 1] === *(vars+1);
    vars[1, 0] === *(vars+3);
    ==>
    vars[i, j] === *(vars+3*i+j)
    

    如果是列优先排列呢,则是

    vars[0, 0] === *(vars+0);
    vars[0, 1] === *(vars+1*3+1);
    vars[1, 0] === *(vars+0*3+1);
    ==>
    vars[i, j] === *(vars+3*j+i)
    

    在这里插入图片描述
    到这里为止都好理解,不过后续我们需要理解的一点是,我们现在跑得程序很多都不是在裸机上跑的。这里指的裸机就是没有操作系统的计算机,比如单片机等,或者是没有任何操作系统的其他CPU。在操作系统上跑程序,那么我们的程序的内存空间其实都是虚拟内存空间,是一种逻辑地址,其和物理的内存位置是没有必然关联的,需要受到操作系统的控制。采用虚拟内存有很多好处,其中最明显的就是:

    1. 不需要考虑不同程序之间的相对地址偏移,每个程序都有其独自的内存空间,其地址范围都是从0到系统定义的最大值max_mem
    2. 虚拟内存可以看成是一个巨大的线性内存空间,不需要考虑内存不足的情况,因为当内存不足的情况或者需要访问的数据不在内存上时,就发生了缺页错误(page fault),操作系统会自动进行“换页”(paging),将物理内存暂时不用了的内存页保存到存储空间巨大的硬盘上,然后把需要读取的内存加载到内存上。在这种情况下,可以把整个硬盘都看成是一个可以换入和切出的内存(虽然速度很慢,没法和真正的内存比)

    虚拟内存的细节太多,是操作系统设计的一个主要概念,其他细节需要参考其他书籍,比如[1,2]。我们在这里需要知道的是,我们程序中随时可能遇到数据在内存中找不到的事情发生,这个时候就会发生换页的操作,从硬盘中加载数据到内存(IO操作)是非常花时间的,因此很多程序的瓶颈都会在此。我们这里的关键点也正是在此。

    在一个以行优先排序的编译器上,每一行的数据在内存地址上都是比较接近的,而列数据的地址总是相差着sizeof(data_type) * N,其中 N N N是每一行的元素数量。这就导致每一行的数据可能是处于同一个内存页帧(page frame)上的,而每一个列的数据处于不同的页帧上。在以行优先排序的编译器上,如果外循环是列索引,内循环是行索引,有可能会发生频繁地进行缺页,换页的操作(准确地说是n*m次),严重影响程序的性能。当然,这里只是可能,具体情况和你的数组大小,系统的页大小设置等有关。(这里的缺页特别在物理内存比较小的时候更为严重)

    这里举个代码例子,说明性能上的差别。


    考虑代码:

    # code 1
    #define LENGTH 20000
    int main(){
    	float vector[LENGTH][LENGTH];
    	int i, j;
    	float sum = 0.0f;
    	for (i = 0; i < LENGTH; i++){
    		for (j = 0; j < LENGTH; j++){
    			sum += datas[i][j];
    		}
    	}
    	return 0;
    }
    
    # code 2
    #define LENGTH 20000
    int main(){
    	float vector[LENGTH][LENGTH];
    	int i, j;
    	float sum = 0.0f;
    	for (i = 0; i < LENGTH; i++){
    		for (j = 0; j < LENGTH; j++){
    			sum += datas[j][i];
    		}
    	}
    	return 0;
    }
    

    这两个代码除了索引的顺序不同(相当于内外循环调换)之外,其他别无差别。我们为了防止编译器对代码进行优化,影响分析,我们采用-O0编译参数以去掉任何gcc的优化。(其实用其他优化等级效果也是类似的,读者可以自行尝试,并且用指令gcc -O0 -S code1.c观察分析其汇编代码。)

    gcc -O0 code1.c
    /usr/bin/time -v ./a.out
    

    我们用/usr/bin/time分析程序的运行时间,我们有这两者的运行时间分别为:

    在这里插入图片描述
    code 1的运行时间为2.05s
    在这里插入图片描述
    而code 2的时间则变成了5.68s,性能明显差code 1很多。

    但是如果我们把数组的大小从20000改成200会怎么样呢?我们发现其性能将不会有明显区别了,就是因为尺寸大小的不同影响了页帧的换页过程。


    Reference

    [1]. Bryant R E, David Richard O H, David Richard O H. Computer systems: a programmer’s perspective[M]. Upper Saddle River: Prentice Hall, 2003.
    [2]. Tanenbaum A S. Structured computer organization[M]. Pearson Education India, 2016.

    展开全文
  • 举个例子,一个10次的一层循环在执行时,除了在第一次和最后一次会预测错误,其他8次都会预取成功,避免了执行转移指令时重新取出新指令造成的时间浪费。  所以,当有两层循环,外层循环数为A,内层为B,A...
  • 关于变量在for循环内外定义的思考 在c#或java里面,我们通常会这样写for循环: for(int i = 0;i10;i++) { Console.WriteLine(i); } 前一阵子,我突发奇想,如果按照下面这样写,会不会比上面的运行效率高...
  • new对象在for循环内与循环外的区分

    千次阅读 2019-01-23 13:47:51
    遇到的问题:excle导入文件时java代码解析入数据库发现数据是同一条。 Excle导入模板 数据库查询发现都是第二条,也就是说前面的全部覆盖。...数据库的记录是一样的,说明插入的时候就错了。。 ...
  • 循环使用之打印星号

    千次阅读 2015-01-10 13:42:40
    也就是说内循环完了之后外循环才会i++ 当i=0时,j=0,j++  i=0时,j=1,j++  i=0 j=2,j++  i=0, j=3,j++  i=0, j=4,j++ i++ i=2 j=0,j++ i=2 j=1,j++ ...
  • 对于for循环的嵌套循环的理解

    千次阅读 多人点赞 2016-03-18 08:59:50
    循环嵌套是外循环里套个内循环,外循环执行一次,内循环全部执行完,直到外循环执行完毕,整个循环结束。循环次数:外循环的次数*内循环的次数;例:for(int i=0;i;i++){ for(int j=0;j;j++) { System.out.print(...
  • python:使用循环找一定范围内的素数 ​ 前言:什么是素数?...# 首先要知道外循环是要判断的数字,而内循环则是外边数字与2-这个数字的判断 while i <= 100: # 1、首先要知道0、1不是素数,所
  • 最近写代码的时候,一直在思考这个问题,现在空了,写篇文章记录一下吧! 先来看一段源代码,三种场景:public static List<Demo> for1(){ System.out.println("1."+System.currentTimeMillis()); Demo demo = ...
  • 变量的声明是在栈中进行的,而实例化则是在堆中,故栈中只有基础数据与实例的引用。...2、在里面的定义的变量,循环多少次就在栈中声明了多少次; 3、在内存或说资源节省方面,肯定是在外面定义较好,是摧荐的写法;
  • 关于变量在for循环内外定义的思考

    千次阅读 2017-04-13 14:58:36
    关于变量在for循环内外定义的思考 在c#或java里面,我们通常会这样写for循环: for(int i = 0;i10;i++) { Console.WriteLine(i); } 前一阵子,我突发奇想,如果按照下面这样写,会不会比上面的...
  • 然后发现在while循环中只要整个循环体都被同步代码块包含的话就只要满足条件线程获得锁后不会释放锁,尝试了一下在同步代码块随便加一个都可以释放锁,所以想问一下while循环中判断的时候什么不会释放锁?...
  • 一、什么是事件循环 JS的代码执行是基于一种事件循环的机制,之所以称作事件循环,MDN给出的解释为因为它经常被用于类似如下的方式来实现 while (queue.waitForMessage()) { queue.processNextMessage(); } ...
  • try catch放在循环内外的区别

    万次阅读 2012-11-09 10:17:47
     二者的区别是如果test2中循环内容抛出异常的话,就直接跳出了循环,而test1在执行中抛出异常会继续循环。  至于二者选择哪种方式更好就要看你循环的内容了,①如果是为了计算一个结果的话,其中一个遇到异常...
  • 通过调试发现,当外循环进行第一次的时候,内循环正常工作,可是,当外循环进行第二次以后,内循环无法正常工作,根本跳不到内循环里面去,经过一番调试,终于有了发现,究其原因还是循环对象出了问题,因为我的第二...
  • for循环与函数

    千次阅读 2019-08-25 17:27:24
    1.1应用:如果某个语句需要被执行【多次】的时候,可以使用For 循环。 1.2为什么遍历数组? 1.3如果想要获取到数组中每一条数据,arr[索引值]多次执行。所以使用for循环 1.4 将arr[索引值]作为循环体中语句,索引值...
  • 1 情形描述 这里使用while双层循环时踩的炕, ...使用debug的时候,当left指向t,right指向c时,此时,满足外层条件,会进入外层while循环体,执行循环体;内层循环的第一个while作用是,从left开始寻找下一个元...
  • 目标:判断用户输入的数据是否是数字,如果不是,提示用户重新输入,使用while true循环实现问题:当输入第一个数据是字符时,catch块捕获到异常执行完catch块后循环输出
  • 10. C语言 -- 循环结构:for 语句和嵌套循环

    万次阅读 多人点赞 2018-10-19 19:18:09
    本文主要讲述 C 语言循环结构中的 for 语句,介绍了表达式的省略、逗号表达式和在表达式中定义变量这三种灵活的使用方式,举例说明了循环嵌套的使用方式。
  • 1、do…while循环语句 格式: do{ 循环执行体 }while(循环条件); 注意该循环和while循环的区别: 当首次的循环条件结果是false,那么while循环的执行体是不执行的, 但是do…while循环的执行体无条件执行一次; 这个...
  • 不论什么时候只要能用单线程就不用多线程,只有在需要响应时间要求比较高的情况下用多线程 某此操作允许并发而且该操作有可能阻塞时, 用多线程. 例如SOCKET, 磁盘操作. 使用多线程编程可以给程序员带来很大的灵活...
  • python:while循环、for循环

    千次阅读 多人点赞 2018-03-17 22:59:56
    循环: 在python编程中,python提供了for循环和while循环 while循环: 基本语法为: while 判断条件: 执行语句...... 1、while循环用于循环执行程序,以处理需要重复处理的任务。 2、while循环用于多次...
  • 循环神经网络(RNN)

    千次阅读 2018-11-04 22:13:17
    微信公众号  本文同步更新在我的微信公众号里,地址:... ... 目录 1. 基本循环神经网络 2. 双向循环神经网络 2.1 双向循环神经网络的介绍 2.2 双向循环神经网络的训练 ...
  • Unity中几种循环——C# 循环(9)

    千次阅读 2019-02-28 11:53:25
    有的时候,可能需要多次执行同一块代码。一般情况下,语句是顺序执行的:函数中的第一个语句先执行,接着是第二个语句,依此类推。 编程语言提供了允许更为复杂的执行路径的多种控制结构。 循环语句允许我们多次...
  • js | label是什么,在循环中有什么

    千次阅读 2018-07-28 16:40:21
    在看《JavaScript高级程序设计》第三版的第三章时发现label语句,在平常学习js基本没有遇见,经过学习和练习,...先来一个正常的for循环: var num = 0; for (var i = 0 ; i &lt; 10 ; i++){ for (var j ...
  • 在stm32中使用while循环导致程序卡死

    千次阅读 2016-07-16 10:56:40
    今天在使用stm32的过程中发现这样一个问题,程序莫名卡死。 先来看导致卡死的代码 while ( ReadRawRC ( CommandReg ) &amp; 0x10 ); 这是我们在程序中经常用到的写法,容易理解,程序的作用的读出一份数据...
  • volatile 变量提供了线程的可见性,并不能保证线程...什么是线程的可见性: 锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility)。互斥即一次只允许一个线程持有某个特定的锁

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 239,348
精华内容 95,739
关键字:

外循环什么时候使用