深入理解java虚拟机之垃圾收集器
Java一个重要的优势就是通过垃圾管理器GC (Garbage Collection)自动管理和回收内存,程序员无需通过调用方法来释放内存。也因此很好多的程序员可能会认为Java程序不会出现内存泄漏的问题,这种想法是不对的,当我们对内存使用不当的时候仍然可能会出现内存泄漏,并且问题相对与c++来说更隐秘,问题的根源排查起来也比较困难。不过,当我们了解了Java虚拟机内存区域,Java垃圾收集器之后,对于解决内存泄漏的问题也就不是什么困难的事情了。
相关阅读
在前一篇博客中已经已经详细分析了java内存运行时各个区域,其中程序计数器、虚拟机栈、本地方法栈随着线程而生,随着线程而灭亡,操作数栈中的栈帧随着方法的执行的有条不紊地进行着入栈和出栈的操作,每个栈帧中分配多少内存基本上在类结构确定下来时就已经确定了大小了,因此这几个区域的内存分配和回收都具备确定性,在这个几个区域中就不需要过多的考虑内存分配的问题了,因为随着线程的结束,内存在然而然地就已经被回收了。而java堆和方法区不一样,一个接口中多个实现类所需要的内存可能不一样,一个方法中多个分支需要的内存的大小也有可能是不同的,我们在程序的运行期才能确定哪些对象需要创建,创建所需内存是多大,这些内存都是动态分配的,垃圾回收器考虑的就是这部分的内存,接下来我们将围绕以下3个问题来展开描述java垃圾收集器是如何自动回收内存的:
1、哪些内存需要回收?
2、什么时候回收?
3、如何回收?
本片博客就围绕这第一个问题展开说明,其余连个问题将在后面的博客中一一详解;
首先,Java垃圾回收器的一个想要解决的问题是什么样的内存需要被回收呢?Java垃圾收集器认为,当一个对象再无被其它对象引用时可以认为这个对象可以回收了。那么垃圾收集器是怎么知道对象的死亡的还是存活的呢?目前,Java虚拟机有两种算法来确定哪些对象是无用的,需要被回收的。
1.引用计数法
该算法的思路是给每个对象都添加一个引用计算器,每当有其它对象引用时计数器就+1,当引用失效时计数器-1,任何时刻当该对象的引用数为0的时候,则判定这个对象不会再被使用了,可以将该对象回收了。这种算法实现起来很简单,效率也非常高,但是并没有被Java所采用,原因是这种算法很难解决对象相互引用的问题。看一下下面例子的代码:
public class Test {
private Object instance = null;
private static final int _1MB = 1024 * 1024;
/** 这个成员属性唯一的作用就是占用一点内存 */
private byte[] bigSize = new byte[2 * _1MB];
public static void main(String[] args)
{
Test A= new Test();
Test B = new Test();
A.instance = B;
B.instance = A;
A = null;
B = null;
System.gc();
}
}
运行结果:
[GC (System.gc()) 9299K->720K(249344K), 0.0010947 secs]
[Full GC (System.gc()) 720K->627K(249344K), 0.0075042 secs]
上面的例子中A和B相互引用,它们的引用计数并不为0,但是执行System.gc()方法后仍然被回收了,这说明了Java垃圾收集器并不是使用引用计数法的。
2.可达性分析算法
可达性分析算法就是Java垃圾收集器判断‘垃圾’对象的算法。基本思路是通过一系列的”GC Roots“ 的对象作为起点,从这些节点开始向下搜索,搜索所走过的路程叫做引用链,当一个对象没有任何引用链与”GC Roots“有链接时,那么可以判定这个对象是无用的对象。可以作为GC Roots对象的包括一下几种:
1)Java虚拟机栈中局部变量表引用的对象;
2)本地方法栈中JNI所引用的对象;
3)方法区中的静态变量;
4)方法区中常量引用的对象;
下图为GC Roots:

