Java之堆内存(Heap)与栈内存(Stack)

Java中的内存类型

栈内存:在函数中定义的基本类型的变量、Java指令代码、对象的引用变量均在函数的栈内存中分配,当超过变量的作用域后,Java 会自动释放掉该变量分配的内存空间。
堆内存:用来存放 new 创建的对象和数组【实质上只保存对象实例的属性值、属性的类型和对象本身的类型标记等,并不保存对象方法(方法是指令,保存在Stack中)】,由 Java 虚拟机的自动垃圾回收器来进行内存管理。在堆中定义的变量,会在栈中定义引用变量【占用4字节的Heap内存地址】指向堆中的变量,以后可通过栈中的引用变量直接操作堆中的数组或者对象。堆中的变量占用的内存不会主动释放,即使没有引用变量指向他。
 

内存分配策略

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

静态存储分配

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

栈式存储分配

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

 堆式存储分配

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

堆和栈的比较

撇开静态存储分配,集中比较堆和栈的区别。从堆和栈的功能和作用来通俗来讲,堆主要用来存放对象,栈主要是用来执行程序。而这种不同又主要是由堆和栈的特点决定的:
  在编程中,例如C/C++中,所有的方法调用都是通过栈来进行的,所有的局部变量、形式参数都是从栈中分配内存空间的。需要注意的是,在分配的时候,比如为一个即将要调用的程序模块分配数据区时,应事先知道这个数据区的大小,也就说是虽然分配是在程序运行时进行的,但是分配的大小多少是确定的,而这个"大小多少"是在编译时确定的,不是在运行时 
  堆是应用程序在运行的时候请求操作系统分配内存,由于从操作系统管理的内存分配,所以在分配和销毁时都要占用时间,因此用堆的效率非常低,但是堆的优点在于编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间,因此用堆保存数据时会得到更大的灵活性。事实上,面向对象的多态性,堆内存分配是必不可少的,因为多态变量所需的存储空间只有在运行时创建了对象之后才能确定。
 

非静态方法和静态方法的区别

最大的区别在于,非静态方法有一个JVM分配的隐含传入参数,与怎么写代码无关,该参数即对象实例在Stack中的地址指针。因此非静态方法(在Stack中的指令代码)总可以找到自己的专用数据(在Heap 中的对象属性值)。因此非静态方法在调用前,必须先new一个对象实例,获得在Stack中的地址指针,否则JVM无法将隐含参数传给非静态方法。
  静态方法无此隐含参数,因此也不需要new对象,只要class文件被ClassLoader load进入JVM的Stack,该静态方法即可被调用。
 

小Tips

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的情况。
如果再令a=4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;
如果已经存在,则直接将a指向这个地址,因此a值的改变不会影响到b的值。
注意:这种数据的共享与两个对象的引用同时指向相同的对象的共享方式是不同的,因为这种情况下a的修改并不会影响到b, 它是由编译器完成的,有利于节省空间,而对象的引用变量修改对象的内部状态,会直接影响到另一个对象引用变量。
 
 
 
 
 
 
 
 
 
 
 
 
 
 





posted @ 2016-08-19 15:09  小轩948  阅读(315)  评论(0编辑  收藏  举报