Java 详解垃圾回收与对象生命周期
这里有新鲜出炉的 Java 设计模式, 程序狗速度看过来!
Java 程序设计语言
java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言, 是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 程序设计语言和 Java 平台 (即 JavaEE(j2ee), JavaME(j2me), JavaSE(j2se)) 的总称
这篇文章主要介绍了 Java 详解垃圾回收与对象生命周期的相关资料, 这里对堆内存与栈内存进行详解及 JVM 的生命周期介绍, 需要的朋友可以参考下
Java 垃圾回收与对象生命周期详解
Java 中的垃圾回收与对象生命周期
1. 垃圾回收
垃圾回收是 Java 程序设计中内存管理的核心概念, JVM 的内存管理机制被称为垃圾回收机制
一个对象创建后被放置在 JVM 的堆内存中, 当永远不再引用这个对象时, 它将被 JVM 在堆内存中回收被创建的对象不能再生, 同时也没有办法通过程序语句释放它们即当对象在 JVM 运行空间中无法通过根集合到达 (找到) 时, 这个对象被称为垃圾对象根集合是由类中的静态引用域与本地引用域组成的 JVM 通过根集合索引对象
在做 Java 应用开发时经常会用到由 JVM 管理的两种类型的内存: 堆内存和栈内存简单来讲, 堆内存主要用来存储程序在运行时创建或实例化的对象与变量例如通过 new 关键字创建的对象而栈内存则是用来存储程序代码中声明为静态或非静态的方法
(1) 堆内存
堆内存在 JVM 启动的时候被创建, 堆内存中所存储的对象可以被 JVM 自动回收, 不能通过其他外部手段回收, 也就是说开发人员无法通过添加相关代码的手段来回收堆内存中的对象堆内存通常情况下被分为两个区域: 新对象区域与老对象区域
新对象区域: 又可细分为三个小区域: 伊甸园区域 From 区域与 To 区域伊甸园区域用来保存新创建的对象, 它就像一个堆栈, 新的对象被创建, 就像指向该栈的指针在增长一样, 当伊甸园区域中的对象满了之后, JVM 系统将要做到可达性测试, 主要任务是检测有哪些对象由根集合出发是不可达的, 这些对象就可以被 JVM 回收, 并且将所有的活动对象从伊甸园区域拷贝到 To 区域, 此时一些对象将发生状态交换, 有的对象就从 To 区域被转移到 From 区域, 此时 From 区域就有了对象上面对象迁移的整个过程, 都是由 JVM 控制完成的
老对象区域: 在老对象区域中的对象仍然会有一个较长的生命周期, 大多数的 JVM 系统垃圾对象, 都是源于 "短命" 对象, 经过一段时间后, 被转入老对象区域的对象, 就变成了垃圾对象此时, 它们都被打上相应的标记, JVM 系统将会自动回收这些垃圾对象, 建议不要频繁地强制系统作垃圾回收, 这是因为 JVM 会利用有限的系统资源, 优先完成垃圾回收工作, 导致应用无法快速地响应来自用户端的请求, 这样会影响系统的整体性能
(2) 栈内存
堆内存主要用来存储程序在运行时创建或实例化的对象与变量例如通过 new 关键字创建的对象而栈内存则是用来存储程序代码中声明为静态或非静态的方法
2. JVM 中对象的生命周期
在 JVM 运行空间中, 对象的整个生命周期大致可以分为 7 个阶段:
创建阶段;
应用阶段;
不可视阶段;
不可到达阶段;
可收集阶段;
终结阶段;
释放阶段
上面这 7 个阶段, 构成了 JVM 中对象的完整的生命周期
(1) 创建阶段
在对象的创建阶段, 系统主要通过下面的步骤, 完成对象的创建过程:
<1> 为对象分配存储空间;
<2> 开始构造对象;
<3> 从超类到子类对 static 成员进行初始化;
<4> 超类成员变量按顺序初始化, 递归调用超类的构造方法;
<5> 子类成员变量按顺序初始化, 子类构造方法调用
在创建对象时应注意几个关键应用规则:
<1> 避免在循环体中创建对象, 即使该对象占用内存空间不大
<2> 尽量及时使对象符合垃圾回收标准比如 myObject = null
<3> 不要采用过深的继承层次
<4> 访问本地变量优于访问类中的变量
(2) 应用阶段
在对象的引用阶段, 对象具备如下特征:
<1> 系统至少维护着对象的一个强引用(Strong Reference);
<2> 所有对该对象的引用全部是强引用 (除非我们显示地适用了: 软引用(Soft Reference) 弱引用 (Weak Reference) 或虚引用(Phantom Reference)).
强引用(Strong Reference): 是指 JVM 内存管理器从根引用集合出发遍历堆中所有到达对象的路径当到达某对象的任意路径都不含有引用对象时, 这个对象的引用就被称为强引用
软引用(Soft Reference): 软引用的主要特点是有较强的引用功能只有当内存不够的时候, 才回收这类内存, 因此内存足够时它们通常不被回收另外这些引用对象还能保证在 Java 抛出 OutOfMemory 异常之前, 被设置为 null 它可以用于实现一些常用资源的缓存, 实现 Cache 功能, 保证最大限度地使用内存你而不引起 OutOfMemory
下面是软引用的实现代码:
- import java.lang.ref.SoftReference;
- ...
- A a = new A();
- ...
- // 使用 a
- ...
- // 使用完了 a, 将它设置为 soft 引用类型, 并且释放强引用
- SoftReference sr = new SoftReference(a);
- a = null;
- ...
- // 下次使用时
- if (sr != null) {
- a = sr.get();
- } else {
- // GC 由于低内存, 已释放 a, 因此需要重新装载
- a = new A();
- sr = new SoftReference(a);
- }
软引用技术的引进使 Java 应用可以更好地管理内存, 稳定系统, 防止系统内存溢出, 避免系统崩溃因此在处理一些占用内存较大且生命周期较长, 但使用并不繁地对象时应尽量应用该技术提高系统稳定性
弱引用(Weak Reference): 弱应用对象与软引用对象的最大不同就在于: GC 在进行垃圾回收时, 需要通过算法检查是否回收 Soft 应用对象, 而对于 Weak 引用, GC 总是进行回收 Weak 引用对象更容易更快地被 GC 回收 Weak 引用对象常常用于 Map 结构中
- import java.lang.ref.WeakReference;
- ...
- A a = new A();
- ...
- // 使用 a
- ...
- // 使用完了 a, 将它设置为 Weak 引用类型, 并且释放强引用
- WeakReference wr = new WeakReference(a);
- a = null;
- ...
- // 下次使用时
- if (wr != null) {
- a = wr.get();
- } else {
- a = new A();
- wr = new WeakReference(a);
- }
虚引用(Phantom Reference): 虚引用的用途较少, 主要用于辅助 finalize 函数的使用
虚引用 (Phantom Reference) 对象指一些执行完了 finalize 函数, 并为不可达对象, 但是还没有被 GC 回收的对象这种对象可以辅助 finalize 进行一些后期的回收工作, 我们通过覆盖了 Refernce 的 clear()方法, 增强资源回收机制的灵活性
在实际程序设计中一般很少使用弱引用和虚引用, 是用软引用的情况较多, 因为软引用可以加速 JVM 对垃圾内存的回收速度, 可以维护系统的运行安全, 防止内存溢出 (OutOfMemory) 等问题的产生
(3) 不可视阶段
当一个对象处于不可视阶段, 说明我们在其他区域的代码中已经不可以在引用它, 其强引用已经消失, 例如, 本地变量超出了其可视
的范围
- try {
- Object localObj = new Object();
- localObj.doSomething();
- } catch(Exception e) {
- e.printStackTrace();
- }
- if (true) {
- // 此区域中 localObj 对象已经不可视了, 编译器会报错
- localObj.doSomething();
- }
(4) 不可到达阶段
处于不可达阶段的对象在虚拟机的对象引用根集合中再也找不到直接或间接地强引用, 这些对象一般是所有线程栈中的临时变量所有已经装载的静态变量或者是对本地代码接口的引用
(5) 可收集阶段终结阶段与释放阶段
当一个对象处于可收集阶段终结阶段与释放阶段时, 该对象有如下三种情况:
<1> 回收器发现该对象已经不可达
<2> finalize 方法已经被执行
<3> 对象空间已被重用
来源: http://www.phperz.com/article/18/0209/358832.html