图中obj1 ~ obj7都能够直接或间接地与GC Roots有连接,因此他们是存活对象,不会被GC回收。obj8基本上可以被回收了,obj9和obj10虽然有相互引用,但是他们的引用链中并没有达到GC Roots,因此也会判定为非存活对象。
3.引用的四种状态
在jdk1.2之前,Java的引用类型是比较简单的,只有被引用和未引用两种状态,类型过于简单的话不利于垃圾收集器的回收,当已经将未被引用的对象都被回收之后内存仍然比较紧张时,垃圾收集器将无法确认被引用的对象是否需要回收,哪些对象可以被回收等。jdk1.2后Java对引用类型进行的拓展,包括:强引用、软引用、弱引用和虚引用四种情况,四者的引用强度依次递减。这样在虚拟机中内存使用不同的情况下,分别回收不容引用类型的对象。
1)强引用
在程序中我们直接new出来的对象的引用都属于强引用,比如:Object obj = new Object();只要强引用还在,就不会被GC回收。
2)软引用
描述部分部分有用但非必须的对象。在系统将要发生内存溢出的时候,虚拟机会尝试从这部分的引用类型中进行二次回收,如果二次回收后的内存仍不够用才会抛出内存溢出异常。Java中的类SoftReference表示软引用。
3)弱引用
描述被必须对象。被弱引用关联的对象只能生存到下一次回收之前,在垃圾收集器工作之后,无论内存是否够用,这类的对象都会被回收掉。Java中的类WeakReference表示弱引用。
4)虚引用
被虚引用关联的对象在被回收时会收到系统的通知。被虚引用关联的对象,和其生存时间完全没关系。Java中的类PhantomReference表示虚引用。

对于可达性分析算法,对于那些没有与GC Roots关联的对象并非是立即回收的,而是经历过两次标记后仍然没有与GC Roots关联上,此时才会回收该对象。对象在进行可达性分析后如果没有与GC Roots关联,则会进行第一次标记和第一次筛选,筛选条件为是否有必要执行finalize方法,比如如果finalize方法是否被覆盖,或是否已被执行一次。如果没有被覆盖或已被执行了,基本可以确认该对象会被回收了。否则,将这个对象放到F-Queue队列中。对F-Queue队列二次标记,如果在这次标记中对象成功关联上GC Roots,则该对象拯救了自己,将从”即将回收“的集合中移除,否则将会被回收。然而,对象只能拯救自己一次,第二次就会被回收了。
迎大家关注公众号: 【java解忧杂货铺】,里面会不定时发布一些技术干货博客;关注即可免费领取大量最新,最流行的技术教学视频:


