谈到垃圾回收器,java程序员骄傲了起来,c语言你是够快,但是你有管家帮你打扫吗,还不是得靠自己的一双手,有钱就是任性。既然如此令java程序员骄傲的垃圾回收器,怎能让人不想去一探究竟呢!

  垃圾回收器字面意思就是回收垃圾的,那么对于程序来说,什么事垃圾呢,怎样定位垃圾呢,我们来看一下:

一、什么是垃圾

  垃圾简单来说,就是废弃的东西,我不需要了,他就是垃圾,记得之前看言情剧总有一句台词,就算是被全世界嫌弃,但是在我这里依然是宝贝,同理,如何定义垃圾,众说纷纭,对于程序来说,最常见的垃圾回收器主要还是:引用计数法,可达性分析。

  1.引用计数法:

    引用计数法就是对每个对象的引用进行计数,有一个引用指向它就加1,当计数为0表示没有引用,这就是个垃圾。

  2.可达性分析

    可达性分析理解起来稍微复杂一点,核心就是根(GC ROOT)可达,从根节点出发,向下发散搜索,所有能引用到的对象才是宝贝,其他的都是垃圾。那么问题来了,根节点到底是啥:

    可以作为根节点的对象:

    1. 虚拟机栈中(本地变量表)引用到的对象,如各个线程堆栈使用的参数,局部变量,临时变量等,其实就是当前还被线程持有的变量,

    2. 方法区中类静态属性引用到的对象如java类的静态变量

    3. 方法区中常量对象的引用,如字符串常量池的引用

    4.本地方法栈中引用的对象,即我们的native方法。

    5.java内部引用,如class对象、异常对象(NullPointException,OutofMemoryError等),系统类加载器

    6.同步锁synchronized持有的对象

    7.JVM 内部的 JMXBean、JVMTI 中注册的回调、本地代码缓存等

    8.JVM 实现中的“临时性”对象,跨代引用的对象

    说实话我也不怎么记这个东西,我觉得要我背的东西都不是好东西,所以我理解的话,主要还是理解前面几个如虚拟机栈中局部变量表持有对象,那么局部变量表里面有的对象又是啥呢,线程在使用的对象,那么自然是不能被回收的,静态变量属于类级别的,这种对象是需要可以直接被使用的,自然也不能被回收,常量我理解和静态变量一样,native方法那不是我们能管的,自然也不能被回收,其实理解了也就是线程在用的不能回收,随时要被用的不能回收,native不归你管,你凭啥回收呢,不用背,自然能记得。

  看了上述两种方法,让你选你会选哪个,你可能会选择第一个,毕竟简单,但是咱们再来看一下,第一种垃圾分类的方法,如果我有俩对象,虽然都不用了,但是你拉着我,我拉着你,自然是不能被回收的,但是其实呢,已经不用了,一个固然无所谓,但是程序在运行是,可能会出现无数个这样的状况,而且这种对象你很可能永远都无法回收,越积越多,多恐怖的一件事,所以你必然是需要其他方式来解决这种问题,这么一想好像也没那么简单,但是第二种虽然听起来麻烦一点,但是确实不会出现这样的问题,也不需要任何辅助。好了如果是你,现在你会选哪个呢。

