在说堆和栈之前, 我们先说一下 JVM(虚拟机)内存的划分:
Java 程序在运行时都要开辟空间, 任何软件在运行时都要在内存中开辟空间, Java 虚拟机运行时也是要开辟空间的. JVM 运行时在内存中开辟一片内存区域, 启动时在自己的内存区域中进行更细致的划分, 因为虚拟机中每一片内存处理的方式都不同, 所以要单独进行管理.
JVM 内存的划分有五片:
寄存器;
本地方法区;
方法区;
栈内存;
堆内存.
我们重点来说一下堆和栈:
栈内存: 栈内存首先是一片内存区域, 存储的都是局部变量, 凡是定义在方法中的都是局部变量(方法外的是全局变量),for 循环内部定义的也是局部变量, 是先加载函数才能进行局部变量的定义, 所以方法先进栈, 然后再定义变量, 变量有自己的作用域, 一旦离开作用域, 变量就会被释放. 栈内存的更新速度很快, 因为局部变量的生命周期都很短.
堆内存: 存储的是数组和对象(其实数组就是对象), 凡是 new 建立的都是在堆中, 堆中存放的都是实体(对象), 实体用于封装数据, 而且是封装多个(实体的多个属性), 如果一个数据消失, 这个实体也没有消失, 还可以用, 所以堆是不会随时释放的, 但是栈不一样, 栈里存放的都是单个变量, 变量被释放了, 那就没有了. 堆里的实体虽然不会被释放, 但是会被当成垃圾, Java 有垃圾回收机制不定时的收取.
下面我们通过一个图例详细讲一下堆和栈:
比如主函数里的语句 int [] arr=new int [3]; 在内存中是怎么被定义的:
主函数先进栈, 在栈中定义一个变量 arr, 接下来为 arr 赋值, 但是右边不是一个具体值, 是一个实体. 实体创建在堆里, 在堆里首先通过 new 关键字开辟一个空间, 内存在存储数据的时候都是通过地址来体现的, 地址是一块连续的二进制, 然后给这个实体分配一个内存地址. 数组都是有一个索引, 数组这个实体在堆内存中产生之后每一个空间都会进行默认的初始化(这是堆内存的特点, 未初始化的数据是不能用的, 但在堆里是可以用的, 因为初始化过了, 但是在栈里没有), 不同的类型初始化的值不一样. 所以堆和栈里就创建了变量和实体:
在这里插入图片描述
那么堆和栈是怎么联系起来的呢?
我们刚刚说过给堆分配了一个地址, 把堆的地址赋给 arr,arr 就通过地址指向了数组. 所以 arr 想操纵数组时, 就通过地址, 而不是直接把实体都赋给它. 这种我们不再叫他基本数据类型, 而叫引用数据类型. 称为 arr 引用了堆内存当中的实体.(可以理解为 c 或 c++ 的指针, Java 成长自 c++ 和 c++ 很像, 优化了 c++)
在这里插入图片描述
如果当 int [] arr=null;
arr 不做任何指向, null 的作用就是取消引用数据类型的指向.
当一个实体, 没有引用数据类型指向的时候, 它在堆内存中不会被释放, 而被当做一个垃圾, 在不定时的时间内自动回收, 因为 Java 有一个自动回收机制,(而 c++ 没有, 需要程序员手动回收, 如果不回收就越堆越多, 直到撑满内存溢出, 所以 Java 在内存管理上优于 c++). 自动回收机制 (程序) 自动监测堆里是否有垃圾, 如果有, 就会自动的做垃圾回收的动作, 但是什么时候收不一定.
所以堆与栈的区别很明显:
栈内存存储的是局部变量而堆内存存储的是实体;
栈内存的更新速度要快于堆内存, 因为局部变量的生命周期很短;
栈内存存放的变量生命周期一旦结束就会被释放, 而堆内存存放的实体会被垃圾回收机制不定时的回收.
java 中 static 变量和方法到底是存在内存什么区域呢?
JVM 内存总体一共分为了
4 个部分(stack segment,heap segment,code segment,data segment)
当我们在程序中, 申明一个局部变量的时候, 此变量就存放在了 stack segment(栈)当中;
当 new 一个对象的时候, 此对象放在了 heap segment(堆)当中;
而 static 的变量或者字符串常量 则存在在 data segment(数据区)中;
那么类中方法的话, 是存在在 code segment(代码区)中了.
来源: http://www.jianshu.com/p/c0baf6fc91ee