为了让程序可以以最佳的状态运行,java把内存分为了堆和栈。每当我们声明新的对象和变量,调用新的方法,声明字符串或执行更多此类相似的操作,JVM都会从堆或栈中为这些操作分配空间。
本文将讨论这两个内存模型。我将会列举堆和栈的几个关键的不同,以及阐述它们是如何存储在物理内存中的,还有它们提供给我们的一些特性,何时去使用它们。
java中的栈被用于静态内存分配和执行线程。 它包含函数中的原始类型值,和函数中的引用,这些引用指向的对象存储在堆中。
访问栈中内存需要按照后进先出的顺序。每当调用一个新方法时,栈顶就会创建一个新的区块,这个区块包含了和这个函数有关的一些特定的值,例如原始类型变量和对象的引用。
该方法执行完毕后,将刷新相应的堆帧,数据回流到所调用方法中,该区块被清空,为下一个要执行的函数腾出空间。
除了上文讨论的内容,栈还有如下特性:
java中的堆空间被用于java对象和JRE类在运行时的动态内存分配。 新的对象总是在堆中创建,并且该对象的引用被存储在栈中。
这些对象具有全局访问属性,可以从程序的任何位置被访问。
在之前的JVM实现中堆中会被细分为世代以便于GC回收内存,但是新的ZGC已经不通过世代来回收内存,每次GC都会标记整个堆空间。故有关世代的内容本文不再阐述。
除了上文提到的内容,堆还有如下特性:
基于上文的知识点,来分析一段简单的kotlin代码吧!
class Person(pid: Int = 0,name: String? = null)
fun main() {
val id = 23
val pName = "Jon"
var p: Person? = null
p = Person(id, pName)
}
整个分配如下图所示:
参数 | 栈 | 堆 |
---|---|---|
应用场景 | 栈在局部使用,在线程执行期间一次只访问一个栈 | 整个程序在运行时都会使用堆空间 |
容量大小 | 根据os不同,栈有大小限制,通常比堆低 | 堆没有大小限制 |
存储 | 只存储原始类型变量和堆中对象的引用 | 存储所有新创建的对象 |
生命周期 | 栈只存在于当前方法运行的时候 | 只要程序在运行,堆空间就存在 |
分配效率 | 相较于堆分配较快 | 相较与栈分配较慢 |
分配/释放 | 当方法被调用时和返回时,内存自动分配和释放 | 当新对象被创建时,分配堆空间,当对象不再被引用时,GC释放空间 |