Java底层知识:GC相关

目录

7-1 垃圾回收之判定对象是否为垃圾的方法

7-2 垃圾回收算法

7-3 新生代垃圾收集器

7-4 老年代垃圾收集器

7-5 常见面试题


7-1 垃圾回收之判定对象是否为垃圾的方法

对象被判定为垃圾的标准

  • 没有被任何对象引用的情况下

判定对象是否为垃圾

  • 引用计数算法
    • 通过判断对象的引用数量来决定对象是否被回收
    • 每个对象实例都有一个引用计数器,被引用则+1,完成引用(完成了一个线程后,栈内的变量消失)则+1
    • 任何引用计算为0的对象实例可以被当作为垃圾收集
    • 优点:执行效率高,程序执行受影响较小
    • 缺点:无法检测出循环引用的情况,导致内存泄露
  • 可达性分析算法(JVM用的时这种算法)
    • 通过判断对象的引用链是否可达来决定对象是否可以被回收
    • 是从离散数学中的图论来引用的
    • GC root对象的时候作为起始节点,从这些节点往下搜索,搜索所走过的路径,就被称为引用链,当一个对象从GCroot起始节点开始,没有任何引用链相连,从GC roots到这个对象是不可达的,证明这个对象是不可用的
  • 可以作为GC root的对象
    • 虚拟机栈中引用的对象(栈帧中的本地变量表)
    • 方法区中的常量引用的对象
    • 方法区中的类静态属性引用的对象
    • 本地方法栈中JNI(Native方法)的引用对象
    • 活跃线程中的引用对象

7-2 垃圾回收算法

标记-清除算法(Mark and Sweep)

  • 标记:从根集合进行扫描,对存活的对象进行标记(用可达性分析算法进行标记)
  • 清除:对堆内存从头到尾进行线性遍历,如果发现没有对象被标记为可达对象,那么就将此对象占用的内存给回收了,并且将原来的标记为可达对象的标识给清除掉,以便进行下一次垃圾回收
  • 缺点
    • 碎片化:标记清除不需要进行对象的移动,并且仅对不存活的对象进行处理,因此标记清除后会产生大量不连续的内存碎片,空间碎片太多,以后分配较大的对象的时候,无法找到足够的连续内存,不得不提前进行下一次的垃圾收集动作

复制算法:适用于对象存活率低的场景,例如年轻代

  • 分为对象面和空闲面
  • 对象在对象面上创建
  • 存活的对象被从对象面复制到空闲面
  • 将对象面所有对象内存清除
  • 优点
    • 解决碎片化问题
    • 顺序分配内存,简单高效
    • 适用于对象存活率低的场景,用于回收年轻代(每次回收,只有10%的对象存活)

在应对存活率较高的对象,复制算法就有点力不从心,必须要进行较多的复制操作,效率将会变低,更关键的是,如果不想浪费50%的空间,就需要有额外的空间担保,以应对所有对象都百分之百存活的极端情况,在老年代一般不能直接选用这种算法

标记-整理算法(Compacting)

  • 标记:从根集合开始扫描,对存活的对象进行标记
  • 清除: 移动所有存货的对象,且按照内存地址次序依次排序,然后将末端地址以后的内存全部回收。
  • 解决了内存碎片的问题 
  • 优点
    • 避免内存的不连续性
    • 不用设置两块内存互换
    • 适用于存活率极高的场景

分代收集算法(Generational Collector )

  • 垃圾回收算法的组合拳
  • 按照生命周期的不同划分区域以采用不同的垃圾回收算法
  • 目的:提高JVM的回收效率
  • GC分类
    • Minor GC --- 年轻代----复制算法
      • 年轻代:尽可能快速的收集掉那些生命周期短的对象
        •  Eden区  8:1:1
        • 两个Survivor区:from 和 to 区 
    • Magor GC
      • 老年代---存放生命周期较长对象--标记清理算法或者标记整理算法
    • Full FC:触发FullGC的条件 
      • 老年代空间不足
      • 永久代空间不足(JDK7)
      • CMS GC出现promation filed 和 concurrent mode failure
      • Minor GC晋升到老年代的平均大小大于老年代的剩余空间。   
      • 调用System.gc()
      • 使用RMI(远程方式)进行RPC,每小时执行一次Full GC

对象如何晋升老年代

  • 经历一定Minor次数仍然存活的对象,15岁
  • Survivor区中存放不下的对象,由分配担保进入老年代中
  • 新生成的大对象

常用的调优参数

  • -XX:SurvivorRatio:Eden和Survivor的比值,默认8:1
  • -XX:NewRatio:老年代和年轻代内存大小的比例
  • -XX:--XX:MaxTenuringThreshold 设置新生代晋升老年代经过GC次数的最大阈值

7-3 新生代垃圾收集器

Stop-the-World

  • JVM由于要执行GC而停止了应用程序的执行
  • 任何一种GC算法中都会发生
  • 多数GC优化通过减少Stop -the-World发生的时间来提高程序性能,从而使系统具有高吞吐低停顿的特点

Safepoint

  • 分析过程中对象引用关系不会发生变化的点
  • 产生安全点二点地方:方法调用;循环跳转;异常跳转
  • 安全点的数量得适中,太少会增加GC等待的时间,太多会增加程序运行的负荷

