javaGC回收机制

在面试java后端开发的时候一般都会问到java的自动回收机制(GC)。在了解java的GC回收机制之前,我们得先了解下Java虚拟机的内存区域。

java虚拟机运行时数据区

java虚拟机在执行的过程中会将其管理的内存划分为不用的数据区域,不同的区域有不同的作用以及线程时间。

数据区划分如下:

 

下面将介绍不同区域的作用,如果已经了解可以跳过

  • 程序计数器(线程私有)

    程序计数器的作用很简单,就是记录当前线程所执行的位置(所以为线程私有),可以看成当前线程所执行的字节码的行号指示器。如果执行的是native方法,则这个计数器为空。

  • Java虚拟机栈(线程私有,生命周期与线程相同)

    虚拟机栈描述的是Java方法执行的内存模型:每个Java方法在执行的时候都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息

     
  • 本地方法栈(线程共享)

    本地方法栈与虚拟机栈发挥的作用类似,不过它执行的是虚拟机使用的Native方法。

  • Java堆(线程共享)

    Java堆是Java虚拟机管理内存中最大的一块,在虚拟机启动的时候创建。此区域的唯一目的就是存放对象示例,几乎所有的对象实例都是在这分配内存的。

  • 方法区(线程共享)

    刚开始的时候,看到方法区域,第一想法就是Java中的方法,不过实际上并不是这样。方法区储存的是已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。我们可以想一想,当我们需要创建一个对象的时候,我们需要根据类的信息去创建,那么类的信息在哪?当然是在方法区!

    • 运行时常量池

      运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量和符号引用。

垃圾收集(Garbage Collection)GC

前面说了这么多,现在我们终于可以来说说垃圾回收机制了。

首先我们得说下垃圾回收回收的是哪一部分内存区域。在前面我们知道:程序计数器,虚拟机栈,本地方法栈都是线程私有的,随着线程生或灭。这部分我们就不需要考虑了。所以我们需要考虑的就是Java堆方法区

垃圾回收的内容

回收java堆

  • 对象是否可以被回收

    判断对象是否被回收就是当一个对象死了的时候就需要进行回收。那么如何判断一个对象是否死亡,在Java中,我们使用了可达性分析算法来判断对象是否存活。

     

    当一个对象到GC Roots没有任何链(称为引用链)相连(也就是对象到GC Roots不可达)则判定对象已经死亡(如图中的Object5,Object6),可进行回收。

    可作为GC Roots的对象:

    • 虚拟机栈(栈帧中的本地变量表)中引用的对象
    • 方法区中类静态属性引用的对象
    • 方法区中常量引用的对象
    • 本地方法栈中JNI(即一般说的Native方法)引用的对象

    在前面中,我们知道,不可达就意味着回收,可是当我们的内存很够时,有一些对象又是“食之无味弃之可惜”的时候,我们怎么办呢?在JDK1.2中,Java对引用进行扩张,分为以下引用:

    1. 强引用(Strong Reference):只要强引用还在,则不回收
    2. 软引用(Soft Reference):描述一些有用但非必须的对象,在系统将要发生内存溢出之前,将这些对象列入回收范围之中进行第二次回收。<java.lang.ref.SoftReference>
    3. 弱引用(Weak Reference):比软引用还要弱,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。<java.lang.ref.WeakReference>
    4. 虚引用(Phantom Reference):不会对生存时间构成影响,唯一的作用就是这个对象被回收的时候会收到一个通知。<java.lang.ref.PhantomReference>
  • 最终判断对象是否能够存活

    在可达性分析算法中,如果一个对象不可达,那么这个对象就进入到了“缓刑”阶段,真正宣告一个对象死亡还需要进行两次标记。

    1. 第一次标记进行筛选

      对不可达的对象进行第一次标记并进行筛选。筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize方法,或者finzlize方法已经被虚拟机调用过(意思就是finalize()方法只能被调用一次,也就是对象只能够有一次避免被回收),虚拟机将这两种情况都视为“没有必要执行”,对象被回收。

    2. 第二次标记

      如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个名为:F-Queue的队列之中,并在稍后由一条虚拟机自动建立的、低优先级的Finalizer线程去执行。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束。这样做的原因是,如果一个对象finalize()方法中执行缓慢,或者发生死循环(更极端的情况),将很可能会导致F-Queue队列中的其他对象永久处于等待状态,甚至导致整个内存回收系统崩溃。

      finalize()方法是对象脱逃死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模标记,如果对象要在finalize()中成功拯救自己----只要重新与引用链上的任何的一个对象建立关联即可,譬如把自己赋值给某个类变量或对象的成员变量,那在第二次标记时它将移除出“即将回收”的集合。如果对象这时候还没逃脱,那基本上它就真的被回收了。

