一、栈内存(Stack Memory):快速、私有的临时存储区
栈内存是一种遵循“后进先出(LIFO) ”原则的线性数据结构。在 JVM 中,它被称为虚拟机栈,主要用于存储方法执行时的临时数据。
-
存储内容:
-
栈帧:每个方法被调用时,JVM 都会为其创建一个栈帧。栈帧中包含了:
- 局部变量表:存储基本类型(如
int、boolean)和对象引用。 - 操作数栈:用于方法的运算。
- 方法返回地址:方法执行完毕后,返回到调用者的地方。
- 局部变量表:存储基本类型(如
-
-
特点:
- 线程私有:每个线程都有自己独立的栈,互不干扰,因此是线程安全的。
- 生命周期:栈帧的生命周期与方法的执行同步。方法开始时入栈,方法结束时出栈,数据自动销毁。
- 速度快:栈内存的分配和回收都是静态的、高效的,没有额外的开销。
二、堆内存(Heap Memory):动态、共享的对象仓库
堆内存是 JVM 中最大的一块内存区域,所有通过 new 关键字创建的对象和数组都存储在此。
-
存储内容:
- 对象实例:包括
String、Person、Integer等所有类实例。 - 数组:所有数组的实例。
- 对象实例:包括
-
特点:
- 线程共享:所有线程共享同一块堆内存,因此需要处理线程安全问题。
- 生命周期:堆中对象的生命周期由**垃圾回收器(GC)**管理。当一个对象不再被任何引用指向时,GC 会在合适的时候回收其内存。
- 速度慢:堆内存的分配和回收都是动态的,会涉及 GC 过程,因此速度慢于栈内存。
三、堆与栈的协作模式
堆和栈在 Java 程序中协同工作,共同管理数据的生命周期。
-
基本类型:
int i = 5。变量i及其值5都存储在栈中。 -
对象:
Cat cat = new Cat("咪咪")。new Cat("咪咪"):Cat对象实例存储在堆中。cat变量:cat变量存储了Cat对象在堆中的地址,这个变量本身存储在栈中。
四、内存溢出与String的特殊性
-
栈溢出:当方法调用深度超过栈的容量时,会抛出
StackOverflowError。这通常发生在无限递归或过深的方法调用中。 -
堆溢出:当堆中没有足够的内存来分配新对象时,会抛出
OutOfMemoryError。这通常发生在创建大量对象或内存泄漏时。 -
String的存储:String s = "abc":字符串常量"abc"存储在字符串常量池中。在 Java 7 之后,字符串常量池位于堆中。String s = new String("abc"):new关键字强制在堆中创建一个新的String对象。