二、垃圾回收基础

  1.丐帮秘诀基础篇 --- 什么是GC

    GC全称Garbage Collection,翻译过来就是垃圾回收,在java中其实就是自动化的垃圾回收机制

    我们一般关注的GC主要是针对堆中进行回收,栈中由于是随线程自动回收,方法区的回收效率极低,因此不需要太多关注,毕竟还是回收对象嘛,对象在哪呢,自然是堆中。

  2.丐帮秘决晋级篇 --- 垃圾分代

    

    垃圾收集器一般分为新生代和老年代,新生代又分为Eden区和两个survior区,至于为什么分呢,首先我们要了解一下对象的GC分配策略:

      1.栈上分配--- 判断当前对象可逃出当前方法,其实就是判断当前对象是否有可能在任何其他方法中有可能被修改,任意如作为入参传入其他方法,作用域不仅限于当前方法等都算是逃出当前方法(其实这就是大名鼎鼎的逃逸分析),如果确定不会逃出方法,则可直接在栈上分配,相信看过前面文章的同学应该知道栈比堆要快很多,直接栈上分配可以显著提高jvm效率。

      2.对象优先在Eden区分配 --- 即对象会首先进入Eden区,如果Eden区分配满了,则会发起一次minorGc,为啥要这么做待会会细讲。

      3. 大对象直接进入老年代 --- 啥叫大对象呢,比如一个大的list,一个占用很大内存的数组,其实就是一个对象个体,但是占用的内存很大。

      4.长期存活对象进入老年代 --- 什么是长期存活对象呢,上节有说过,对象头里面有个记录GC年龄的,每次发生minorGc,年龄都会加一,hotsport默认是年龄达到15会进入老年代,当然,这个也是可以通过配置-XX:MaxTenuringThreshold 调整。  

      5.对象年龄动态判断 --- 如果在survior空间中,同意年龄对象总和大于survior空间的一半,年龄大于或等于这个年龄的对象可以直接进入老年代

      6.空间分配担保 --- 网上的解释话太多了,我总结一下就是对象从新生代准备进入老年代的时候,如果设置了允许空间担保, 则继续检查新生代最大可用连续空间是否大于历代晋级到老年代对象的平均大小,如果大于,则直接进入老年代,担保失败则需进行fullGc,而没有设置分配担保如果新生代总大小大于老年代最大可用连续空间,则需先fullGc。可以看到区别就是一个是担保失败fullGc,而另一个则是直接fullGc(这里新生代大于老年代最大可用连续空间是前提)。

    介绍完了对象分配策略,我们在回过头来看一下为啥分代要这么分,大家都知道对象是朝生夕死的,而分新生代和老年代就是要让一大部分对象在新生代就pass掉,而新生代又分为Eden和survior区,这样分完,很多对象在进行第一次minorGc时,就直接pass掉了,根本不需要进入survior区,这也是为啥那么对象要优先在Eden区分配,而大对象(可以设置参数-XX:PretenureSizeThreshold控制大小)直接进入老年代,如果当出现很多朝生夕死的大对象,那么由于没有新生代的过滤,老年代很快占满,就需要频繁FullGc,就会导致系统卡顿,这种确实没有什么好的办法解决,毕竟大对象新生代是装不了多少的,想想还是这么设计靠谱,大牛们果然不是盖的。

    GC分配策略好像是有点多,背肯定是会忘记的,所以还是要靠理解,比如上面说的为啥要这么分代,正是因为这么解决是比较合适的,理解了这么设计的原因,其实这些策略看起来更像是一种解决方案。

    

