jvm内存管理-垃圾收集
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内存管理-垃圾收集相关推荐
- JVM 内存管理(垃圾收集)
java内存模型和并发 关于 JVM 内存管理或者说垃圾收集,大家可能看过很多的文章了,笔者准备给大家总结下.这算是系列的第一篇,接下来一段时间会持续更新. 本文主要是翻译<Memory Man ...
- 详解JVM内存管理与垃圾回收机制2 - 何为垃圾
随着编程语言的发展,GC的功能不断增强,性能也不断提高,作为语言背后的无名英雄,GC离我们的工作似乎越来越远.作为Java程序员,对这一点也许会有更深的体会,我们不需要了解太多与GC相关的知识,就能很 ...
- JVM内存管理:深入Java内存区域与OOM
Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝 ...
- JVM内存管理及GC机制
转载自:http://blog.csdn.net/suifeng3051/article/details/48292193 一.概述 Java GC(Garbage Collection,垃圾收集,垃 ...
- 【JVM调优】JVM内存管理调优浅谈
什么是JVM Java Virtual Machine,Java虚拟机 Java虚拟机有自己完善的硬件架构,如处理器.堆栈等,还具有相应的指令系统. Java虚拟机本质上就是一个程序,当它在命令行上启 ...
- JVM内存管理------垃圾搜集器参数精解
转载自 JVM内存管理------垃圾搜集器参数精解 垃圾搜集器选择参数 UseSerialGC:开启此参数使用serial & serial old搜集器(client模式默认值). U ...
- 详解JVM内存管理与垃圾回收机制5 - Java中的4种引用类型
在Java语言中,除了基础数据类型的变量以外,其他的都是引用类型,指向各种不同的对象.在前文我们也已经知道,Java中的引用可以是认为对指针的封装,这个指针中存储的值代表的是另外一块内存的起始地址(对 ...
- JVM内存管理--笔记
文章目录 Java代码是如何运行起来的? JVM内存管理 程序计数器 Java虚拟机栈 局部变量表 变量槽 本地方法栈 Java堆 方法区 运行时常量池 直接内存 Java代码是如何运行起来的? Ja ...
- 读懂 JVM 内存管理这篇就够了
读懂 JVM 内存管理这篇就够了 JVM 的内存结构 程序计数器 作用 概述 PC寄存器的常见问题 虚拟机栈 栈中可能出现的异常 栈的存储单位 栈运行原理 栈帧的内部结构 局部变量表 槽 Slot 操 ...
最新文章
- timestamp的两个属性:CURRENT_TIMESTAMP 和ON UPDATE CURRENT_TIMESTAMP
- glibc手动升级高版本导致系统(RedHat/Centos)异常(无法开机等)的解决方法(回退低版本glibc)
- 存储型xss_web安全测试--XSS(跨站脚本)与CSRF
- UVA - 10253 Series-Parallel Networks(递推式、记忆化搜索写法)
- MySQL8.0.19下载安装及配置详细步骤
- c语言模拟试卷答案,C语言模拟试卷及其答案
- mysql pdo手册_灾难恢复 - [ MySql参考手册 ] - 在线原生手册 - php中文网
- 连通域最小外接矩形算法原理_算法|图论 2W字知识点整理(超全面)
- linux mysql 端口 查看进程_Linux如何查看端口状态
- ReSharper卸载后Visual Studio的快捷键和智能提示消失
- zuc算法代码详解_ZUC算法了解
- 应用wps对证件照进行更改颜色,更换只需三步。
- 计算机英语词汇汇总,计算机英语词汇汇总
- MOX:开创区块链通证参与电影融资的新篇章
- 音乐播放器功能的实现,歌词lrc显示,播放过程中来电
- 原来在SOLIDWORKS中,把马鞍面填充为实体这么简单!
- Unity 交通系统
- 美泰推出首款获CarbonNeutral(R)认证的Matchbox(R) Tesla Roadster压铸模型车,使用99%的回收材料制作,彰显品牌的发展蓝图
- 在线html5视频播放器,分享10款最棒的免费HTML5视频播放器
- 阅读英文原版计算机书籍的一些经验
热门文章
- 凤凰x86无限重启_凤凰系统启动不了,一直在出..................
- lua tcp socket read timed out
- Unity开发日记【第七天】——怪物的移动和动画及类的实现
- 计算机网络应用层报告,计算机网络实验报告应用层
- [网络工程师]-防火墙-防火墙体系
- 408计算机网络学习笔记——应用层
- 命令行终端怎么显示√2̅?这其实是一个博客的Unicode测试文章
- Android 展讯-关闭IP拨号功能
- 解决插入U盘后有提示音,但无法打开、格式化或恢复的问题
- 指数平滑方法(一次指数平滑、二次指数平滑、三次指数平滑):理论、代码、参数 介绍(全)