常见的垃圾收集器

JVM运行模式

  • Server:启动较慢,进入稳定期后,Server程序的运行速度要比Client要快,重量级的虚拟机
  • Client:启动速度快,轻量级的虚拟机

垃圾收集器之间的联系

常见年轻代收集器

  • serial收集器:单线程收集,垃圾收集时必须暂停其他线程,简单高效,client下默认的年轻代收集器(复制算法,-XX:+UseSerialGC)
  • ParNew收集器:多线程,其余特点和serial一样,server模式下jvm首选年轻代收集器,多核有优势(复制算法,暂停其他线程,-XX:+UseParNewGC),能够与CMS收集器相配合
  • ParallelScavenge收集器:比起用户停顿时间,更关注系统吞吐量;多核有优势,server模式下默认年轻代收集器(-XX:+UseParallelGC,复制算法,暂停其他线程)(-XX:+UseAdaptiveSizePolicy ,自适应调节策略)
  • 吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),用户停顿时提高用户体验两,吞吐量一般用在后端提高程序运行效率,快速完成工作。

7-4 老年代垃圾收集器

  • SerialOld收集器:单线程收集,暂停其他线程,简单高效,Client下默认老年代收集器(-XX:UseSerialOldGC,标记-整理算法
  • ParallelOld收集器:多线程,吞吐量优先(-XX:UseParallelOldGC,标记-整理算法
  • CMS收集器:尽可能的减少了停顿时间,并发几乎能与用户线程同时工作,内存cpu硬性条件高有优势,下图加粗1,4步骤需要短暂stop-the-world。注意算法会生产碎片(-XXUseConcMarkSweepGC,标记-清除算法

CMS收集器步骤(最适合老年代的收集器):并发收集器

  1. 初始标记:stop-the-word (从垃圾对象的根对象开始,进扫描和根对象相连接的对象)
  2. 并发标记:并发追溯标记,程序不会停顿。应用程序的线程和并发标记的线程比并发执行,所以用户不会感到停顿
  3. 并发预处理:查找执行并发比标记阶段从年轻代晋升到老年代的对象,通过重新扫描,减少下一阶段重新标记的工作
  4. 重新标记:暂停标记,扫描CMS堆中剩余对象
  5. 并发清理:清理垃圾对象,收集线程和应用线程并发执行的
  6. 并发清理:清理垃圾对象,程序不会停顿。
  7. 并发重置:重置CMS收集器的数据结构。

G1收集器(-XX:+UseG1GC,复制+标记-整理算法

  • GarbageFirst收集器特点:并行并发;分代收集;空间整合;可预测的停顿。
  • 与其他区别:将整个java堆内存分成多个大小相等的Region;
  • 年轻代老年代不再物理隔离:在分配内存的时候,不需要一个连续的内存空间,即不需要在JVM启动时哪些属于老年代,哪些属于年轻代。年轻代regin被回收后,变成可用状态,也可能变成老年代。
  • 触发:Eden区满了分配失败,触发年轻代回收,所有Eden存活对象复制到survivor区

7-5 常见面试题

Object 的finalize()方法的作用是否与C++的析构函数的作用相同

  • 与C++的析构函数不同,析构函数调用确定,而它是不确定的
  • 将未被引用的对昂放置在F-Queue队列
  • 方法执行随时可能会被终止
  • 给予对象的最后一次重生的机会

当垃圾回收器宣布一个对象死亡,至少需要经历两个阶段

  1. 当对象进行可达性分析的时候发现没有和GC root相连接就会被第一次标记;
  2. 判断对象是否覆盖finalize(),如果覆盖,并且未被引用过这个方法的对象就会被放在F-Queue中,最后由JVM执行该方法

Java中的强引用,软引用,弱引用,虚引用有什么用

强引用

  • 最普遍的引用 Object obj = new Object()
  • 抛出OutOfMemoryError终止程序也不会回收具有强引用的对象
  • 通过对象设置为null来弱化引用,使其被回收

软引用(Soft Reference)

  • 对象处在有用但是非必须的状态
  • 只有当内存空间不足的时候,GC会回收该对象的内存
  • 可以用来实现高速缓存:避免OutOfMemoryError

String str = new String("abc");//强引用

SoftReference<String> softR = new SoftReference<String>(str) //弱引用

  • 还可以用引用队列来实现

弱引用(Weak Reference)

  • 非必须的对象,比软引用更弱一些
  • GC时会被收回
  • 被回收的概率也不大,因为GC线程优先级比较低
  • 适用于引用偶尔被使用且不影响垃圾收集的对象

虚引用(PhantomReference)

  • 不会决定对象的生命值周期
  • 任何时候都可能被垃圾回收器回收
  • 跟踪对象被垃圾回收器回收的活动,起哨兵作用
  • 必须和引用队列ReferenceQueue联合使用
引用类型被垃圾回收时间用途生存时间
强引用

从来不会

对象的一般状态JVM停止运行时终止
软引用在内存不足时对象缓存内存不足时终止
弱引用在垃圾回收时对象缓存GC运行后终止
虚引用Unknown标记、哨兵Unknown

引用队列(ReferenceQueue)

  • 无实际存储结构,存储逻辑依赖于内部节点之间的关系来表达
  • 存储关联的且被GC的软引用,弱引用以及虚引用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值