什么样的 Java 对象会被当垃圾回收?
Java 是一门不需要自己手动控制内存释放的语言。那在程序运行中,它是如何判断哪些内存可以回收呢?
从 JVM 的实现角度总体来看
- 主要考虑的是堆内存区域的 Java 对象的回收
- 一般使用可达性分析算法,通过 GC Roots 找到不可达对象进行回收
- 回收对象时需要考虑对象被引用的强度
- 回收对象前,会调用对象的 finalize() 方法,若在方法里把当前对象赋值给其他变量引用,会有且仅有一次避免被回收的机会
可回收内存区域
JVM 在运行的内存分配区域包括线程私有和线程共享两大类。
线程私有的内存区域:分为程序计数器、虚拟机栈、本地方法栈这三个区域,三者的生命周期与线程相同,即线程执行结束这三个区域的内存就可以被回收了。
线程共享的内存区域:包括堆和方法区,这两部分内存是在程序执行中动态分配与回收的,具有不确定性,所以垃圾收集器主要关注这两部分区域的内存回收。
绝大多数 Java 对象是在堆内存中分配的;方法区中存放了 Class 信息、常量、静态变量、即时编译器编译后的代码缓存等数据,排除大量动态类信息创建和卸载的情况,一般方法区内存回收的效率是很低的。所以垃圾收集器最主要关注的是堆内存的回收。
方法区的垃圾回收主要包含
1、废弃的常量,包括:字符串常量、接口 方法 和 字段的符号引用。
2、不再使用的类,满以下三个要求可以被回收但并非必然。
堆中不存在类及其子类的实例对象
该类的加载器已被回收
Class 对象未被引用,也无法反射访问
另外,直接内存不在 JVM 规范中定义的内存区域,使用 Unsafe 的 native 方法直接分配 (allocateMemory) 和释放 (freeMemory) 内存。nio 中通过在堆内存分配 DirectByteBuffer 对象,该对象可以操作直接内存,DirectByteBuffer 对象被回收后 gc 回收未释放的直接内存。
说到这,Java 对象的内存回收的问题,大致可以转变为「如何判断堆中 Java 对象占用的内存可以被回收?」,那得看下面具体的判断对象可回收的算法。
内存可回收的算法
常见的判断对象可以被回收的算法有两种:- 引用计数算法。理解起来很简单,对象里添加计数器,被引用计数加一,去除引用计数减一,计数为零的时刻就认为对象可以被回收。由于这种算法不好解决 Java 对象循环引用的问题,所以主流垃圾回收器不使用。
- 可达性分析算法。这个算法的核心思想就是先找到内存中的所有根对象,即 "GC Roots",然后根据引用关系搜索引用对象,未被查到的对象就是可以被回收的对象。
GC Roots,根对象包括:
虚拟机栈中引用的对象,如方法中的局部变量、参数等
类的静态变量引用的对象
常量引用的对象
native 方法引用的对象
JVM 内部引用,如基本类型的 Class 对象、异常对象、系统类加载器对象
synchronized 锁住的对象
JVM 内部的对象
细分引用类型
为了提供类似缓存的能力,内存足够时不着急回收对象、解决对象一直被错误地引用导致的内存溢出等问题,JDK 1.2 开始将引用分了为了四种类型。引用强度由强到弱,分别为
- 强引用(Strong Reference):String s = new String("ConstXiong"),代码中直接的赋值,引用关系在对象不会被回收
- 软引用(Soft Reference):SoftReference 实现,系统发生内存溢出前,回收软引用对象
- 弱引用(Weak Reference):WeakReference 实现,下一次垃圾收集会回收掉即会回收弱引用对象
- 虚引用(Phantom Reference):PhantomReference 实现,对垃圾收集器回收内存行为没有影响,仅为了虚引用对象被回收时能收到系统通知
例如:ThreadLocal 中的 ThreadLocalMap 中的 Entry 就继承了 WeakReference 类,用来解决线程本地变量内存泄漏的问题。
最后一次逃离的机会
对象被回收前会调用类的 finalize() 方法,在finalize() 方法中把当前对象赋值给其他变量引用,会有且仅有一次避免被回收的机会。来张脑图汇总下:
在线预览:
https://processon.com/view/link/5f91b04c07912906db39b89a
更多相关文章
- volatile如何避免指令重排序?原来使用了内存屏障
- Redis5.0中的内存淘汰策略详解(最新的版本)
- Spring Ioc 实例化 Bean 对象有几种方式?
- Javascript面向对象入门
- JVM系列(7)内存溢出问题(工作中常用、面试中常问的一个知识点)
- java关键字系列(2)static(内存角度分析,格式调整版)
- JS内存泄漏排查方法