前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JVM 《一 JVM 中的垃圾回收》

JVM 《一 JVM 中的垃圾回收》

作者头像
邹志全
发布2020-02-25 16:24:13
4240
发布2020-02-25 16:24:13
举报
文章被收录于专栏:EffectiveCodingEffectiveCoding

上一篇我们介绍了JVM,还有JVM中的内存结构。

当我们了解其中的内存之后,我们可能会有一点想法,我们的对象、相关类信息是存放在Java堆、方法区之中的。那我们的程序正在不断的new 对象、不断的loading Class。那么我们的JVM为什么没炸了(OOM),即使数量不会多到炸,但是我们不用的那些对象难道一直要仍在内存中?

cpp这样的语言,程序员是对自己的对象负责的,用完之后得了结了它。那Java呢?类似于以上的问题,是依靠JVM的垃圾回收机制去处理那些废弃的对象还有类信息的。

垃圾:废弃的对象、类信息、常量

回收:如何标记垃圾、如何清扫垃圾

垃圾的定义是比较巧妙的,JVM需要完完全全的确定我们不再使用了,才将其定义为垃圾。

《深入理解Java 虚拟机》一书中对什么是垃圾有一个有趣的标题— — 对象已死?

首先说 废弃的对象:

代码语言:javascript
复制
      当一个对象,对我们来说是不可见、并且不可达的。那么如何判断 有这么两种方式或方法

1> 引用计数法:判断程序是否对这个对象还持有引用(这个对象是否还有使用的可能)

这个方法的优缺点显而易见,效率高,但是很有可能出现循环引用。

2>可达性分析:在说可达性之前,先说一个概念叫做GCRoot(既JVM 垃圾回收中判断对象是否可达的起点,是否仍被使用的起始节点),GCRoot常常有这么几个点:栈中所引用的对象(既被方法中直接使用的对象)、方法区中静态元素所引用的对象。

当我们有了GCRoot之后,我们便可以从它们出发去探索我们的对象了,那些不可达的也就可以被判断为应该不会被使用。也就一定程度上可以被标记待回收。

为了好理解,还是画个图吧

image.png

然后知道如何辨别和标记处垃圾之后,剩下的便是清理工作了。

JVM中的垃圾清扫或者收集算法有这么几种:标记-清除、标记-整理、复制、分代回收

标记-清除:

见明知义,先标记再清除(容易实现,但是效率较低、容易产生大量的内存碎片),放个图更好理解吧

image.png

针对内存碎片这个事儿,JVM也算是做出了不少策略。比如下面的,标记整理、复制

标记整理:

清扫掉废弃对象、并且整理,虽然没有内存碎片了,但是需要额外的整理工作,不仅需要标记存活的对象,还需要整理所有存活对象的引用地址。

放个图吧~

image.png

复制:其实是回收时将内存分为两部分,然后将存活的对象收集起来,清除掉待会收区域的垃圾就好啦。效率比标记整理要高,但是浪费了一部分空间。

image.png

JVM是可以这么玩复制算法的,新生代划分为三块区域(Eden区、2个Survival区,这其实就是种简单的面相程序员的一种抽象而已,具体低层实现比这个要复杂的多),其中默认Eden:Survival ==8:1,这个是要根据具体的对象的存活率来定的,为啥?别急,慢慢来。

首先新产生的对象是放在Eden中的,然后GC的时候是将存活对象取出来放到一个Survival(存活区)中,然后回收完成,继续向Eden中扔对象,下一次回收的时候是回收Eden及存放存活对象的那个Survival中,然后把这次的GC幸存者放在空的Survival中,然后回收完成,继续向Eden中扔对象,下一次回收的时候是回收Eden及存放存活对象的那个Survival中,然后把这次的GC幸存者放在空的Survival中(依次循环,我就不复制文字了,其中经历好多次的老对象到达年龄后是被请到老年区的。根据存活率控制空间比值是很重要哒,省的动不动向老年代抛对象)

image.png

然后是分代收集:就是按照对象年龄(经历的回收次数)作为主要标准,对Java Heap进行分区处理,之前作为简单例子说复制的时候提到过。

然后生命周期短的对象、生命周期长的对象,所采取的应该是不一样的,因为对象的存活率差太多。比如标记整理适合老年代,标记清除新生代也可以使用。这些在Java Heap 对象回收的时候其实是配合使用的。具体JVM使用哪种算法,其实是按照垃圾回收器来定的,感觉分代回收很高级的样子,其实在G1中已经弱化分代了。感觉标记-清除那么些毛病,为啥CMS使用它。每个具体的回收算法都有自己的优点和缺点,对这些算法进行合理的优化,相互配合在对象存活率不同的区域使用才能发挥更好的效果。具体的算法或者垃圾回收器的选择,要根据现实世界问题情形及相关物理硬件条件。

然后是对于废弃常量还有类的回收(方法区的回收)

就一点何为废弃的类:

1>该类所有的实例都已经被回收

2>加载该方法的ClassLoader已经被回收

3>该类对应的Class对象已无引用,并且无法通过反射访问。

但满足以上条件时,是允许进行回收的。

并且方法区也是会有溢出风险的,而且也会有废弃产生。所以也具有回收的意义,尤其是在大量反射使用的场景。

然后提几个中间没说到的东西:

1> Stop the world,在对象进行可达性分析的时候,会出现一次骤停(停止在使用对象的线程),然后标记线程启动,对对象进行标记。(并不是一下子让所有线程停止、而是让线程在安全点自行暂停)

2> finalize( ) ,这是每本书都不推荐使用的方法。它是在回收过程中自动执行的,并且仅执行一次。不同于cpp中的析构函数,这个不是来销毁对象的,据说是用来做一些对象销毁前必要的清理工作的。存在内存泄漏的风险。而且这个会让本来在回收流程中的对象复活(仅仅需要在方法中把对象的引用传给一个GCRoot可达的地方就好)。

3>Java 引用相关(传送门~)

4> 大对象直接进入老年代

下一篇 或者 后面几篇 说具体的垃圾回收器,以G1为主,CMS也会说一说。其他那些会大体提一提(较为简单,篇幅不大),然后是关于GC日志分析的。

具体怎么调优和选择,还有我们编码相关的注意事项会在调优那一篇章进行描述~

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
Elasticsearch Service
腾讯云 Elasticsearch Service(ES)是云端全托管海量数据检索分析服务,拥有高性能自研内核,集成X-Pack。ES 支持通过自治索引、存算分离、集群巡检等特性轻松管理集群,也支持免运维、自动弹性、按需使用的 Serverless 模式。使用 ES 您可以高效构建信息检索、日志分析、运维监控等服务,它独特的向量检索还可助您构建基于语义、图像的AI深度应用。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档