三、垃圾回收算法

  垃圾回收算法主流的主要有三种,复制回收,标记清除,标记整理,评价之前我们先了解一下吧:

  1.复制回收

    复制回收算法强调复制两个字,首先会将内存分为两块,一部分存储,一部分用来复制,因此只有一半内存可用,流程如下图首先会将可用内存复制到另一半,之后清掉之前那一半内存,供下次复制使用。复制算法的优点就是快,直接复制过来,原来的直接清掉,而且不会产生内存碎片。

        

  2.标记-清除(Mark-Sweep)

    标记清除算法分为两步,标记和清除,看起来就很麻烦,首先标记出所有需要回收的对象,之后清除回收掉之前标记的对象。标记清除相对于复制算法不需要额外的一份内存,但是会导致有内存碎片,如下图,可以发现很多断断续续的内存块,这种情况如果来一个比较大的对象,即便内存够用,但是没有足够大的连续内存,依然无法使用,此时就必须要再次触发垃圾回收。

  

  3.标记整理(Mark-Compact)

   标记整理算法在我看来更应该叫标记-整理-清除算法,首先标记出所有可回收对象,随后将所有不可回收对象向另一边移动,之后清除掉可以范围外的垃圾,可以看出步骤很复杂,耗时明显会比前两种要长,但是相比复制算法,它不需要额外的内存,相比标记清除算法,它不会产生垃圾碎片。

                        

                  

                       

                 

       

  以上是常用的三种垃圾回收算法,复制算法明显的是用空间换时间,标记清除舍弃了部分空间内存连续性,而最后一种就是用时间换空间了。

  记得很久之前面试的时候,面试官问过我一个问题,新生代使用的是什么算法,我记得自己看过,就回答了复制算法,说实话当时也是连猜带蒙的,紧接着就是为什么,这个问题给我干蒙了,当时脑子里的想法就是,我哪知道为什么,你问设计这个算法的人啊。但是最后还是卑微的回答了句,不知道。我知道这个面试已经结束了,确实是实力不够,回来之后就把jvm这块系统的重新学了遍,回炉重造。

  还是回到问题,新生代为啥要用复制算法呢,有研究表明(确实是用大量数据得出的结果),98%以上的对象都是朝生夕死的,复制过去的对象只占用极少数,那么新生代为啥又要搞这么多分区,又是Eden又是survior,survior还整俩,我们梳理一下常规对象进入新生代的过程是怎样的:首先进入Eden区,随后Eden区满之后,会进行minorGc,可用对象会第一次进入survior区,而下一次survior区满之后,会再次进行minorGc,将对象从survior 1区复制到2区,再清掉1区,循环直到满足GC分配策略进入老年代。如果没有Eden区,则俩survior区都需要扩大很多,而复制算法必须要用俩同样大小的内存,因此俩survior区缺一不可,这时候可能有的小伙伴要说了,标记清除,标记整理不都可以吗,也不是不可以,但是它不够快,标记清除还会有残留内存碎片,标记整理效率太低,因此后两种更多地用于老年代的回收。

总结:

  这一章主要还是介绍垃圾回收的概念,什么是垃圾,垃圾回收器是啥样的,最后垃圾回收器在回收的时候是怎么玩的,都是偏理论的知识,可能理解起来没那么容易(也是我没有写好),但是我相信看完也大概知道我们的垃圾回收器大概是个啥,其实我最想表达的不仅仅是垃圾回收器的分代是啥样的,我想写出的是为什么要这么设计,从对象朝生夕死的特点,我们也可以知道这种分代方式是比较合理的,又通过反证法,证明如果说少了Eden或者survior,都是有很多不必要的弊端,而在对象分配策略中,我们基本也可以通过对象的特点和分代模型,分析出这样设计的合理性,比如优先在Eden区分配是因为这样可以过滤掉绝大部分朝生夕死的对象,其实理解了这些,可能问你分配策略时你还是记不住哪些点,但是你对jvm调优这方面,绝对前进了了一大步。

java虚拟机入门(四)-垃圾回收的故事的更多相关文章

  1. 《深入理解 Java 虚拟机》学习 -- 垃圾回收算法

    <深入理解 Java 虚拟机>学习 -- 垃圾回收算法 1. 说明 程序计数器,虚拟机栈,本地方法栈三个区域随线程而生,随线程而灭,这几个区域的内存分配和回收都具备确定性 Java 堆和方 ...

  2. 深入理解Java虚拟机之JVM垃圾回收随笔

    1.对象已经死亡? 1.1引用计数法:给对象中添加一个引用计数器,每当有一个地方引用他时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器都为0的对象就是不可能再被使用 的.但是它很难解决 ...

  3. Java虚拟机(JVM)与垃圾回收机制(GC)的详解

    一.JVM结构 根据<java虚拟机规范>规定,JVM的基本结构一般如下图所示: 从左图可知,JVM主要包括四个部分: 1.类加载器(ClassLoader):在JVM启动时或者在类运行时 ...

  4. 深入理解java虚拟机之——JVM垃圾回收策略总结

    如何判断一个对象是否存活 引用计数算法:给对象中添加一个引用计数器,每当有引用它时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器为0的对象就是不可能再被使用.  Java虚拟机里面没有 ...

  5. 深入理解java虚拟机---读后笔记(垃圾回收)

    运行时数据区,主要包括方法区.虚拟机栈.本地方法栈.堆.程序计数器,该部分内存都是线程隔离的. 然后和其交互的有执行引擎.本地库接口,此部分线程之间是可以共享的. 1. 引用计数算法 给对象添加一个引 ...

  6. 【转】Java虚拟机的JVM垃圾回收机制

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp43       1.JVM内存空间     JVM堆(Heap)= 新生代 ...

  7. Java基础教程:垃圾回收

    Java基础教程:垃圾回收 垃圾回收 垃圾回收(Garbage Collection,GC),顾名思义是释放垃圾占用的空间,防止内存泄漏.有效的使用可以使用的内存,对内存堆中已经死亡的或者长时间没有使 ...

  8. JAVA虚拟机内存分配与回收机制

    Java虚拟机(Java Virtual Machine) 简称JVM Java虚拟机是一个想象中的机器,在实际的计算机上通过软件模拟来实现.Java虚拟机有自己想象中的硬件,如处理器.堆栈.寄存器等 ...

  9. Java内存管理 -JVM 垃圾回收

    版权声明:本文为博主原创文章,未经博主允许不得转载 一.概述 相比起C和C++的自己回收内存,JAVA要方便得多,因为JVM会为我们自动分配内存以及回收内存. 在之前的JVM 之内存管理 中,我们介绍 ...

