导读

本人java小白一枚,写博客用意一是做一个学习总结,二是作一个分享。所写内容难免会有错误或者理解不到位的情况,恳请各位大佬不断对我提出批评,用技术吊打我,鞭笞我。拜谢~~

一、java中的垃圾

1、什么是垃圾

简单来说,就是java内存中没有用了的对象,或者说是已经被嫌弃,死亡的对象。

2、如何去判断对象是属于垃圾对象呢?

最开始,有一种算法叫做引用计数法。顾名思义就是当对象被引用时,通过对对象的引用情况进行登记,如果存在引用的话,则进行加1,否则减1。当该应用计数为0时,则进行回收处理。
示例:
创建一个对象

String name = new String("csdn");


此时,name对象指向"csdn",所以计数器RC为1.

name = null;

后面我们将name设置为null,即将name指向为空,此时“csdn”区域将会被回收。

一切看起来都是那么简单而美好。然后,还是存在一些缺点的。
问题1:
引用计数法并不遵守“STOP THE WORLD”原则,即在回收垃圾时,会将所有java应用程序挂起,而引用计数法是将垃圾回收进行分摊,分到整个的应用程序当中。
问题2:
引用计数算法无法解决循环依赖的问题


public class Main {public static void main(String[] args) {MyObject object1 = new MyObject();MyObject object2 = new MyObject();object1.object = object2;object2.object = object1;object1 = null;object2 = null;}
}class MyObject{private String name;public Object object = null;public MyObject(String name){this.name = name;}
}

代码流程示意图如下:

对象A和B已经不可能访问了,但是在两者还是相互被引用,导致它们的引用数永远都不可能为0.因此有内存溢出的风险。


那么,针对上述两个问题,我们该如何进行改进呢?

实际上,在java中垃圾标记采取的是可达性分析法。可达性分析法的基本思路是通过GC-ROOTS对象作为起点,通过该节点向下进行搜索,搜索所走过的路径称为引用链,如果一个对象跟GC-ROOTS不存在引用链的话,即该对象无法到达GC-ROOTS,就判定该对象为可回收对象。

GC-ROOTS对象主要有四种:
①虚拟机栈中引用的对象
②方法区中静态属性引用的对象
③方法区中常量引用的对象
④本地方法栈(native方法)中引用的对象

那么,被判定为可收回对象就一定会被回收吗?
答案是否定的。
可达性分析法判定为不可达的对象其实相当于一个被判死缓的人,此时的对象处于缓刑期间,在此期间,Object存在finalize方法,让不可达对象做最后的垂死挣扎,如果不可达对象重写了finalize方法,且尚未执行finalize方法,则该对象会进去队列中等待,如果在该方法中重新变为可达,则不会被回收。在队列中的不可达对象如果通过低优先级线程调用执行finalize方法后,仍旧不可达则会被 第二次标记,进行垃圾回收。

事实上,finalize方法的实际用处并不是很大,该方法具有很大的不确定性,无法确定该方法是否执行完,运行的代价也比较高,十分鸡肋。据不可靠消息,java之所以要设计该方法,主要是为了向C语言程序员更容易接受,而做的一个妥协让步。


二、垃圾回收机制

1、标记-清除算法


同过对对象进行扫描标记,然后将标记为死亡对象或者说不可达对象进行清理。这种方法是最简单粗暴和直接,哪里有垃圾就扫哪里。但是该方法同样存在缺点。从图中可以看出,清除后,内存空间存在很多的小碎片,这会影响后续的使用。例如为大对象分配空间时,由于空间碎片化,大空间不足,将会提前触发垃圾回收机制。

2、标记复制方法


为了解决标记-清除所带来的碎片过多的缺点,标记复制算法的提出就是在其基础上所做的改进。该方法是先将内存空间划分为两个部分,一个部分为创建对象区域,一个部分为空闲区域。首先是在创建对象区域对对象进行标记,标记完毕后,将存活对象挪至空闲区域,依次存放,挪动完毕后,一次性将原区域中的死亡对象清除,标记-复制算法解决了标记-清除算法造成碎片过多的弊端,但是同时,我们可以看到,标记-复制算法所能真正使用的空间只有原来的一半,这大大降低内存空间的使用效率。

3、标记-整理算法


为了改进标记-复制算法所带来的空间利用率不高的问题,人们又提出了标记-整理算法,该算法的主要思路是先将对象进行标记,标记完毕之后,将所有存活对象进行移动一端,移动完毕之后,将端边界以外的不可达对象即死亡对象进行一次清除,释放掉内存。该方法即解决了碎片了问题,也解决了空间利用率低的问题。但是,该方法仍旧不是垃圾回收机制的最优解,由于涉及到对象的移动,毫无疑问,程序需要额外增加工作,这就降低了程序回收垃圾的工作效率。就好比你在收拾东西一样,不仅要把垃圾扔掉,还需要把留下的东西分门别类整理好,肯定会占用你的时间。

