垃圾回收机制
文章目录
垃圾回收机制
讲一下堆常见的分配策略
-
对象优先在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里删除这个对象是不行的。
哪些东西会被当作垃圾回收?
不在引用链上的所有对象,以及在链上的软、弱、虚引用对象。
文章作者 LYR
上次更新 2021-08-17