Java中堆栈及内存分配原理

来源:互联网 发布:rar怎么解压到mac 编辑:程序博客网 时间:2024/03/29 17:25
一、内存分配

1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制。
2. :存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符串常量对象存放在常量池中。)
3. 存放所有new出来的对象,以及数组。
4. 静态域:存放静态成员(static定义的)
5. 常量池:存放字符串常量和基本类型常量(public static final)。
    常量池的优点:
    (1)节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间(2)节省运行时间:比较字符串时,==比  equals()快。对于两个引用变量,只用==判断引用是否相等,也就可以判断实际值是否相等。
      注意: 关于"==",基本数据类型之间应用双等号,比较的是他们的数值。复合数据类型(类)之间应用双等号,比较的是他们在 内存中的存放地址。
            
6. 非RAM存储:硬盘等永久存储空间。

二、堆和栈

       Java 把内存划分成两种:一种是栈内存,另一种是堆内存。在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用。

    栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。 

  堆内存用来存放由 new 创建的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或者对象之后,还可以在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或者对象,引用变量就相当于是为数组或者对象起的一个名称。引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用 new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。


       堆的优势是可以动态地分配内存 大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态 分配内存,存取速度较慢。这也是 Java 比较占内存的原因,实际上,栈中的变量指向堆内存中的变量,这就是 Java 中的指针!


        下面来说几个例子:

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

       2、String str = new String("abc");   String str = "abc";  两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。而第二种是先在栈中创建一个对String类的对象引用变量str,然后通过符号引用去字符串常量池里找有没有"abc"(因为字符串类型是存放在常量池中的),如果没有,则将"abc"存放进字符串常量池 ,并令str指向”abc”,如果已经有”abc” 则直接令str指向“abc”。第二种方法在创建多个“abc”字符串时,实际上只存放了一个对象,这样写有利于节省内存空间。



 


      
  

  




1 0
原创粉丝点击