4、分代回收

有个成语叫做扬长避短,即发挥东西的长处,规避掉它的短处。上述三种算法很难一下子说谁绝对是好,谁绝对的差。而且我们都知道,其实每个对象的生命周期都不尽相同,那么针对不同生命周期的对象我们其实可以采取不同的垃圾回收算法。
那么对象的生命周期我们可以先简单分类一下:
1、临时对象: 朝生夕死的对象,就是命不久矣的对象。可以看做是容易夭折的baby。例如方法内的局域变量,循环体内的临时变量等
2、持久对象: 一般是指能够存活较久的对象。可以看做是正常寿命的人。到达一定岁数还是会挂掉的。例如缓存对象,数据库连接对象等等
3、永久对象: 此类对象可以看做是出生后几乎就不死的对象,可以简单理解为千年老王八。也只是几乎不会挂。例如String池中的对象(享元模式),加载过的类信息等等。
那么,我接下来讲解分代回收的算法实现:
首先,我们先了解一下分代算法的内存模型:

解析:
堆内存中分为新生代和老年代,新生代占1/3,老年代占2/3。其中新生代又分为三个区域,Eden:From survivor:To survivor = 8:1:1。其中有一个关键点需要注意,From区和To区并不是跟图中所示一样,是固定的,二是两者的身份会出现互相变换,这个后面具体算法实现的时候会提到。我们在此先约定,黄色代表From区,绿色代表To区
第一次GC
首先,有对象进来,此时所有堆中都是空的,假设对象0是大对象,我们存放到old区,对象1、2、3、4对象都是小对象,此时他们的age都为0,即代表尚未经历GC回收。此时我们触发一次新生代的minor GC,假设对象1、2为不可达对象,此时我们会清空Eden区和From区(此时From区暂无对象)的不可达对象,并将存活对象挪至To区,并将其age加1.挪完毕后,此时的To区实际上已经变成了From区。思考一下为什么呢?

第二次GC
假设第一次GC完毕之后,又有对象5、6、7进入Eden区,此时又触发了一次minor GC,假设对象6以及From区中的对象3都是是存活对象,那么根据规则(minor GC回收后,Eden区和From区中的存活对象都会挪动到To区,这个就是为什么From和To区是动态交替变化的原因),对象3和对象6统一挪到当前的To区,同时age加1。清除其余的不可达对象。

第N次GC
那么,当GC次数很多时,假设我们的对象3非常之坚挺,撑过了15次minor GC,那么我们就会判定该对象长大了,将其挪至老年代。判定的age其实是可以设置的,默认为15。

那么,老年代在什么时候会触发GC回收呢
一般是在老年代快满了或者System.gc()时,会触发Full GC。对全堆进行回收,同时包括几次minor GC,因此运行时间会比较长。

分代算法总结:
1、新生代采用的算法是标记复制算法,其实是对原有的复制算法的优化,默认将空闲区域划分为10%,即牺牲了10%的空间来换内存的整齐度和GC效率。但是存在一个问题,就是新生代用于保存可达对象可能会存在内存不足的情况。
2、老年代采用的算法是标记-清除和标记-整理算法,具体使用哪种其实和垃圾回收器有关。这里就不一一展开讲了。感兴趣的小伙伴可以自行学习。


大家好,我是一名正在找工作的java程序员,如果您这边有合适的岗位请与我联系(头像即微信)。
原创不易,如果对您有帮助,欢迎三连。如果文中有任何错误,请留言指出,感谢您!