回收方法区

在Java虚拟机规范中说过不要求方法区实现垃圾收集,并且进行垃圾收集的“性价比”也较低。不过既然写了,那必定有方法区的垃圾收集,主要回收以下两部分内容:

  • 废弃常量:字面量和符号引用

  • 无用的类:

    1. 该类的所有实例都被回收,即:Java堆中不存在该类的任何实例
    2. 该类的Classloader已经被回收
    3. 该类对用的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问到该类的方法。
      当满足以上三个条件时,也未必说是一定要被回收。也仅仅是可以。

垃圾收集算法

年代划分

我们通过对象的存活周期来将JVM堆中内存空间划分为新生代和老年代。

  1. 新生代:主要是用来存放新生的对象。一般占据堆的1/3空间。

  2. 老年代:主要存放应用程序中生命周期长的内存对象。

算法

OK,说了这么多,我们现在终于可以来说说垃圾收集的算法了。

下面的图片来源于这位大佬,这位大佬讲的真滴不错。

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

    标记:首先标记需要回收的对象,标记完成统一回收

    清除:就是清除对象,释放空间

     

    缺点:标记和清除的效率不高,同时产生大量不连续的内存碎片(可能不利于下次的空间分配)。

  • 标记整理法

    标记整理算法相比较于标记清除算法,标记-整理算法在清除的时候并不是一个一个的清除对象释放空间,而是一次清除全部的可回收的空间。这样使得空间变得连续,有利于对象空间的分配。

     
  • 复制算法

    1. 将内存分成两块大小相等的空间。
    2. 每次使用其中一块。
    3. 进行垃圾回收的时候,将不要的回收的对象复制到另外一个空间
    4. 完全清除原来的空间。

     

    优点:速度快,效率高,不会产生内存碎片。

    缺点:显而易见,空间浪费大,缩小了一半。

    解决方法

    IBM研究表明:新生代98%的对象是“朝生夕死”,所以我们并不需要将空间划分为1:1,而是将空间划分为Eden:Survivor:Survivor = 8:1:1。每次使用Eden和其中一块Survivor。

    1. 使用其中Eden和一块Survivor。
    2. 进行回收时,讲Eden和Survivor还存活的对象一次性的复制到另外一块Survivor上。
    3. 清理第一步中的Eden和Survivor。

    如果第二步中Survivor的空间不足,则依赖于其他内存(老年代)进行分配担保(也就是讲存活的对象放入老年代)。

     
  • 分代收集算法

    分代收集算法其实就是前面几种算法的应用。根据年代使用不同的算法

    1. 新生代GC(MinorGC,回收速度快):复制算法
    2. 老年代(Full GC/Major GC,比Minor慢10倍以上):标记整理法和标记清除法。

对象分配内存区域

  1. 新生代:大多数情况下爱,对象在新生代Eden区中分配。如果没有足够的空间,则发起一次MinorGC。
  2. 老年代:
    • 大对象直接进入老年代。比如说很长的字符串或数组。
    • 长期存活的对象:没熬过一次MinorGC,年龄age增加一岁,当它的年龄超过一定岁数时(默认15,可设置),则进入老年代中。
    • 动态对象年龄判定:如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代。

参考书籍:《深入理解Java虚拟机》——周志明,这本书写的太好了,写的通熟易懂。强烈推荐去看看。

