java中内存分配策略

按照编译原理的观点,程序运行时的内存分配有三种策略:静态的,栈式的,堆式的

静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配特定的存储空间。静态存储分配要求程序中不允许有可变数据结构的存在(比如数组,链表),也不允许有嵌套或者递归的结构出现,因为它们会导致编译程序无法计算准确的存储空间需求。

栈式分配(动态存储分配)是由一个类似于堆栈的运行栈来实现的。在栈式存储方案中,程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道,但是规定在运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存.和我们在数据结构所熟知的栈一样,栈式存储分配按照先进后出的原则进行分配。

堆式存储分配则专门负责在编译时或运行时模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例.堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释放.

栈与堆

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

:堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。

数据类型与内存的关系

java把内存分为两种,一种是堆内存,一种是栈内存
基本数据类型以及对象的引用变量是存储在栈内存中,当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用。

而对象本身的值或者说new创建的对象和数组是存储在堆内存中。在堆中分配的内存,由java虚拟机自动垃圾回收器来管理。在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量(对象的引用变量),这个变量的取值等于数组或者对象在堆内存中的首地址,在栈中的这个特殊的变量就变成了数组或者对象的引用变量,以后就可以在程序中使用栈内存中的引用变量来访问堆中的数组或者对象,引用变量相当于为数组或者对象起的一个别名,或者代号。

数组和对象在没有引用变量指向它的时候,才变成垃圾,不能再被使用,但是仍然占着内存,在随后的一个不确定的时间被垃圾回收器释放掉。这个也是java比较占内存的主要原因,实际上,栈中的变量指向堆内存中的变量,这就是 Java 中的指针。

基本数据类型和引用数据类型作为参数的区别

基本数据类型的变量中直接存放数据值本身,所以改的时候改的是数据值本身。(基本数据类型就好比复印了一份,其中一人将自己的资料撕了,并不影响别人)

但是引用类型不同的地方在于真正的数据并没有在栈区的变量中保存,而是在堆区里面保存着,所以虽然也拷贝了一份,也是副本,但是二者指向的是同一块堆区,其中一个改变了,另一个也会跟着改变。(引用数据类型就好比如说,两位同事使用的是同一份复习资料,其中一人把资料弄丢了,另一人也会受到影响。)

  • 当使用基本数据类型作为方法的形参时,在方法体中对形参的修改不会影响到实参的数值
  • 当使用引用数据类型作为方法的形参时,若在方法体中修改形参指向的数据内容,则对实参变量的数值产生影响,因为形参变量和实参变量共享同一块堆区
  • 当使用引用数据类型作为方法的形参时,若在方法体中修改形参变量的指向,此时不会对实参变量的数值产生影响,因此形参变量和实参变量分别指向不同的堆区