随机推荐

  1. 史上最全Xshell and Xftp 工具的使用

    文章目录 什么是xshell 解决: 安装Xshell Xshell怎么建立连接 Xshell如果修改已有连接信息? 修改,背景色,字体,编码 Xshell导出已有的登录信息 Xftp的使用 XFP建 ...

  2. linux下使用vsftp搭建FTP服务器:匿名登录,账号登录,SSL加密传输

    目录 一.关于FTP和VSFTP 二.ftp.sftp.vsftp.vsftpd的区别 三.项目一:搭建一台所有人都可以访问的通用FTP服务器 3.1 项目要求 3.2 项目思路分析 3.3 使用vs ...

  3. 个人MySQL的事务特性原理学习笔记总结

    目录 个人MySQL的事务特性原理笔记总结 一.基础概念 2. 事务控制语句 3. 事务特性 二.原子性 1. 原子性定义 2. 实现 三.持久性 1. 定义 2. 实现 3. redo log存在的 ...

  4. MRP物料需求计划

    1.重订货点的采购计划. 计算方式:再订货点的库存数量 = 安全库存 + 采购提前期 * 每天消耗的数量 一旦库存数量触及再订货点的库存数量,需触发采购订单订购物料,理想的情况下 ,下次到采购订单收货 ...

  5. SpringBoot进阶教程(六十九)ApplicationContextAware

    在某些特殊的情况下,Bean需要实现某个功能,但该功能必须借助于Spring容器才能实现,此时就必须让该Bean先获取Spring容器,然后借助于Spring容器实现该功能.为了让Bean获取它所在的 ...

  6. 基于snort、barnyard2和base的 网络入侵检测系统的部署与应用

    1.项目分析 1.1.项目背景 伴随着互联网产业的不迅猛发展,新兴技术层数不穷,互联网通讯技术逐渐成为了各行各业不可替代的基础设施,越来越多的业务都是依靠互联网来得以实现.随着我国科技产业的飞速发展, ...

  7. Thymeleaf语法总结 | 笔记分享

    Thymeleaf语法总结 一.Thymeleaf介绍 Thymeleaf是Spring boot推荐使用的模版引擎,直接以html显示,前后端可以很好的分离.   二.Thymeleaf语法(Thy ...

  8. Redis缓存篇(一)Redis是如何工作的

    Redis提供了高性能的数据存取功能,所以广泛应用在缓存场景中,既能有效地提升业务应用的响应速度,还可以避免把高并发压力发送到数据库层. 因为Redis用作缓存的普遍性以及它在业务应用中的重要作用,所 ...

  9. Apache下的配置文件httpd.conf、httpd-vhosts.conf 转

    Apache下的配置文件httpd.conf.httpd-vhosts.conf(windows) 2013-05-24 22:09 by youxin, 58 阅读, 0 评论, 收藏, 编辑 ht ...

  10. 两个很赞的用法(count函数里的表达式+计算时间间隔)

    1.count函数里写表达式 #无效写法,这样写不会判断表达式(ischecked=0),会全部列出来 SELECT cardid FROM search_detail GROUP BY cardid ...