前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >简述JVM基础(三):垃圾收集器与内存分配策略

简述JVM基础(三):垃圾收集器与内存分配策略

作者头像
open
发布2020-03-19 17:07:59
4220
发布2020-03-19 17:07:59
举报

一、前言

了解了JVM中的内存分配以及数据存储的情况,那么怎么分配的方式是最高效的,对于已经过期无用的内存占用我们怎么回收起来,这个活必须得有人来干。好在JVM自带有垃圾回收机制,我们不需要关心这些,但是当我们需要排除各种内存溢出,内存泄漏的问题时。了解其中的一些知识还是很有必要的。

说明: 本系列多处摘抄《深入理解Java虚拟机》中内容,主要精简了本书的要点,并叙述自己对本书的理解。本人才疏学浅,文章中有不对的地方,还望批评指教。

二、对象

1、引用计数算法

  • 定义:给对象添加一个引用计数器,当增加一个引用时,加1,当一个引用时,减1;
  • 缺陷:当对象之间互相循环引用时,就会变的像“不死对象”;

2、可达性分析算法

  • 定义:从“GC Roots”为起点,向下搜索引用链,当一个对象的引用没有和引用链有任何的连接的时候,可以判定为可以回收的对象;
  • 分类:
    • 虚拟机栈中(栈中本地变量表)引用的对象;
    • 本地方法栈中JNI引用的对象;
    • 方法区中静态属性的对象;
    • 方法区中常量引用的对象;

3、引用

  • 强引用:“=”实现,引用只要还在,就不会被GC回收;
  • 软引用:SoftReference实现,在内存不足发生OOM之期,就回收掉该引用;
  • 弱引用:WeakReference实现,在下一次GC前,就回收掉该引用;
  • 虚引用:PhantomReference实现,在任何时候可能被回收,不过回收后可以收到系统的通知;

4、finalize方法自救

  • 如果GC Roots 不可达,进行第一次标记;
  • 再看finalize()方法有没有被复写,有则先执行finalize()方法;
    • 逃命方法:将自己关联到类变量之类或者某对象的成员变量的;
    • 终身只有一次调用机会;
  • 接着GC进行第二次标记,如果没有逃脱,则回收;

5、无用的类

  • 所有该类的实例已被回收
  • 该类的ClassLoad已被回收
  • 该类对应的java.lang.Class没有在任何地方被引用,不存在该类的反射

三、垃圾回收算法

1、标记——清除法

  • 方案:先标记,然后直接回收
  • 缺陷:产生大量碎片,不好分配大内存

2、复制算法

  • 方案:将内存分为AB两块,回收时,将A块的对象全部复制到B,然后把未回收的上移,然后把已使用过的A全部清除;
  • 优点:实现简单,运行高效;
  • 缺陷:有一半处于无用状态,浪费;
  • HotSpot:通过 Eden:Surivor:Surivor=8:1:1的比例分配,这样只有10%的空间浪费;不足时,需要依赖其他内存分配担保;

3、标记—整理算法

  • 方案:标记—清理之后,再整体上移;
  • 适应:适合老年代内存区使用

4、分代收集算法

  • 新生代:使用复制算法
  • 老年代:使用“标记-清理”或者“标记—整理”算法

四、垃圾收集器

1、 Serial收集器

  • 这个收集器是一个单线程收集器,
  • 只会使用一个CPU或者一条收集线程进行垃圾收集工作
  • 其余的工作现场必须暂停,直到收集结束

2、ParNew收集器

  • Serial收集器的多线程版本
  • 垃圾收集器线程和工作线程同时工作

3、Paraller Scavenge收集器

  • 目标:达到一个可控的吞吐量
    • 吞吐量: 用户运行时间/(用户运行时间+垃圾回收时间)
  • GC自适应调节:调整参数提供最合适的停顿时间或者最大的吞吐量

4、CMS收集器

  • 目标:最短回收停顿时间
  • 步骤:
    • 初始标记
    • 并发标记
    • 重新标记
    • 并发清除
  • 缺点:
    • 对CPU资源非常敏感
    • 无法处理浮动垃圾
    • 基于“标记-清除”算法实现,碎片多

5、G1 收集器

  • 并行与并发
  • 分代收集
  • 空间整合:
    • 整体看:基于“标记-整理”算法
    • 局部看:基于“复制”算法
  • 可预测停顿
    • 有计划避免Java堆中进行全区域的垃圾收集

6、GC日志

代码语言:javascript
复制
	33.125: [GC [DefNew: 3324k ->152k(3712k),0.0025925 secs] 3324k->152k(11904k),0.0031680 secs]
  • 33.125: GC发生时间
  • [GC:GC停顿类型,如果是 [Full GC
  • [DefNew: 垃圾回收器类型
  • 3324k ->152k(3712k): GC前内存区域已使用容量-> GC后内存区域已使用容量(内存区域总容量)
  • 3324k->152k(11904k): GC前Java堆已使用容量->GC后Java堆已使用容量
  • 0.0025925:内存区域GC所占用时间

五、内存分配和回收策略

最终都是给对象分配内存和回收分配给对象的内存

  • 对象优先在Eden分配
  • 大对象直接进入老年代
  • 长期存活的对象进入老年代
  • 动态对象年龄判定

如果在一个Survivor空间中相同年龄所占内存大小占有一半,那么大于或者等于该年龄的对象直接进入老年代

  • 空间分配担保

新生代的回收采用“复制算法”,如果没有足够的内存空间,需要老生代来担保,将一部分对象直接进入老年代,如果老年代空间还是不足,就有危险了。所以就根据以前进入老年代的对象容量大小的平均值来做个参考。通过Fulll GC 对老年代进行一次GC,尽量腾出更多的空间。以防担保失败。

  • Minor Gc 和 Full GC
    • Minor Gc:新生代的GC,很快
    • Full GC:老年代的GC,慢10倍

六、小结

本篇了解引用的算法,由此去判断一个对象是"死”还是“活”,四种引用类型,由强至弱,生存能力越来越差,被GC回收的可能性也就越高。当然在一生中有唯一一次的自救机会,就是复写finalize()方法。进行垃圾回收时,会影响到性能。所以,出了各种回收算法以及垃圾回收器。这些各有优劣,只有适合当前环境的才是最好的。

小贴士

本文由原作者井方哥独家授权Open软件开发小组发布,著作权归原作者所有。如需转载请联系原作者申请授权。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-11-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Open软件开发小组 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、前言
  • 二、对象
  • 1、引用计数算法
  • 2、可达性分析算法
  • 3、引用
  • 4、finalize方法自救
  • 5、无用的类
  • 三、垃圾回收算法
  • 1、标记——清除法
  • 2、复制算法
  • 3、标记—整理算法
  • 4、分代收集算法
  • 四、垃圾收集器
  • 1、 Serial收集器
  • 2、ParNew收集器
  • 3、Paraller Scavenge收集器
  • 4、CMS收集器
  • 5、G1 收集器
  • 6、GC日志
  • 五、内存分配和回收策略
  • 六、小结
相关产品与服务
数据保险箱
数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档