深入理解java虚拟机之垃圾收集器的更多相关文章
- 深入理解Java虚拟机:垃圾收集器与内存分配策略
目录 3.2 对象已死吗 判断一个对象是否可被回收 引用类型 finalize() 回收方法区 3.3. 垃圾收集算法 1.Mark-Sweep(标记-清除)算法 2.Copying(复制)算法 3. ...
- 《深入理解Java虚拟机》垃圾收集器
说起垃圾收集(Garbage Collection,GC),大部分人都把这项技术当做Java语言的伴生产物.事实上,GC的历史远比Java久远,1960年诞生于MIT的Lisp是第一门真正使用内存动态 ...
- 《深入理解Java虚拟机》——垃圾收集器与内存分配策略
GC需要完成: 哪些内存需要回收 什么时候回收 如何回收 如何确定对象不再使用 引用计数算法 给对象添加一个引用计数器,当有一个地方引用它时,计数器值进行加1操作:当引用失效时,计数器值进行减1操作: ...
- 深入理解Java虚拟机笔记——垃圾收集器与内存分配策略
目录 判断对象是否死亡 引用计数器算法 可达性分析算法 各种引用 回收方法区 垃圾收集算法 标记-清除算法 复制算法 标记-整理算法 分代收集算法 HotSpot算法实现 枚举根节点 GC停顿(Sto ...
- 深入理解java虚拟机(3)垃圾收集器与内存分配策略
一.根搜索算法: (1)定义:通过一系列名为"GC Roots"的对象作为起点,从这些起点开始向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连的时 ...
- 深入理解JVM虚拟机-2垃圾收集器
这里讨论的收集器基于JDK 1.7 Update 14之后的HotSpot虚拟机. 如果两个收集器之间存在连线,说明可以搭配使用.虚拟机所处的区域,则表示它是属于新生代收集器还是年老代收集器.在这里我 ...
- Java虚拟机学习 - 垃圾收集器
HotSpot JVM收集器 上面有7中收集器,分为两块,上面为新生代收集器,下面是老年代收集器.如果两个收集器之间存在连线,就说明它们可以搭配使用. Serial(串行GC)收集器 Serial收集 ...
- Java虚拟机学习 - 垃圾收集器 (4)
HotSpot JVM收集器 上面有7中收集器,分为两块,上面为新生代收集器,下面是老年代收集器.如果两个收集器之间存在连线,就说明它们可以搭配使用. Serial(串行GC)收集器 Serial收集 ...
- java虚拟机(六)--垃圾收集器和内存分配策略
目前没有完美的收集器,不同的厂商.版本的虚拟机提供的垃圾收集器会有很大的差别,用户根据自己应用特点和要求组合出各个年代所使用 的收集器.基于jdk1.7Update14之后的虚拟机. HotSpot的 ...
随机推荐
- 关于期权池Option Pools与Vesting:码农创业防身必备法器
之前又看到饿了么创始人团队纠纷的几篇文章,参考了百科.wiki.36Kr.虎嗅.知乎以及邵亦波老师的文章,对之前一直感兴趣的期权汇编初略总结了下 ,仍觉粗糙,对一些具体操作还是不甚了了,不过感觉在中国 ...
- Niop2017初赛滚粗记
初赛踢蹬滚粗 TOT (╯°Д°)╯︵┻━┻ ヽ(`Д´)ノ︵ ┻━┻ ┻━┻ 排序啊排序,净是排序,自打我学了C++就再没学过排序!!wtf! (╯°Д°)╯︵ /(.□ . )我tm怎么知道建国那 ...
- HTML学习笔记3:文字和段落
①标题标签 <h1></h1> ~ <h6></h6>分别对应字体不同的大小,数字又小到大对应字体由大到小 ②段落 <p> ...
- Android 开发知识体系
知识体系 1.Unix/Linux平台技术:基本命令,Linux下的开发环境 2.企业级数据库技术:SQL语言.SQL语句调优.Oracle数据库技术 3.Java 语言核心技术:Java语言基础.J ...
- 【ASP.NET MVC系列】浅谈ASP.NET MVC八大类扩展(上篇)
lASP.NET MVC系列文章 [01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操 ...
- 高通spi 屏幕 -lk代码分析
lk SPI驱动 1. 初始化时钟 在lk中,我们是从kmain开始执行下来的,而执行顺序则是先初始化时钟,也就是在platform_early_init函数中开始执行的: 在这里我们需要修改这个函数 ...
- AbstractQueuedSynchronizer原理及代码分析
一.AQS简介 AbstractQueuedSynchronizer(AQS)是java.util.concurrent并发包下最基本的同步器,其它同步器实现,如ReentrantLock类,Reen ...
- PAT1134:Vertex Cover
1134. Vertex Cover (25) 时间限制 600 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue A vertex ...
- mysqldump+系统计划任务定时备份MySql数据
MYSQL 数据库备份有很多种(cp.tar.lvm2.mysqldump.xtarbackup)等等,具体使用哪一个还要看你的数据规模.下面给出一个表 #摘自<学会用各种姿态备份Mysql数据 ...
- 多个DbContext修改同一张表测试
多个DbContext修改同一张表经测试是可行的. UserStore和DepartmentStore都可以向SysLog表写入数据 用多个线程同时通过UserStore和DepartmentStor ...