图片


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


更多相关文章

  1. volatile如何避免指令重排序?原来使用了内存屏障
  2. Redis5.0中的内存淘汰策略详解(最新的版本)
  3. Spring Ioc 实例化 Bean 对象有几种方式?
  4. Javascript面向对象入门
  5. JVM系列(7)内存溢出问题(工作中常用、面试中常问的一个知识点)
  6. java关键字系列(2)static(内存角度分析,格式调整版)
  7. JS内存泄漏排查方法

随机推荐

  1. ANDROID 绝对布局 相对布局 Linear…
  2. Android(安卓)编译,打包、签程名详细教
  3. android 带文字的ImageButton
  4. 修改标题栏的高度
  5. Android中android:layout_width与 androi
  6. 关于android LinearLayout的比例布局
  7. Android 之 五大布局案例
  8. android xml布局文件属性说明
  9. Android Gesture 手势识别使用实例 - And
  10. Android日记抓取并保存