java垃圾收集

文章开始前考虑一下java程序中什么才是所谓的“垃圾”,回想我们之前说的详见上篇博客 java程序运行时jvm内存分配,其中程序计数器、虚拟机栈、本地方法区都是线程私有的,随线程生和灭;栈中的栈帧随方法的开始和退出进行着入栈和出栈操作,每一个栈帧中分配多少内存基本在类结构确定下来后就已知了,这几个区域的内存分配和回收具备确定性。而java堆和方法区则不一样,一个接口中的多个实现类需要的内存不同,每个方法需要的内存也不一样,我们只有在程序处于运行期间才知道创建的对象,这部分对象的内存分配和回收都是动态的,垃圾收集器所关注的就是这部分内存。

1. 对象死了吗?

在垃圾收集前,垃圾收集器首先要判断对象是否已经“死了”(不可能再通过任何途径使用的对象)。判断对象是否存活的方式有以下几种常见的。

1.1 引用计数法(Reference Counting

给对象添加一个引用计时器,每当有一个地方引用它,计数器值加一;当引用失效时,计数器值减一;当计数器为0时,认为该对象是不可能再被使用的。
优点: 实现简单,判定效率高;
缺点: java对象的引用关系复杂,很难解决对象之间互相循环引用的问题。如下代码所示:

public class ReferCountGCTest{public Object instance = null;private static final int _1MB = 1024*1024;//用于占内存private byte[] bigSize = new byte[2 * _1MB];public static void main(String []args){ReferCountGCTest a = new ReferCountGCTest();ReferCountGCTest b = new ReferCountGCTest();a.instance = b;b.instance = a;a = null;b = null;//gc,a和b能否被回收?System.gc();}
}

这段测试代码里面,令a.instance = b;b.instance = a;除此之外这两个对象再无引用,之后令a和b都为null,实际上这两个对象都不可能再被访问,但是它们互相引用着对方,因此各自的引用计数都不为0,则无法被gc。因此在java虚拟机中不是使用的引用计数法来管理内存。

1.2 可达性分析算法(Reachability Analysis)

主流的商用程序语言都是通过可达性分析来判断对象是否存活。算法思想为:通过一系列被称为“GC Roots”的对象作为起始点,从这些根节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链时,此对象不可达即认为此对象是不可用的。如图:

其中对象5、6、7虽然相互之间有关联,但是都是不可达的,所以会被判定为可回收的对象。

在java语言中,可作为GC Roots的对象包括:
1. 虚拟机栈(栈帧中的本地变量表)中引用的对象。
2. 方法区中类静态属性引用的对象。
3. 方法区中常量引用的对象。
4. 本地方法栈中JNI引用的对象。

2. 垃圾收集算法

判断完什么是垃圾之后,在进行垃圾收集时候主要有有如下算法:

2.1 标记-清除算法

标记-清除算法(Mark-Sweep)是最基础的算法,后续算法都是基于其思路进行改良的。与其名字一样,算法分为标记清除两个阶段:首先标记出所有需要回收的对象,标记完成后统一回收被标记的对象。它的不足主要有两个:一个是效率低,标记和清除的效率都不高,另一个问题是清除之后会产生大量的不连续的内存碎片。空间碎片多了之后当需要给一个较大的对象分配内存时,会因为无法找到足够的连续内存而提前触发一次垃圾收集。但实际上,剩下的内存总量够这个对象分配。
优点:实现方便
缺点:效率低,会产生内存碎片

2.2 复制算法

为了解决上述问题,复制(Copying)算法出现了,它将内存按容量划分为大小相等的两块。每次只使用其中一块内存,当一块内存用完了,就将还存活的对象复制到另一块内存上,然后将已使用的内存一次清理掉,这样每次只要对半个区进行内存回收,也解决了内存碎片问题(只需要在转移时移动堆顶指针,按顺序分配内存即可),实现简单,运行高效。缺点就是将内存缩小为原来的一半,代价太大了!
优点:不会产生内存碎片,效率高于标记-清除。
缺点:牺牲了内存容量

现在的商用虚拟机都采用这种算法思想回收新生代,因为新生代的对象98%的都是“短命”的,是“朝生夕死”的。因此并不需要按1:1的比例划分内存空间,而是将新生代内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor,在回收时,将Eden和该Survivor中还存活的对象一次性复制到另一块Survivor区上,然后清理掉Eden和用过的Survivor空间。HotSpot虚拟机默认的Eden和Survivor大小比例为8:1,即新生代可用内存空间占整个新生代容量的80%+10%=90%,只有10%暂时被浪费掉。

这里插入一个说明,我们没法保证每次回收只有不多于10%的对象存活,当Survivor空间不够转移的时候,老年代空间会进行分配担保。即另一块Survivor不够存放上一次新生代收集后存活的对象时,,这些对象会进入老年代。正常情况下,对象每在Survivor区中“躲过”一次回收,“岁数”就会加一,直到一个我们设定的值后就会进入老年代

复制算法在对象存活率较高的情况下就不适用了,因为较高的存活率意味着更多的复制操作和更多的额外担保空间,因此老年代一般不直接使用复制算法。

2.3 标记-整理算法

根据老年代的特点,有人提出了“标记-整理”(Mark-Compact)算法,标记过程与“标记-清除”算法一样,但是之后不是直接对可回收对象进行清理,而是让存活的对象向一段移动,然后直接清理掉端边界以外的内存。这样不会产生碎片,也避免了大量复制。
优点:不会产生内存碎片,比复制算法节约空间
缺点:整理内存空间需要额外开销。

2.4 分代收集算法

分代收集(Generational Collection)算法本身不是新的算法思想,只是按对象各自的特点奖内存划分为几块,一般是java堆分为新生代和老年代。对于新生代,其中的对象都比较“短命”,存活率低,使用复制算法,只需要复制少量存活对象即可完成收集;对于老年代,其中对象存活率高、没有额外空间进行分配担保,需要使用“标记-清除”或者“标记-整理”算法进行回收。现在的虚拟机一般都采用“分代收集”算法进行垃圾收集。

本文讲述了java虚拟机判断对象是否死亡(成为“垃圾”)的方法,和常用的垃圾收集算法。参考《深入理解java虚拟机》

jvm内存管理-垃圾收集相关推荐

  1. JVM 内存管理(垃圾收集)

    java内存模型和并发 关于 JVM 内存管理或者说垃圾收集,大家可能看过很多的文章了,笔者准备给大家总结下.这算是系列的第一篇,接下来一段时间会持续更新. 本文主要是翻译<Memory Man ...

  2. 详解JVM内存管理与垃圾回收机制2 - 何为垃圾

    随着编程语言的发展,GC的功能不断增强,性能也不断提高,作为语言背后的无名英雄,GC离我们的工作似乎越来越远.作为Java程序员,对这一点也许会有更深的体会,我们不需要了解太多与GC相关的知识,就能很 ...

  3. JVM内存管理:深入Java内存区域与OOM

    Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝 ...

  4. JVM内存管理及GC机制

    转载自:http://blog.csdn.net/suifeng3051/article/details/48292193 一.概述 Java GC(Garbage Collection,垃圾收集,垃 ...

  5. 【JVM调优】JVM内存管理调优浅谈

    什么是JVM Java Virtual Machine,Java虚拟机 Java虚拟机有自己完善的硬件架构,如处理器.堆栈等,还具有相应的指令系统. Java虚拟机本质上就是一个程序,当它在命令行上启 ...

  6. JVM内存管理------垃圾搜集器参数精解

    转载自   JVM内存管理------垃圾搜集器参数精解 垃圾搜集器选择参数 UseSerialGC:开启此参数使用serial & serial old搜集器(client模式默认值). U ...

  7. 详解JVM内存管理与垃圾回收机制5 - Java中的4种引用类型

    在Java语言中,除了基础数据类型的变量以外,其他的都是引用类型,指向各种不同的对象.在前文我们也已经知道,Java中的引用可以是认为对指针的封装,这个指针中存储的值代表的是另外一块内存的起始地址(对 ...

  8. JVM内存管理--笔记

    文章目录 Java代码是如何运行起来的? JVM内存管理 程序计数器 Java虚拟机栈 局部变量表 变量槽 本地方法栈 Java堆 方法区 运行时常量池 直接内存 Java代码是如何运行起来的? Ja ...

  9. 读懂 JVM 内存管理这篇就够了

    读懂 JVM 内存管理这篇就够了 JVM 的内存结构 程序计数器 作用 概述 PC寄存器的常见问题 虚拟机栈 栈中可能出现的异常 栈的存储单位 栈运行原理 栈帧的内部结构 局部变量表 槽 Slot 操 ...

最新文章

  1. timestamp的两个属性:CURRENT_TIMESTAMP 和ON UPDATE CURRENT_TIMESTAMP
  2. glibc手动升级高版本导致系统(RedHat/Centos)异常(无法开机等)的解决方法(回退低版本glibc)
  3. 存储型xss_web安全测试--XSS(跨站脚本)与CSRF
  4. UVA - 10253 Series-Parallel Networks(递推式、记忆化搜索写法)
  5. MySQL8.0.19下载安装及配置详细步骤
  6. c语言模拟试卷答案,C语言模拟试卷及其答案
  7. mysql pdo手册_灾难恢复 - [ MySql参考手册 ] - 在线原生手册 - php中文网
  8. 连通域最小外接矩形算法原理_算法|图论 2W字知识点整理(超全面)
  9. linux mysql 端口 查看进程_Linux如何查看端口状态
  10. ReSharper卸载后Visual Studio的快捷键和智能提示消失
  11. zuc算法代码详解_ZUC算法了解
  12. 应用wps对证件照进行更改颜色,更换只需三步。
  13. 计算机英语词汇汇总,计算机英语词汇汇总
  14. MOX:开创区块链通证参与电影融资的新篇章
  15. 音乐播放器功能的实现,歌词lrc显示,播放过程中来电
  16. 原来在SOLIDWORKS中,把马鞍面填充为实体这么简单!
  17. Unity 交通系统
  18. 美泰推出首款获CarbonNeutral(R)认证的Matchbox(R) Tesla Roadster压铸模型车,使用99%的回收材料制作,彰显品牌的发展蓝图
  19. 在线html5视频播放器,分享10款最棒的免费HTML5视频播放器
  20. 阅读英文原版计算机书籍的一些经验

热门文章

  1. 凤凰x86无限重启_凤凰系统启动不了,一直在出..................
  2. lua tcp socket read timed out
  3. Unity开发日记【第七天】——怪物的移动和动画及类的实现
  4. 计算机网络应用层报告,计算机网络实验报告应用层
  5. [网络工程师]-防火墙-防火墙体系
  6. 408计算机网络学习笔记——应用层
  7. 命令行终端怎么显示√2̅?这其实是一个博客的Unicode测试文章
  8. Android 展讯-关闭IP拨号功能
  9. 解决插入U盘后有提示音,但无法打开、格式化或恢复的问题
  10. 指数平滑方法(一次指数平滑、二次指数平滑、三次指数平滑):理论、代码、参数 介绍(全)