精华内容
下载资源
问答
  • C++各种变量内存分配

    千次阅读 2017-08-30 13:52:46
    程序在的内存中的分配(常量,局部变量,全局变量,程序代码)一. 在c中分为这几个存储区 1.栈 - 由编译器自动分配释放 2.堆 - 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收 3.全局区(静态区),...

    一. 在c中分为这几个存储区
    1.栈 - 由编译器自动分配释放
    2.堆 - 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收
    3.全局区(静态区),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。- 程序结束释放
    4.另外还有一个专门放常量的地方。- 程序结束释放
                                                                                                                                                 
    在函数体中定义的变量通常是在栈上,用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。在所有函数体外定义的是全局量,加了static修饰符后不管在哪里都存放在全局区(静态区),在所有函数体外定义的static变量表示在该文件中有效,不能extern到别的文件用,在函数体内定义的static表示只在该函数体内有效。另外,函数中的”adgfdf”这样的字符串存放在常量区。比如:




    //main.cpp
    int a = 0;      // 全局初始化区
    char *p1;      // 全局未初始化区
    void main()
    {
        int b;            // 栈区
        char s[] = "abc"; // 栈区
        char *p2; // 栈区
        char *p3 = "123456"; // p3在栈区;   "123456\0" 在常量区, 
        static int c =0;      // 全局(静态)初始化区
        p1 = (char *)malloc(10);
        p2 = (char *)malloc(20); // 分配得来的10和20字节的区域就在堆区 
        strcpy(p1, "123456");    // "123456\0" 放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
    }




    二.在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区
    1.栈,
    就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。
    2.堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
    3.自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。
    4.全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
    5.常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改)

    以上引自http://www.cnblogs.com/JCSU/articles/1051579.html

    三、堆和栈的理论知识
    申请方式
    stack: 由系统自动分配。   例如,声明在函数中一个局部变量   int   b;   系统自动在栈中为b开辟空间
    heap: 需要程序员自己申请,并指明大小,在c中malloc函数
    如p1  =   (char   *)malloc(10);
    在C++中用new运算符
    如p2  =   (char   *)malloc(10);

    但是注意p1、p2本身是在栈中的。

    三。

    VC++ 6.0 编译器编译期存储器分配模型 (内存布局)

    分类: VC学习 194人阅读 评论(0) 收藏 举报

                                                                 VC ++ 6.0  编译器编译期存储器分配模型(内存布局)

                                                                                                                       —-转载自网络

    一、内存区域的划分

          一个由C/C++编译的程序占用的内存分为以下几个部分:

         1)、栈区(Stack):由编译器(Compiler)自动分配释放,存放函数的参数值,局部变的值等。其操作方式类似于数据结构中的栈。

         2)、堆区(Heap ):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配

                 方式倒是类似于链表。

         3)、全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全

                 局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。

         4)、文字常量区:常量字符串就是放在这里的。程序结束后由系统释放。

         5)、程序代码区:存放函数体的二进制代码。

    二、测试案例(源码与反汇编对照)

      

         2.1   测试案例源码与反汇编对照

                 为了能够形象地说明内存布局模型,先来看一段Win32 Console Application代码(表3.1),其中,加粗文字(行最左端为行标号)

         为C源代码,未加粗文字(行最左端为地址)为反汇编后的指令代码。看上去比较零乱,不过一定要耐住性子,后面的文字将基于此。

        

    3.2   内存布局图

            对于该案例,以下几幅图形象地说明了第2节提到的内存5大区域。需要注意的是,图中各区域的起始地址不是绝对的,不同的编译环境可能不完全相同,这里给出的只是一个典型示例。需要注意的是,不同区域地址编址方向也不同。

       

       

       

       

       

       

      

    3、应用

         通过对第3节案例的理解,我们将对一些现象予以解释。

    3.1、变量初始化

            1)局部变量将被分配在栈中,如果不初始化,则为不可预料值。编译时,编译器将抛出一个编号为C4700警告错误(local variable ‘变量名’ used without having been initialized)。

            表4.1代码测试了局部变量未初始化的情况。

          

       该测试的一个典型的输出结果为:-858993460,同时,编译时编译器抛出了一条警告错误。

       2)全局变量如果不初始化,则默认为0,编译时编译器不提示“变量未初始化”。

            表4.2代码测试了全局变量未初始化的情况。

      该测试的输出结果为:0.

       3)全局变量初始化为0与不初始化效果一样。请留意表3.1第9行代码,即

             intinit_array_g1[10]={0};                       //初始化的全局数组1

          等效于:

          int   init_array_g1[10];                       //初始化的全局数组1

          当然,出于谨慎,我们还是建议在使用全局变量前对其初始化。

       3.2 变量初始化对代码空间的影响

             本小节任然讨论变量初始化问题,但出于重视,我们将其独立成小节。现在看两个测试案例。

             案例1:建立Win32 Console Application工程,工程名:Test1,代码如表4.3。

       编译成Debug版本,察看Debug目录下的Test1.exe可执行文件大小,典型大小约184KB(约0.18MB)。

       案例2:建立Win32 Console Application工程,工程名:Test2,代码如表4.4。

       编译成Debug版本,察看Debug目录下的Test2.exe可执行文件大小,典型大小约46MB。

       两个案例唯一区别不过在于是用0还是1初始化 init_array_g1[]数组第0个元素。生成的可执行文件大小却天壤之别。

       上面已经说过,对于全局变量初始化为0与不初始化效果一样。因此,这里的Test1案例并没有对全局变量初始化。

       那么全局变量初始化于不初始化对代码空间又有什么影响呢?

       我们知道,运行于基于冯·诺依曼体系结构系统上的程序,数据和程序是一起存储了。因此,编译时,编译器会将全局变量的初始化数据捆绑到最终生成的程序文件中,而对于未初始化的全局变量只是为其分配(指示)了存储位置,不会将大量的0捆绑到程序中。

       现在再来看以上两个案例。Test1实质上没有初始化全局变量,编译时编译器只是为了init_array_g1[]指出了将要使用的内存位置,而不发生 数据绑定。Test2则不同,它将init_array_g1[0]初始化为1,其它元素全部初始化为0,因此,编译器将把 init_array_g1[]数组的10000000个元素的初始化数据全部捆绑到最终的可执行文件中,导致编译后的文件十分庞大。

        3.3   关于堆和栈

        由于历史原因,我们习惯把堆和栈合在一起称呼(堆栈),然而,在这里我们要严格区分堆和栈的概念。

        例程中声明的局部变量被分配在栈中,而栈的大小是相当有限的(一、两个兆),庞大的数组可能使栈不够用,造成运行期栈溢出(Overflow)错误(注意:不是编译器错误),而堆的大小主要取决于系统可用内存和虚存的多少。下面来看几个例子:

        案例3代码如表4.5所示:

      

       编译该代码,没有编译期错误。执行时却发生了运行期错误(提示Statck Overflow),因为栈空间不够用。

       案例4,把案例3代码改一下,数组定位为全局变量,如表4.6所示:

       

         编译该代码,没有编译期错误,也不发生运行期错误。因为全局变量不是分配在栈中的(注意:也不在堆中),能用多大空间取决于系统可用内存和虚存的多少。

         对于案例3的问题还有一种方法可以解决:动态申请内存空间。

         动态申请的内存空间是在运行期分配的,一旦申请成功,将分配在堆中,因此,大小也是取决于系统可用内存和虚存的多少。

         案例5:把案例3代码用另一种方法改一下,如表4.7所示。

        案例5的内存空间在堆中。还有一点不同于案例4:案例4的内存空间是在编译器分配的,而案例5的内存空间是在运行期分配的,有可能分配不到空间。

        3.4) 地址递减编制方式

        或许其它资料中已经描述了“地址递减”编址方式分配内存的概念,所谓“地址递减“是指编译器编译程序时,按变量声明先后,从可分配内存中从高地址向低地址分配内存。什么意思?还是先来看一个例子。

        案例6是一个有逻辑错误的程序(表4.7所示),不妨称其为“变态”程序。那么它是如何BT的呢?

       

          这个程序没有编译器错误,但却是一个死循环程序。我们想知道的是:它为什么是个死循环,而不是其它什么错误?通过以上文字对内存布局的介绍,我们已经可以很容易解释之。

          仿照第3节内容可以画出内存布局示意图(如图4.1所示,图中起始地址只是一个典型情况)。

          注意,程序中引用了array[10]————数组下标越界(VC++6.0编译器可以检查出显示的下标越界,但是不检查隐式的下标越界)。循环内部会将 所谓的array[10]置1,而从图4.1可知,array[10]实质上就是i,导致程序最终死循环也就理所当然了。

          一切变得明朗起来,我们不仅解释了程序中的问题,同时还明白了“地址递减”编址方式并不神秘,它原来就是我们前面提到的栈内存区的编址方式。



    展开全文
  • 关于内存,环境变量

    千次阅读 2013-07-27 15:51:55
    内存分为4段,栈区,堆区,代码区,全局变量区 BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。 BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。 数据段...

    内存分为4段,栈区,堆区,代码区,全局变量区

    BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。
    BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。

    数据段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。

    代码段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。
    这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。
    在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。代码段是存放了程序代码的数据,
    假如机器中有数个进程运行相同的一个程序,那么它们就可以使用同一个代码段。

    堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,
    可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);
    当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)
    栈(stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,
    也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。
    除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。
    由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。


    (1)内存分段和内存分页一样都是一种内存管理技术,分段:权限保护,分页:虚拟内存.


    (2)分段后,程序员可以定义自己的段,各段有独立的地址空间,象进程的地址空间互相独立一样.


    (3)同一个类的实例分配在一个段中,只有该类的方法可以访问,如果其他类的方法去访问,会因为段保护而出错.可以从硬件上实现类的数据保护和隐藏

    ####################################################################

    分段好处:

    cpu中的段寄存器-------段址(base)和偏移值的上限(limit)。
    段址:有效地址 中,如果有效地址大于limit,便会引发异常。这样就可以限制程序不能范围当前段外的数据,不能访问其他程序的数据。
    面向对象的好处:对象就是一块连续的内存中的数据




    寄存器是特殊形式的内存,嵌入到处理器内部。

             每个进程需要访问内存中属于自身的区域,因此,可将内存划分成小的段,按需分发给进程。
    寄存器用来存储和跟踪进程当前维护的段。偏移寄存器(Offset Registers)用来跟踪关键的数据放在段中的位置。

           在进程被载入内存中时,基本上被分裂成许多小的节(section)。我们比较关注的是6个主要的节:

    (1) .text 节

        .text 节基本上相当于二进制可执行文件的.text部分,它包含了完成程序任务的机器指令。
    该节标记为只读,如果发生写操作,会造成segmentation fault。在进程最初被加载到内存中开始,该节的大小就被固定。

    (2).data 节

    .data节用来存储初始化过的变量,如:int a =0 ; 该节的大小在运行时固定的。

    (3).bss 节

    栈下节(below stack section ,即.bss)用来存储为初始化的变量,如:int a; 该节的大小在运行时固定的。

    (4) 堆节

    堆节(heap section)用来存储动态分配的变量,位置从内存的低地址向高地址增长。内存的分配和释放通过malloc() 和 free() 函数控制。

    (5) 栈节

    栈节(stack section)用来跟踪函数调用(可能是递归的),在大多数系统上从内存的高地址向低地址增长。
    同时,栈这种增长方式,导致了缓冲区溢出的可能性。

    (6)环境/参数节

         环境/参数节(environment/arguments section)用来存储系统环境变量的一份复制文件,
    进程在运行时可能需要。例如,运行中的进程,可以通过环境变量来访问路径、shell 名称、主机名等信息。
    该节是可写的,因此在格式串(format string)和缓冲区溢出(buffer overflow)攻击中都可以使用该节。
    另外,命令行参数也保持在该区域中。

    以win32程序为例。
    程序执行时,操作系统将exe文件映射入内存。exe文件格式为头数据和各段数据组成。

    头数据说明了exe文件的属性和执行环境,段数据又分为数据段,代码段,资源段等,段的多少和位置由头数据说明。
    也就是说,不仅仅只是代码段和数据段。这些段由不同的编译环境和编译参数控制,由编译器自动生成exe的段和文件格式。
    当操作系统执行exe时,会动态建立堆栈段,它是动态的,并且属于操作系统执行环境。

    也就是说,程序在内存的映射一个为exe文件映射,包括数据段、代码段等它是不变的。
    另一个为堆栈段,它是随程序运行动态改变的。

    1、编译器把源代码转化成分立的目标代码(.o或者.obj)文件,这些文件中的代码已经是可执行的机器码或者是中间代码。
    但是其中变量等事物的地址只是一些符号。   
    2、接下来是通过链接器处理这些目标代码,主要目的就是把分立的目标代码连接成一份完整的可执行代码,
    并将其中的地址符号换成相对地址。如果这时候产生错误,我们就可以得到一份地址符号列表,而不是变量列表。   
    3、执行程序的时候操作系统分配足够的内存空间,建立好系统支撑结构后把二进制可执行代码读入内存中。
    在读入过程中内存首址就成了程序的“绝对地址”(实际上还是相对地址,不过是操作系统里的相对地址了)。
    于是绝对地址+相对地址(就是偏移量)就得到了变量的地址。   
    因此,CS的值是由系统填入的,而其它S寄存器的值则是根据程序代码中附加的信息计算后得到的。 

     

    转自:http://blog.csdn.net/yeyuangen/article/details/6766567

    展开全文
  • 实例变量和类变量内存分配

    千次阅读 2013-11-03 12:54:53
    Java向程序员许下一个承诺:无需关心内存回收,java提供了优秀的垃圾回收机制来回收已经分配的内存。大部分开发者肆无忌惮的挥霍着java程序的内存分配,从而造成java程序的运行效率低下! java内存管理分为两方面: ...

     ------- android培训java培训、期待与您交流! ----------

    Java向程序员许下一个承诺:无需关心内存回收,java提供了优秀的垃圾回收机制来回收已经分配的内存。大部分开发者肆无忌惮的挥霍着java程序的内存分配,从而造成java程序的运行效率低下!

    java内存管理分为两方面:

        1,内存的分配:指创建java对象时,jvm为该对象在堆内存中所分配的内存空间。

    2,内存的回收:指当该java对象失去引用,变成垃圾时,jvm的垃圾回收机制自动清理该对象,并回收该对象占用的内存。

    jvm的垃圾回收机制由一条后台线程完成。不断分配内存使得系统中内存减少,从而降低程序运行性能。大量分配内存的回收使得垃圾回收负担加重,降低程序运行性能。

     

    一,实例变量和类变量(静态变量)

        java程序的变量大体可分为成员变量和局部变量。

            其中局部变量有3类:形参、方法内的局部变量、代码块内的局部变量。

                局部变量被存储在方法的栈内存中,生存周期随方法或代码块的结束而消亡

            在类内定义的变量被称为成员变量。没使用static修饰的称为成员变量,用static修饰的称为静态变量或类变量。

    1.1实例变量和类变量的属性

    在同一个jvm中,每个类只对应一个Class对象,但每个类可以创建多个java对象。

    【其实类也是一个对象,所有类都是Class实例,每个类初始化后,系统都会为该类创建一个对应的Class实例,程序可以通过反射来获取某个类所对应的Class实例(Person.class或Class.forName(“Person”))】

     

    因此同一个jvm中的一个类的类变量只需要一块内存空间;但对实例变量而言,该类每创建一次实例,就需要为该实例变量分配一块内存空间。

    非静态函数需要通过对象调用,静态函数既可以通过类名调用,也可以通过对象调用,其实用对象调用静态函数,底层还是用类名调用来实现的

    1.2实例变量的初始化时机

        对实例变量而言,它属于java对象本身,每次创建java对象时都需要为实例变量分配内存空间,并执行初始化。

        从语法角度来看,程序可在三个地方对实例变量执行初始化:

           1. 定义实例变量时指定初始值

           2. 非静态初始块中对实例变量指定初始值

           3. 构造器中对实例变量指定初始值

            其中第1、2种方式比第三种方式更早执行,第1、2种方式执行的顺序与他们在源程序中的排列位置相关,在前面的先执行。

     

            每当程序指定构造器来创建java对象时,该构造器必然会获得执行机会。与此同时,该类所包含的非静态初始化块 和 定义实例变量指定初始值也将会获得执行机会,并且总是在构造器执行之前获得执行。

    class TestDemo {
    	 public TestDemo(){
    		 System.out.println("这是一个构造函数");
    		 System.out.println("ser的值为"+ser);
    	 }
    	 {//这是一个构造代码块
    		 System.out.println("这是一个构造代码块");
    		 ser=4;
    	 }
    	 double ser=3.0;
    	public static void main(String[] args) {
    		TestDemo test=new TestDemo();
    		System.out.println("...........");
    		TestDemo test2=new TestDemo();
    	}
    }
    /*运行结果:
    	  	这是一个构造代码块
    		这是一个构造函数
    		ser的值为3.0
    		...........
    		这是一个构造代码块
    		这是一个构造函数
    		ser的值为3.0
    */

    定义实例变量时指定的初始值,初始块中为实例变量指定初始值的语句的地位是平等的,当经过编译器处理后,他们都将被提取到构造器中。也就是说,对于类定义中的语句:

        double ser = 3.0

    实际上会被分成如下2次执行:

    1.double ser ;创建java对象时系统根据该语句为该对象分配内存

    2.ser = 3.0;赋值动作会被提取到构造器中执行!

    实际上,ser所指定的初始化值每次都会被3.0覆盖,因为定义变量时指定的初始值和初始化块中指定的初始值的执行顺序,与它们在源程序中的排列顺序相同。

     

    1.3类变量的初始化时机

    类变量属于类本身,只有当jvm加载该类时才会为该类的类变量分配内存空间,并执行初始化。

    从程序运行角度看,JVM对一个java类只初始化一次,因此java程序每运行一次,系统只为类变量分配一次内存空间,执行一次初始化。

    从语法角度看,程序可在2个地方对类变量执行初始化:

    1.定义类变量时指定初始值

    2.静态初始化块中对类变量指定初始值。

    两种方式的执行顺序与他们在源程序中排列顺序相同。同样,程序先为所有类变量分配内存空间,再按源代码中两种方法的排列顺序执行相应的初始化值。

     

    总而言之,对象的初始化过程为

    1.在栈内建立变量

    2.类加载进内存

    3.执行静态代码块

    4.在堆内存中开辟空间,分配内存地址

    5.在堆内存中建立对象的特有属性,并进行默认初始化

    6.对属性进行显示初始化

    7.对对象进行构造代码块初始化

    8.对对象进行对应的构造函数初始化

    9.将内存地址赋给栈内存中的变量


    展开全文
  • Java--内存分配及变量存储位置

    千次阅读 2013-07-17 13:01:18
     当在一段代码块定义一个变量时,Java就在栈中 为这个变量分配内存空间,当该变量退出该作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。栈中的数据大小和生命周期是可以确定...

    Java内存分配中的栈

      在函数中定义的一些基本类型的变量数据和对象的引用变量都在函数的栈内存中分配。

      当在一段代码块定义一个变量时,Java就在栈中 为这个变量分配内存空间,当该变量退出该作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。栈中的数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会消失。

     

    Java内存分配中的堆

      堆内存用来存放由new创建的对象和数组。 在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。

      在堆中产生了一个数组或对象后,还可以 在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。  引用变量就相当于是 为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。引用变量就相当于是为数组或者对象起的一个名称。

      引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序 运行到使用 new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍 然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。这也是 Java 比较占内存的原因。

      实际上,栈中的变量指向堆内存中的变量,这就是Java中的指针! 

     

    堆与栈

      Java的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过new、newarray、 anewarray和multianewarray等指令建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存 大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态 分配内存,存取速度较慢。

      栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是 确定的,缺乏灵活性。栈中主要存放一些基本类型的变量数据(int, short, long, byte, float, double, boolean, char)和对象句柄(引用)。

           栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:

      Java代码

      int a = 3;

      int b = 3;

      编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。

      这时,如果再令 a=4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响 到b的值。

      要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b, 它是由编译器完成的,它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。

    Java代码 
    1.int i1 = 9;  
    2.int i2 = 9;  
    3.int i3 = 9;   
    4.public static final int INT1 = 9;  
    5.public static final int INT2 = 9;  
    6.public static final int INT3 = 9;  

    对于成员变量和局部变量:成员变量就是方法外部,类的内部定义的变量;局部变量就是方法或语句块内部定义的变量。局部变量必须初始化。 
    形式参数是局部变量,局部变量的数据存在于栈内存中。栈内存中的局部变量随着方法的消失而消失。 
    成员变量存储在堆中的对象里面,由垃圾回收器负责回收。 
    如以下代码: 
    Java代码 
    1.class BirthDate {  
    2.    private int day;  
    3.    private int month;  
    4.    private int year;      
    5.    public BirthDate(int d, int m, int y) {  
    6.        day = d;   
    7.        month = m;   
    8.        year = y;  
    9.    }  
    10.    省略get,set方法………  
    11.}  
    12.  
    13.public class Test{  
    14.    public static void main(String args[]){  
    15.int date = 9;  
    16.        Test test = new Test();        
    17.           test.change(date);   
    18.        BirthDate d1= new BirthDate(7,7,1970);         
    19.    }    
    20.  
    21.    public void change1(int i){  
    22.        i = 1234;  
    23.    }  
    对于以上这段代码,date为局部变量,i,d,m,y都是形参为局部变量,day,month,year为成员变量。下面分析一下代码执行时候的变化: 
    1. main方法开始执行:int date = 9; 
    date局部变量,基础类型,引用和值都存在栈中。 
    2. Test test = new Test(); 
    test为对象引用,存在栈中,对象(new Test())存在堆中。 
    3. test.change(date); 
    i为局部变量,引用和值存在栈中。当方法change执行完成后,i就会从栈中消失。 
    4. BirthDate d1= new BirthDate(7,7,1970);  
    d1 为对象引用,存在栈中,对象(new BirthDate())存在堆中,其中d,m,y为局部变量存储在栈中,且它们的类型为基础类型,因此它们的数据也存储在栈中。 day,month,year为成员变量,它们存储在堆中(new BirthDate()里面)。当BirthDate构造方法执行完之后,d,m,y将从栈中消失。
    5.main方法执行完之后,date变量,test,d1引用将从栈中消失,new Test(),new BirthDate()将等待垃圾回收。

    常量池 (constant pool)

      常量池指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。

    除了包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值(final)还包含一些以文本形式出现的符号引用,比如:

      ◆类和接口的全限定名;

      ◆字段的名称和描述符;

      ◆方法和名称和描述符。

    如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份。

    String是一个特殊的包装类数据。可以用:

      Java代码

      String str = new String("abc");

      String str = "abc";

      两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。而第二种是先在栈中创建一个对 String类的对象引用变量str,然后通过符号引用去字符串常量池 里找有没有"abc",如果没有,则将"abc"存放进字符串常量池 ,并令str指向”abc”,如果已经有”abc” 则直接令str指向“abc”。

      比较类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==,下面用例子说明上面的理论。

      Java代码

      String str1 = "abc";

      String str2 = "abc";

      System.out.println(str1==str2); //true

      可以看出str1和str2是指向同一个对象的。

      Java代码

      String str1 =new String ("abc");

      String str2 =new String ("abc");

      System.out.println(str1==str2); // false

      用new的方式是生成不同的对象。每一次生成一个。

      因此用第二种方式创建多个”abc”字符串,在内存中 其实只存在一个对象而已. 这种写法有利与节省内存空间. 同时它可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。

      另 一方面, 要注意: 我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,创建了String类的对象str。担心陷阱!对象可能并没有被创建!而可能只是指向一个先前已经创建的 对象。只有通过new()方法才能保证每次都创建一个新的对象。

     

    String常量池问题的几个例子

    示例1:

    Java代码

      String s0="kvill";

      String s1="kvill";

      String s2="kv" + "ill";

      System.out.println( s0==s1 );

      System.out.println( s0==s2 );

      结果为:

      true

      true

    分析:首先,我们要知结果为道Java 会确保一个字符串常量只有一个拷贝。

      因为例子中的 s0和s1中的”kvill”都是字符串常量,它们在编译期就被确定了,所以s0==s1为true;而”kv”和”ill”也都是字符串常量,当一个字 符串由多个字符串常量连接而成时,它自己肯定也是字符串常量,所以s2也同样在编译期就被解析为一个字符串常量,所以s2也是常量池中” kvill”的一个引用。所以我们得出s0==s1==s2;

    示例2:

    示例:

      Java代码

      String s0="kvill";

      String s1=new String("kvill");

      String s2="kv" + new String("ill");

      System.out.println( s0==s1 );

      System.out.println( s0==s2 );

      System.out.println( s1==s2 );

      结果为:

      false

      false

      false

    分析:用new String() 创建的字符串不是常量,不能在编译期就确定,所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。

    s0还是常量池 中"kvill”的应用,s1因为无法在编译期确定,所以是运行时创建的新对象”kvill”的引用,s2因为有后半部分 new String(”ill”)所以也无法在编译期确定,所以也是一个新创建对象”kvill”的应用;明白了这些也就知道为何得出此结果了。

    示例3:

    Java代码

      String a = "a1";

      String b = "a" + 1;

      System.out.println((a == b)); //result = true 

           String a = "atrue";

      String b = "a" + "true";

      System.out.println((a == b)); //result = true 

            String a = "a3.4";

      String b = "a" + 3.4;

      System.out.println((a == b)); //result = true

    分析:JVM对于字符串常量的"+"号连接,将程序编译期,JVM就将常量字符串的"+"连接优化为连接后的值,拿"a" + 1来说,经编译器优化后在class中就已经是a1。在编译期其字符串常量的值就确定下来,故上面程序最终的结果都为true。

    示例4:

    Java代码

      String a = "ab";

      String bb = "b";

      String b = "a" + bb;

      System.out.println((a == b)); //result = false

    分析:JVM对于字符串引用,由于在字符串的"+"连接中,有字符串引用存在,而引用的值在程序编译期是无法确定的,即"a" + bb无法被编译器优化,只有在程序运行期来动态分配并将连接后的新地址赋给b。所以上面程序的结果也就为false。

    示例5:

    Java代码

      String a = "ab";

      final String bb = "b";

      String b = "a" + bb;

      System.out.println((a == b)); //result = true

    分析:和[4]中唯一不同的是bb字符串加了final修饰,对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储到自己的常量 池中或嵌入到它的字节码流中。所以此时的"a" + bb和"a" + "b"效果是一样的。故上面程序的结果为true。

    示例6:

    Java代码

      String a = "ab";

      final String bb = getBB();

      String b = "a" + bb;

      System.out.println((a == b)); //result = false

      private static String getBB() {  return "b";   }

    分析:JVM对于字符串引用bb,它的值在编译期无法确定,只有在程序运行期调用方法后,将方法的返回值和"a"来动态连接并分配地址为b,故上面 程序的结果为false。

     

    关于String是不可变的

           通过上面例子可以得出得知:

      String  s  =  "a" + "b" + "c";

      就等价于String s = "abc";

      String  a  =  "a";

      String  b  =  "b";

      String  c  =  "c";

      String  s  =   a  +  b  +  c;

      这个就不一样了,最终结果等于:

      Java代码

      StringBuffer temp = new StringBuffer();

      temp.append(a).append(b).append(c);

      String s = temp.toString();

      由上面的分析结果,可就不难推断出String 采用连接运算符(+)效率低下原因分析,形如这样的代码:

      Java代码

      public class Test {

         public static void main(String args[]) {

           String s = null;

           for(int i = 0; i < 100; i++) {

               s += "a";

          }

       }

      }

      每做一次 + 就产生个StringBuilder对象,然后append后就扔掉。下次循环再到达时重新产生个StringBuilder对象,然后 append 字符串,如此循环直至结束。如果我们直接采用 StringBuilder 对象进行 append 的话,我们可以节省 N - 1 次创建和销毁对象的时间。所以对于在循环中要进行字符串连接的应用,一般都是用StringBuffer或StringBulider对象来进行 append操作。

      由于String类的immutable性质,这一说又要说很多,大家只 要知道String的实例一旦生成就不会再改变了,比如说:String str=”kv”+”ill”+” “+”ans”; 就是有4个字符串常量,首先”kv”和”ill”生成了”kvill”存在内存中,然后”kvill”又和” ” 生成 “kvill “存在内存中,最后又和生成了”kvill ans”;并把这个字符串的地址赋给了str,就是因为String的”不可变”产生了很多临时变量,这也就是为什么建议用StringBuffer的原 因了,因为StringBuffer是可改变的。

     

    String中的final用法和理解

      Java代码

      final StringBuffer a = new StringBuffer("111");

      final StringBuffer b = new StringBuffer("222");

      a=b;//此句编译不通过

      final StringBuffer a = new StringBuffer("111");

      a.append("222");// 编译通过

      可见,final只对引用的"值"(即内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译期错误。至于它所指向的对象 的变化,final是不负责的。

     

    总结

      栈中用来存放一些原始数据类型的局部变量数据和对象的引用(String,数组.对象等等)但不存放对象内容

      堆中存放使用new关键字创建的对象.

      字符串是一个特殊包装类,其引用是存放在栈里的,而对象内容必须根据创建方式不同定(常量池和堆).有的是编译期就已经创建好,存放在字符串常 量池中,而有的是运行时才被创建.使用new关键字,存放在堆中。

    展开全文
  • C语言中 对变量内存关系的探讨

    千次阅读 2013-07-26 20:59:28
    变量:用来标识(identify)一块内存区域,这块区域的值一般是可以更改的,这就是它“变”的由来,但是我们可以通过使用如const等一些修饰符号来限定这一内存区域的操作特性(characteristic),即变量的操作特性。...
  • Java中变量内存分析

    千次阅读 2013-10-09 22:49:34
    在定义成员变量时可以对其初始化,如果不初始化,java会使用默认的值对其初始化(引用类型为null,布尔类型为false,其余基本类型的初始值都为0);成员变量的作用范围为整个类体; 2. 引用类型:java语言中除基本...
  • JavaScript变量——栈内存or堆内存

    万次阅读 多人点赞 2014-12-13 14:16:14
    堆和栈这两个字我们已经接触多很多次,那么具体是什么存在栈中什么存在堆中呢?... 基本类型就是保存在栈内存中的简单数据段,而引用类型指的是那些保存在堆内存中的对象。   1、基本类型    基本类型有Undef
  • 3.成员变量随着对象的建立建立,随着对象的消失而消失,存在于对象所在的堆内存中   局部变量: 1.局部变量定义在局部范围内:如方法,函数,语句中,只在作用域有效 2.局部变量没有默认初始化值 3.局部变量...
  • Java中实例变量存放在内存中的堆(heap)上; 局部变量存放在内存中的栈(stack)上; 方法是在调用时才压进栈的。关于变量的创建时间: 静态变量是在类被JVM加载(比如在命令行中输入java ClassName)时创建的并...
  • IOS开发—block对外部变量内存管理

    千次阅读 2015-03-21 14:18:29
    block对外部变量内存管理 代码块在ios中通常用于回调,本文主要介绍block对外部变量的管理机制。我们知道如果要在block中使用block外面的变量,如果该变量是局部变量,就要先将其申明为__block类型。为什么呢?...
  • 变量名是否占用内存空间?

    千次阅读 2014-12-21 14:43:34
    变量:用来标识(identify)一块内存区域,这块区域的值一般是可以更改的,这就是它“变”的由来,但是我们可以通过使用如const等一些修饰符号来限定这一内存区域的操作特性(characteristic),即变量的操作特性。...
  • 一个结构体变量的指针就是该变量所占据的内存段的起始地址。可以设一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。指针变量也可以用来指向结构体数组中的元素。 声明一个...
  • C/C++程序内存的各种变量存储区域和各个区域详解

    万次阅读 多人点赞 2018-03-11 17:57:16
    内存栈区: 存放局部变量名;2. 内存堆区: 存放new或者malloc出来的对象;3. 常数区: 存放局部变量或者全局变量的值;4. 静态区: 用于存放全局变量或者静态变量;5. 代码区:二进制代码。知道如上一些内存分配...
  • 方法和成员变量内存中的位置

    千次阅读 2014-03-04 22:37:50
    一:在方法中声明的变,即该变量是局部变量,每当程序调用方法时,系统都会为该方法建立一个方法栈,其所在方法中声明的变量就放在方法栈中,当方法结束系统会释放方法栈,其对应在该方法中声明的变量随着栈的销毁而...
  • delphi变量内存分配与释放

    千次阅读 2008-09-27 23:20:00
    全局的非指针类型,声明后自动分配内存,并初始化值局部的非指针...声明后不自动分配内存,但会随机指向一个地址,所以地址不为nil应用程序可用的内存区分三类:全局变量区(存放全局变量)、栈(Stack)、堆(Heap)。应
  • c++各种类型变量内存分配

    千次阅读 2012-08-24 13:54:04
    程序在的内存中的分配(常量,局部变量,全局变量,程序代码) 一. 在c中分为这几个存储区 1.栈 - 由编译器自动分配释放 2.堆 - 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收 3.全局区...
  • 变量名首写字母使用小写,如果由多个单词组成,从第2个单词开始的其他...2、成员变量随着对象的建立建立,随着对象的消失而消失,存在于对象所在的堆内存中。 3、成员变量有默认初始化值。局部变量:1、局部变...
  • 静态变量static的内存

    千次阅读 2015-10-29 17:31:01
    public class Person { public static void main(String[] args) { PersonDemo pd=new PersonDemo(10); PersonDemo.getName("旺财"); pd.speak(); System.out.print(PersonDemo.name.../*static静态变量与成
  • Java变量以及内存分配(非常重要)

    千次阅读 2016-08-23 19:26:56
    堆栈静态存储区域一个由C/C++编译的程序占用的内存分为以下几个部分1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。2、堆区(heap)— 由程序员...
  • .NET框架-内存管理-变量创建与销毁

    千次阅读 2017-03-03 11:11:08
     从上面的例子中,可以看出建立引用实例的过程要比建立变量的过程更复杂,系统开销更大。那么既然开销这么大,它到底优势何在呢? 引用数据类型强大到底在哪里???    请看下面代码: { //block1 ...
  • java定义变量的时候分配内存的过程

    千次阅读 2018-08-31 12:18:04
    一个由C/C++编译的程序占用的内存分为以下几个部分 1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 2、堆区(heap)— 由程序员分配释放, 若...
  • C++中的成员变量内存分配问题

    千次阅读 2015-03-07 09:20:27
    在C\C++中,通常可以把内存理解为4个分区:栈、堆、全局/静态存储区和常量存储区。下面我们分别简单地介绍一下各自的特点。 1 栈(stack)  通常是用于那些在编译期间就能确定存储大小的变量的存储区,用于在函数...
  • 变量的本质

    千次阅读 2014-12-15 21:08:39
    变量是指其值可以变化的量。在计算机中,指令代码、数据都存储于内存中。变量也需要存储在内存中。在计算机中,每个变量都被分配了一块内存空间,在这些空间存储的就是变量...在编译过程中,编译器会建立一张变量符号表
  • java+内存分配及变量存储位置的区别

    万次阅读 多人点赞 2011-10-13 19:11:35
    Java内存分配与管理是Java的核心技术之一,之前我们曾介绍过Java的内存管理与内存泄露以及Java垃圾回收方面的知识,今天我们再次深入Java核心,详细介绍一下Java在内存分配方面的知识。一般Java在内存分配时会涉及到...
  • ②成员变量随着对象的建立建立,随着对象的消失而消失,存在于对象所在的堆内存中。 ③成员变量有默认初始化值。 局部变量: ①局部变量只定义在局部范围内,如:函数内,语句内等,只在所属的区域有效。 ②局部...
  • 动态分配内存和指向它的指针变量

    千次阅读 2016-03-28 18:51:57
    1.建立内存动态分配的四个库函数:malloc , calloc , free , realloc.这三个函数的声明都在头文件stdlib.h中。以上指针的基类型为void,使用该指针时,应先对它进行强制类型转换。malloc: ------> void *malloc...
  • 全局变量全局变量又叫成员变量,它是声明在类里,函数,静态语句块外的变量,全局变量又分为类变量(静态变量)、实例变量两种. 通过代码来看一下:private long i;//实例变量 private static long j;//类变量实例变量...
  • 局部变量作用域在函数内部,函数执行结束,变量占用的内存会在垃圾回收机制中回收全局变量,作用域在整个类中项目中,使用前需要进行实例化,分配内存空间成员变量,定义在类中的变量静态变量,用statics修饰的变量...
  • 成员变量和局部变量的区别 成员变量: 1、成员变量定义在类中,在整个类中都可以被访问。... 2、成员变量随着对象的建立建立,随着对象的消失而消失,存在于对象所在的堆内存中。 3、成员变量有默认初始...
  • Python中参数的传递本质——建立变量与对象的关联

    千次阅读 多人点赞 2015-10-27 16:37:31
    参考文章1:知乎网站 参考文章2:winterTTr 的个人博客 参考文章3 在python中,类型属于对象,变量是没有...所以,希望大家在看到一个python变量的时候,把变量和真正的内存对象分开。 我们先看一段代码:def add

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 389,782
精华内容 155,912
关键字:

内存变量的建立