一文看懂javaGC的更多相关文章

  1. 一文看懂web服务器、应用服务器、web容器、反向代理服务器区别与联系

    我们知道,不同肤色的人外貌差别很大,而双胞胎的辨识很难.有意思的是Web服务器/Web容器/Web应用程序服务器/反向代理有点像四胞胎,在网络上经常一起出现.本文将带读者对这四个相似概念如何区分. 1 ...

  2. 一文看懂https如何保证数据传输的安全性的【转载、收藏】

    一文看懂https如何保证数据传输的安全性的   一文看懂https如何保证数据传输的安全性的 大家都知道,在客户端与服务器数据传输的过程中,http协议的传输是不安全的,也就是一般情况下http是明 ...

  3. [转帖]一文看懂web服务器、应用服务器、web容器、反向代理服务器区别与联系

    一文看懂web服务器.应用服务器.web容器.反向代理服务器区别与联系 https://www.cnblogs.com/vipyoumay/p/7455431.html 我们知道,不同肤色的人外貌差别 ...

  4. [转帖] 一文看懂:"边缘计算"究竟是什么?为何潜力无限?

    一文看懂:"边缘计算"究竟是什么?为何潜力无限? 转载cnbeta   云计算 雾计算 边缘计算...   知名创投调研机构CB Insights撰文详述了边缘计算的发展和应用前景 ...

  5. 一文看懂Stacking!(含Python代码)

    一文看懂Stacking!(含Python代码) https://mp.weixin.qq.com/s/faQNTGgBZdZyyZscdhjwUQ

  6. Nature 为引,一文看懂个体化肿瘤疫苗前世今生

    进入2017年,当红辣子鸡PD-1疗法,一路横扫多个适应症.而CAR-T治疗的“小车”在获得FDA专委会推荐后也已经走上高速路,成为免疫治疗又一里程碑事件.PD-1.CAR-T之后,下一个免疫治疗产品 ...

  7. 一文看懂大数据的技术生态圈,Hadoop,hive,spark都有了

    一文看懂大数据的技术生态圈,Hadoop,hive,spark都有了 转载: 大数据本身是个很宽泛的概念,Hadoop生态圈(或者泛生态圈)基本上都是为了处理超过单机尺度的数据处理而诞生的.你可以把它 ...

  8. 转载来自朱小厮博客的 一文看懂Kafka消息格式的演变

    转载来自朱小厮博客的 一文看懂Kafka消息格式的演变     ✎摘要 对于一个成熟的消息中间件而言,消息格式不仅关系到功能维度的扩展,还牵涉到性能维度的优化.随着Kafka的迅猛发展,其消息格式也在 ...

  9. 【转帖】一文看懂docker容器技术架构及其中的各个模块

    一文看懂docker容器技术架构及其中的各个模块 原创 波波说运维 2019-09-29 00:01:00 https://www.toutiao.com/a6740234030798602763/ ...

随机推荐

  1. 使用JScript编译指定目录下所有工程

    作者:朱金灿 来源:http://blog.csdn.net/clever101 我遇到这样一个问题:在一个插件工程目录下的插件工程越来越多,因此通过建一个解决方案然后把新增加的工程逐个添加进解决方案 ...

  2. 简单使用.net core 自带的DI

    1.创建一个web api项目 2.在项目中创建一个接口类 namespace LearnCore.CoreDI { public interface ILearnDI { string GetNam ...

  3. 随机森林与 GBDT

    随机森林(random forest),GBDT(Gradient Boosting Decision Tree),前者中的森林,与后者中的 Boosting 都在说明,两种模型其实都是一种集成学习( ...

  4. uow Unit of work

    通过学习圣杰的文章  UnitOfWork知多少  知道uow其实就是为了解决 一次提交所有更改 1.ef本身可以具备这样一个功能,但是我们在写仓储的实现的时候 经常会直接显式saveChanges了 ...

  5. opengl编程指南 第七版 源代码bug Page35 lines.c 红宝书

    问题1:根据源代码时,我发现的时候去敲门.不正确实施效果.哪里是不正确?没有源代码glPushAttrib(GL_LINE_STIPPLE) glPopAttrib().所以会出现最后的下一次抽奖提供 ...

  6. Java之java.lang.IllegalMonitorStateException

    今天又中彩了, 原本很简单的多线程程序, 蓦然间冒了个"java.lang.IllegalMonitorStateException" , 杀了个措手不及. 一直纳闷, 为什么为什 ...

  7. C# WPF 中用代码模拟鼠标和键盘的操作

    原文:C# WPF 中用代码模拟鼠标和键盘的操作 原文地址 C#开发者都知道,在Winform开发中,SendKeys类提供的方法是很实用的.但是可惜的是,在WPF中不能使用这个方法了. 我们知道,在 ...

  8. ControlTemplate

    ControlTemplate:外观定制 <Window.Resources> <ControlTemplate x:Key="CheckBoxControlTemplat ...

  9. 读BeautifulSoup官方文档之html树的搜索(2)

    除了find()和find_all(), 这里还提供了许多类似的方法我就细讲了, 参数和用法都差不多, 最后四个是next, previous是以.next/previous_element()来说的 ...

  10. .NET CORE EnvironmentVariable

    .NET CORE System variables SETIn System variablese.g1:Variable name: ASPNETCORE_ENVIRONMENTVariable ...