垃圾回收机制

讲一下堆常见的分配策略

  • 对象优先在Eden区分配

    • 第一次垃圾回收后,如果s区放不下,那么只能通过==分配担保机制==把新生代的对象提前转移到老年代
  • 大对象直接进入老年代

    • 新生代如果放不下,直接放到老年代
    • ==如果在分配对象到Eden区的时候发现放不下,触发了GC,还是放不下,那么就会老年代尝试存放,如果老年代也放不下会触发FGC,如若还是放不下直接OOM。==
  • 长期存活的对象将进入老年代

    • 大于默认年龄的会进入老年代,一般的垃圾回收器是15,而CMS是6
    • 动态年龄,按照年龄从小到大排序,对占用的大小进行累计,如果累计的某个年龄大小超过了s区的一半,那么取这个年龄和默认年龄的最小值,作为晋升年龄
      1
      2
      3
      4
      
      年龄1的对象占用了33%
      年龄2的对象占用33%
      年龄3的对象占用34%。
      年龄2和3都会晋升到老年代
      

JVM中有几种GC讲一下

分为两大类:

  • Partial GC
    • Young GC:只收集年轻代的GC
    • Old GC: 只收集老年代的GC
    • Mixed GC: 收集整个年轻代和部分老年代
  • Full GC
    • 收集整个堆的,包括年轻代,老年代和永久代

说一下各GC的触发条件

  • young GC:eden区分配满了的时候。==注意s区满了是不会触发gc的==
  • full GC:当要触发young GC时,根据以前的统计数据,young GC平均晋升的大小比目前老年代剩余空间大,那么就触发full GC,不会再发生young GC了
  • 如果有永久代的话,永久代剩余空间不足时,会触发full GC
  • 老年代空间不足也会触发full GC

如何判断一个对象已经无效

  • 引用计数法

    • 每被引用一次+1,引用失效-1,引用次数为0的就无效了。
    • 但是存在无法解决相互引用的问题。比如A引用B,B引用A。而这俩没有其他的引用,应该被回收掉,但是此时无法回收。
  • 可达性分析算法

    • 从GC Root的对象作为起点,开始搜索引用链,如果一个对象没有到GC Root的引用链,那就是不可用的。
    • 可以作用GC Roots的对象包括
      • 虚拟机栈中引用的对象
      • 本地方法栈中引用的对象
      • 方法区中类静态属性引用的对象
      • 方法区中常量引用的属性
      • 所有被同步锁持有的对象

说一下几种引用的情况

  • 强引用
    • 大部分引用都是强引用
    • 无论如何垃圾回收器都不会回收
  • 软引用
    • 如果内存空间足够,就不会回收,如果不足,就会回收这些引用
  • 弱引用
    • 一旦被发现是弱引用,不管内存是否足够,马上将它回收
  • 虚引用
    • 就跟没有引用一样
    • 虚引用主要用来跟踪对象被垃圾回收器回收的活动。

不可达的对象一定会被回收吗

不一定,在真正被回收之前需要标记两次。

  • 第一次是检查这些被标记的是否要进行finalize方法,如果对象没有覆盖finalize方法或者已经执行过了,那么就会被回收
  • 第二次:如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会放置在一个叫做F-Queue的队列之中,触发finalize方法,它们有可能重新被引用而逃离死亡。

但是虚拟机并不承诺会等待finalize的结果。如果一个对象在finalize()方法中执行缓慢,或者发生了死循环。

如何判断常量是废弃常量,谁负责回收它。

运行时常量池负责回收废弃常量。

如果一个常量没有对象引用它了,就会被回收。

如何判断一个类是无用类,谁负责回收它

方法区主要负责回收无用的类。 三个条件

  • 该类的所有实例都已经被回收
  • 加载该类的classLoader已经被回收
  • 该类对应的Class对象没有在任何地方被引用

讲一下有哪几种垃圾回收算法,讲一下缺点

  • 标记-清除算法:先把不需要回收的对象标记出来,统一回收没有标记的对象
    • 效率问题
    • 空间问题,会产生碎片
  • 标记-复制算法:把内存分为两个大小一样的块,标记不需要回收的,复制到另一边,然后清空这一边
    • 浪费空间
  • 标记-整理算法:让所有存活的对象向一段移动,然后清理掉端边界以外的内存
    • 算法复杂度大,步骤复杂
  • 分代收集算法
    • 根据对象存活周期的不同,将内存分为年轻代,老年代和永久代
    • 年轻代有大量的对象死去,就用标记-复制算法
    • 老年代存活率高用标记-清除或标记-整理

为什么HotSpot要分为年轻代和老年代

因为使用了分代收集算法,可以根据对象存活的时间,选择不同的算法

讲一下有哪些垃圾收集器

  • Serial收集器
    • 单线程,必须停止所有工作线程stop the world
    • 年轻代使用标记-复制算法,老年代使用标记-整理算法
  • ParNew收集器
    • 就是并行的Serial,多线程版本。
    • 年轻代标记-复制,老年代标记-整理
  • Parallel Scavenge收集器
    • 关注吞吐量的多线程收集器
    • 年轻代标记-复制,老年代标记-整理
    • JDK8默认收集器
  • Serial Old
    • Serial的老年代版本
  • Parallel Old
    • Parallel Scavenge的老年代版本
  • CMS
    • 是并发收集器,实现最短停顿时间
    • 是一种标记-清除算法,会有空间碎片
    • 四个步骤
      • 初始标记:stw,暂停所有的其他线程,记录下与root相连的对象,速度很快
      • 并发标记:同时启动GC和用户线程,记录Roots的可达对象
      • 重新标记:stw,修正并发阶段因为用户行为导致的变动。
      • 并发清除:开启用户线程,并发清除垃圾

如果频繁发生full gc是什么原因

可能有内存泄漏了

举几个会发生内存泄漏的例子

被静态集合引用的对象。static List<> ls; ls.add(a)。因为静态对象的生命周期跟JVM一样,JVM不结束静态集合就不会销毁。

hash会改变的对象。比如set.add(这个对象),然后对象的hash变了,这时候想去set里删除这个对象是不行的。

哪些东西会被当作垃圾回收?

不在引用链上的所有对象,以及在链上的软、弱、虚引用对象。