2.6.1. Java内存模型
在Java中,内存被划分为以下几个区域:
- 堆(Heap):存储对象实例和数组,是垃圾回收的主要区域。
- 栈(Stack):存储局部变量和方法调用。每个线程有自己的栈。
- 方法区(Method Area):存储类信息,如类的结构、方法、字段等。
- 本地方法栈(Native Method Stack):存储本地方法(如JNI)的调用。
- 程序计数器(Program Counter Register):指示当前线程执行的字节码指令。
2.6.2. Java对象的生命周期
Java对象的生命周期分为以下几个阶段:
- 创建:使用
new
关键字创建对象实例。 - 使用:对象被程序引用和使用。
- 不可达:对象没有任何引用指向它,无法被程序访问。
- 垃圾回收:垃圾回收器回收不可达对象所占用的内存。
- 内存释放:内存被释放并归还给系统。
2.6.3. 垃圾回收
Java的垃圾回收器自动回收不再使用的对象。垃圾回收的目的是释放无用对象占用的内存,避免内存泄漏。垃圾回收过程主要发生在堆内存区域。
垃圾回收器的工作原理:
- 标记:垃圾回收器会找出所有不可达的对象,并将它们标记为垃圾。
- 清除:垃圾回收器会回收被标记的对象所占用的内存。
Java中常用的垃圾回收算法:
- 引用计数法:每个对象维护一个引用计数,当引用计数为0时,对象被视为垃圾。但这种方法无法解决循环引用问题。
- 标记-清除(Mark-Sweep):分为标记和清除两个阶段,标记阶段标记所有不可达对象,清除阶段回收它们的内存。但可能产生内存碎片。
- 标记-整理(Mark-Compact):在标记-清除基础上,将存活对象整理到内存的一端,回收边界以外的内存。解决了内存碎片问题。
- 分代收集(Generational Collection):将堆内存划分为新生代和老年代,针对不同代采用不同的垃圾回收策略。新生代使用复制算法(Copying),老年代使用标记-清除或标记-整理算法。
2.6.4. 内存泄漏与内存溢出
- 内存泄漏:程序中某个对象不再使用,但仍然被引用,导致无法被垃圾回收器回收。内存泄漏可能导致内存溢出。
- 内存溢出:程序请求的内存超过了系统可分配的最大内存,导致程序崩溃。
避免内存泄漏的方法:
- 及时释放不再使用的对象引用。
- 避免静态集合类引用长期占用内存的对象。
- 使用WeakReference、SoftReference等弱引用类型。
2.6.5. 示例
下面的示例展示了一个简单的类及其实例化过程,以及垃圾回收的触发。
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public static void main(String[] args) {
Student student1 = new Student("Alice", 20); // 创建一个Student实例
Student student2 = new Student("Bob", 22); // 创建另一个Student实例
student1 = student2; // student1引用指向student2,此时原先的"student1"实例无法访问,成为垃圾
System.gc(); // 建议JVM进行垃圾回收(注意:这并不保证立即进行垃圾回收)
}
}
在这个示例中,我们创建了两个Student
实例。当student1
引用指向student2
时,原来的student1
实例变得不可达,成为垃圾。我们使用System.gc()
建议JVM进行垃圾回收。需要注意的是,System.gc()
并不保证立即进行垃圾回收,具体的回收时机取决于JVM的实现。
这一节我们详细讲解了Java内存管理与垃圾回收的相关知识,包括内存模型、对象的生命周期、垃圾回收原理及算法、内存泄漏与内存溢出等内容。我们还通过一个简单的示例展示了垃圾回收的触发。希望这些内容对你有所帮助,如果你还有其他问题,请随时提问。
推荐阅读:
https://mp.weixin.qq.com/s/dV2JzXfgjDdCmWRmE0glDA
https://mp.weixin.qq.com/s/an83QZOWXHqll3SGPYTL5g