最近学习了JVM的相关知识,主要是关于HosSpot的,这里大致整理一下。

上次总结了一下JVM的结构和大致算法(地址:http://xlows.blog.51cto.com/5380484/1541823 )

这次接着总结


(1)根元素

wKiom1Py-NDikItXAAFqSZgdkhE262.jpg

由上图可以发现,根元素包括很多:

1.类:被系统加载器加载的类,这些类存放在永久代,不能被垃圾回收器回收,至少jdk6和jdk7回收,但可以被类卸载器卸载;

2.线程:正在执行的线程对象;

3.局部变量中的栈当中的对象;

4.与java本地调用相关的局部和全局对象;

5.管程对象

6.java虚拟机进行垃圾回收阶段时垃圾回收器对象


(2)标记过程

标记过程采用的算法为DFS三色标记算法。

这里大致介绍一下三色标记法:如果节点当中有3个颜色,白色,灰色和黑色,白色代表这个节点从来没有被处理过,灰色代表正在被处理,黑色代表已经被处理。对应标记就是白色代表这个节点还没被标记,灰色代表正在被标记还没标记完,黑色代表已经标记完而且确确实实不能被回收。

下面举个例子:

wKioL1Py-wjgs6uzAAC8u1_uxE4796.jpg

图代表对象之间的引用关系,假设把1当做根对象,首先把1标记成灰色。

wKiom1Py-l2izy9wAADKoLmPNSE025.jpg

1引用的对象是4,下一步就是把4标记成灰色。

wKiom1Py-qHRrrvbAADM7Tt_NqQ998.jpg

现在有一个问题,4和1,2,5相连,那怎么选择呢?到底选择1,2,还是5呢?很简单,1已经被标记,所以不会选择,而4会在2和5之间随便选一个,2也可以,5也可以。那么现在假设4选择了2,把2标记成灰色。

wKiom1Py-wHw6pMWAADL_o-9Dbs576.jpg

2和4,5相关联,但4已经被标记,所以只能选择5。

wKioL1Py_H2zJaUTAADUy3VrPHo799.jpg

同理5会选择3。

wKiom1Py-46iZJZ3AADVpkz3GDY647.jpg

此时3没有与任何节点相连,所以下一步会把3标记成黑色代表3已经处理完成。

wKiom1Py_DfTgxg6AAD8hOsGpUI595.jpg

而后退出到5,此时可以发现5和3,2,4有关联,代表可以通过5来标记3,也可以通过5来标记2或者4。但现在5和3这条线路已经被处理,5和2正在被处理(红线代表正在被处理),但5和4还有一个标记,此时5会发现4也正在被处理,所以会退回来把,此时才会把5标记成黑色。

wKioL1Py_gGgFbQbAAD8zqTqoII011.jpg

下一步就是处理2。

wKioL1Py_vfAPikyAAD6zYuattQ219.jpg

2和5、4相连,5已经被粗粒,他只能处理4,在处理4之前会把2标记成黑色,2在标记4,4可以和2,5,1关联,2和5已经被处理,此时4会去探测一下,然后把4和5这条路径给断掉,然后把4标记成黑色。

wKioL1Py_1PQhE-tAADzwqVbadU414.jpg

最后在把1标记成黑色。

wKioL1Py_4nDIk6sAAD5T2vlZOg442.jpg

通过这种三色标记法,就可以通过根对象把与根对象相关联的对象都找到处理。


(3)Shallow &&Retained Size

wKiom1Py_x7xKIFjAAExjrQyPRE057.jpg

在内存中申请一个结构体,通过SizeOf算出来的大小就是Shallow Size。

但是如果这个结构体有个指针,指向别的结构体,那么通过这个结构体可以关联到其他对象,这些对象可以是很大的,那么这些结构体大小加起来的大小就是Retained Size。Retained Size代表垃圾回收器实际回收的大小。


wKiom1Py_-Pj_MgZAADPj0TCSNQ755.jpg

如上图,R01的Retained Size就是S01+S02+S03,因为04被根引用,所以不输入01,依次类推,R02=S02+S03,R03=S03,R04=S04。

wKioL1PzAc2S2FASAADLOz2YOEI728.jpg

在看这幅图,因为04没有被根引用到,所以R01=S01+S02+S03+S04,R02=S02+S03+S04,R03=S03,R04=S04。


(4)实现

wKiom1PzAWOTAf7TAAD2BG6Zx5s915.jpg

在介绍实现之前先要介绍几个概念。

1.安全点:回收的时候必须有一个点,安全点。比如说一块内存,正在申请,这个申请的过程应该保证是原子操作,不能在申请一半的时候GC把这个申请内存的动作打断;或者有一个for循环,在循环的时候状态可能在不断改变,必须保证在循环过后才能回收。这些可能造成java虚拟机状态改变的点叫做不安全点。那些可以让java虚拟机状态不在改变的点就叫做安全点。

线程达到安全点的方式有两种,第一种是抢占式,第二种是主动式。

什么叫抢占式呢?现在一个线程或者一个组件申请内存申请不到的时候,主线程去主动的操作子线程,让它跑到安全点,或者强制让它停止下来,也就是强迫你停下来进行垃圾回收。

第二种就是在垃圾回收的时候做一个标记,通过信号量的模式,它或者让一个变量设置成标记量,设置成true或者false,设置为true代表我要进行垃圾回收了,子线程在跑的过程中每隔一个阶段会检查这个标记位,如果这个标记位为真的时候,那么子线程跑到安全点的时候,主线程去执行垃圾回收,这就是主动式。

而垃圾回收的方式有3种,串行并行并发,具体将在下一篇博文中详细介绍。