JVM之一:GC垃圾回收原理及算法分析相关推荐

  1. JVM内功心法-GC垃圾回收之GC垃圾回收过程

    JVM内功心法-GC垃圾回收之GC垃圾回收算法 GC 全称garbagecollection,垃圾回收.JAVA 为了屏蔽操作系统和平台之间的差异.选择的是采用 java 虚拟机来运行 java 应用 ...

  2. 【JVM】GC垃圾回收(三)——零落成泥碾作尘,只有香如故

    [引子] 当世界上最后一个记得我的生命也忘记了我的时候,我该如何自处呢?我想我会最后看一眼我深爱的故乡,然后将占用这个世界的最后一缕精华也散去,让灵魂永归沉寂.GC垃圾回收机制也是如此,每一个最终消亡 ...

  3. JVM与GC垃圾回收(各种垃圾收集器,Jvm相关工具)

    1.JVM内存区域: 虚拟机栈,本地方法栈,程序计数器线程隔离,方法区,本地内存,直接内存,堆线程共享. ①.虚拟机栈:描述方法执行时的内存模型, 主要保存执行方法时的局部变量表.操作数栈.动态连接和 ...

  4. 详解JVM和GC垃圾回收

    文章目录 JVM架构图分析 Java虚拟机运行时数据区 对象的创建方式有几种? 创建对象的过程 对象的访问定位 垃圾回收机制 如何判断一个对象是否可以回收 垃圾回收算法都有哪些? 对象分代 垃圾回收器 ...

  5. 精华推荐 | 【JVM深层系列】「GC底层调优系列」一文带你彻底加强夯实底层原理之GC垃圾回收技术的分析指南(GC原理透析)

    前提介绍 很多小伙伴,都跟我反馈,说自己总是对JVM这一块的学习和认识不够扎实也不够成熟,因为JVM的一些特性以及运作机制总是混淆以及不确定,导致面试和工作实战中出现了很多的纰漏和短板,解决广大小伙伴 ...

  6. java知识点8——垃圾回收原理和算法、通用的分代垃圾回收机制、 JVM调优和Full GC、开发中容易造成内存泄露的操作

    垃圾回收原理和算法 内存管理 Java的内存管理很大程度指的就是对象的管理,其中包括对象空间的分配和释放. 对象空间的分配:使用new关键字创建对象即可 对象空间的释放:将对象赋值null即可 垃圾回 ...

  7. Day125.JVM:栈、堆、GC 垃圾回收机制

    目录 一.JVM 体系结构概述 ★★★ 栈 (stack) 堆 (Heap) 新生区.养老区.永久代(元空间) 堆参数调优 OOM 问题定位解决 二.GC 垃圾回收 1.垃圾判定 2.垃圾回收算法 ★ ...

  8. 一文详解,jvm内存分代与垃圾回收原理

    jvm运行时数据区 Java程序启动后,本质上就是启动一个jvm进程,jvm会将自己管理的内存划分为几个区域,每个区域都有自己的用途.在程序运行时的内存区域主要可以划分为五个,分别是:方法区.堆.虚拟 ...

  9. JVM架构和GC垃圾回收机制--面试

    JVM架构和GC垃圾回收机制详解 JVM架构图分析 下图:参考网络+书籍,如有侵权请见谅 (想了解Hadoop内存溢出请看: Hadoop内存溢出(OOM)分类.参数调优化) JVM被分为三个主要的子 ...

最新文章

  1. devops 成长路线
  2. mega_[MEGA DEAL] Ultimate JavaScript电子书和课程包(96%折扣)
  3. day08 MapReduce
  4. 擴展PictureBox的一個組件
  5. SPField的几种name的释疑
  6. Spring的核心模块解析
  7. 控制台发送get命令_.NET Core使用命令行参数库构建控制台应用程序
  8. 值传递与引用传递 c# 1613535799
  9. LabView学习笔记(三):基本控件
  10. 浮点数的加减计算总结
  11. hdu 6377 度度熊看球赛 (dp)
  12. 基于FPGA三大串行通信接口简单概述
  13. 投票和排名系统C语言顺序表,电视大赛观众投票及排名系统C语言设计.docx
  14. 成考专科计算机专业,我是计算机专科生,成考想换个专业,请问学什 – 手机爱问...
  15. 各种器件的灵敏度、精度、分辨率的区别
  16. c语言数组众数,众数问题 (C语言代码)
  17. java 获取file后缀,Java获取文件后缀的方法
  18. 美国音乐学院计算机音乐专业排名2015年,美国音乐学院排名小提琴专业排名大全(本科)...
  19. 阿里巴巴高级技术专家 至简 聊工程师思维(配音版)
  20. 安装2008 R2 SQL,在安装程序支持文件时页面闪退

热门文章

  1. boost::bind(mynteye::imageCallback,this, _1, _2)报错error: no match for call to ‘(boost::_mfi::mf2<vo
  2. 09. 路由器单臂路由配置
  3. 如何分析线程Dump和堆Dump
  4. 赠书 | 年终盘点:超融合架构(HCI)的现状和前景; 中国HCI厂商列表; 全球有哪些HCI厂商?
  5. Python实现二维离散卷积运算
  6. 互联网站规划与设计.txt
  7. 什么叫loopback地址?是怎样用的?
  8. 浏览器开发者选项取消已在调试程序中暂停
  9. QQ图片文件夹说明及清理
  10. QNAP-NAS外网访问——aliyun-ddns,docker,myqnapcloud