vlambda博客
学习文章列表

Java开发——7.内存分配(堆、栈以及参数的值传递+引用传递)

内存:

内存是计算机中的重要原件,临时存储区域,作用是运行程序。

我们编写的程序是存放在硬盘中的,在硬盘中的程序是不会运行的。

必须放进内存中才能运行,运行完毕后会清空内存。 

Java虚拟机要运行程序,必须要对内存进行空间的分配和管理。 


此处补充内存和硬盘的区别:

1.内存是指安装在系统板上的随机存取内存,而硬盘驱动器是磁盘的主轴,也称为硬盘。

2.内存容量范围从 128 MB 到 4 GB,而硬盘容量范围从 320 GB 到 1TB。

3.内存中的存储类型是临时的(如果一断电内存中的数据就会丢失),而硬盘中的存储类型是永远。

4.内存:不允许我们存储个人数据;而是用于存储计算机数据。它读取和写入数据,并且会继续保存在内存,除非系统关闭。硬盘是一种可预测的存储器,可以让用户存储和擦除数据;存储在硬盘上的所有数据在使用期间和使用后都不会自动清除。

拓展:

RAM,一般指随机存取存储器。 

随机存取存储器(英语:Random Access Memory,缩写:RAM),也叫主存,是与CPU直接交换数据的内部存储器。RAM即大家常说的运行内存,简称运存,是一种在手机中用来暂时保存数据的元件,相当于电脑中的内存条。


Java中的内存分配:

区域名称 作用
寄存器

给CPU使用。

寄存器的功能是存储二进制代码,它是由具有存储功能的触发器组合起来构成的。一个触发器可以存储1位二进制代码,故存放n位二进制代码的寄存器,需用n个触发器来构成。

本地方法栈 JVM在使用操作系统功能的时候使用,和我们开发无关。

Java虚拟机栈用于管理Java方法的调用,而本地方法栈用于管理本地方法的调用。

方法区

存储可以运行的.class文件,是各个线程共享的内存区域;方法区有垃圾回收机制

它存储已被Java虚拟机加载的类信息、常量、静态变量、编译器编译后的代码等。

堆内存 存储对象或者数组,用new关键字来创建的对象/数组都存储在堆内存。("此处很好的解释了引用传递".)
方法栈 方法运行时使用的内存,比如main方法运行,进入方法栈中执行。因为我们写的程序都默认保存在硬盘中,所以当程序运行的时候要进入到内存中。
public class Demo{    public static void main(String []args){         int num = 5;     int arr [] = new int []{1,2,3};        }}


栈内存:一般存放基本数据类型的值;当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用。



堆内存:一般存放引用数据类型的值;在栈中存储的变量就变成了数组或者对象的引用变量,以后就可以在程序中使用栈内存中的引用变量来访问堆中的数组或者对象,引用变量相当于为数组或者对象起的一个别名,或者代号。

引用变量是普通变量,定义时在栈中分配内存,引用变量在程序运行到作用域外释放。

例如:

int arr [] = new int []{1,2,3};

int sum = arr[2]; sum就是一个变量。

而数组和对象本身仍存放在堆内存中,数组和对象在没有引用变量指向它的时候,才变成垃圾,不能再被使用,但是仍然占着内存,在一个不确定的时间被垃圾回收器释放掉才能彻底释放内存。

这个也是Java比较占内存的主要原因,实际上,栈中的变量指向堆内存中的变量,这就是 Java 中的指针!



Java开发——7.内存分配(堆、栈以及参数的值传递+引用传递)


值传递的本质:

在参数传递的过程中,原来参数值不会发生改变!

public static void main(String[] args) {//num 就是实参:实参是定义在方法之外类之中 int num = 15; System.out.println("值传递之前:"+num); zhiChuanDi(num); System.out.println("值传递之后:"+num);}
public static void zhiChuanDi(int number){//此处的number 就是形参:形参是定义在方法之中的 number = 150; }

图解分析:

主要原因:方法存储在栈内存中的,栈内存中的方法会随着方法的调用完成而消失;所以尽管num被当做参数传进了zhiChuanDi()的有参方法中,并且发生了重赋值的步骤,但是整个过程并不会影响原来的num实参。相当于复制了一份num,并把复制后的num当做形参传递给了zhiChuanDi()的有参方法,实际的num是不变的。


引用传递的本质:

在进行参数传递的过程中,发生了参数值的改变。

public static void main(String[] args) { int arr[] = new int[]{12,18,20}; System.out.println("引用传递之前:"+arr[0]);
yinYongChuanDi(arr); System.out.println("引用传递之后:"+arr[0]);
String str = "小红"; System.out.println("引用传递之前:"+str);
yinYongChuanDi2(str); System.out.println("引用传递之后:"+str);
    } public static void yinYongChuanDi(int []arr){ arr[0] = 66;    } public static void yinYongChuanDi2(String str){ str = "小明"; }

图解分析:

但是:我下面举了一个例子,是String类型的字符串,在进行参数的传递过程中,值并没有发生改变。虽然String类型也是引用数据类型,但是他发生参数传递的过程相当于基本数据类型,因为他并没有new一个对象!所以此处的String为特殊例子。