jvm垃圾回收

判断对象死亡算法

1
2
3
4
5
6
7
8
9
10
11
12
13
1,引用计数
每引用一次计数器+1
2,可达行分析算法
gc roots-->连接的对象

gc root对象
1,虚拟机栈(栈帧中本地变量表)中引用的对象,比如各个线程被调用的方法堆栈中使用到的参数,局部变量,临时变量等
2,方法区中类静态属性引用的对象,如java类的引用类型静态变量
3,在方法区中常量引用的对象,譬如字符串常量池离店引用
4,在本地方法栈中JNI(通常所说的Native方法)引用的对象
5,java虚拟机内部的引用,如基本数据类型对象的Class对象,一些常驻的异常对象(npe,outofmemoryerror)等,还有系统类加载器
6,所有被同步锁(synchronized)持有的对象
7,反映java虚拟机内部情况的jmxbean,jvmti中注册的回调,本地代码缓存等.

引用

1
2
3
4
5
6
7
8
强引用(strongly reference)
存在就不会回收
软引用(soft)
内存溢出前,会添加到第二次回收中
弱引用(weak)
生存到下次回收
虚引用(phantom)
对象被回收时收到一个系统通知

生存还是死亡

1
2
3
4
5
6
7
二次标记

对象和gc root不相连接,进行第一次标记,随后进行进行一次筛选
如果finalize()没有或者已经执行完毕则没有必要在执行.
若对象有必要执行finalize()方法,则将对象添加到F-Queue队列中,
并不一定保证执行完毕
因为太慢或者死循环可能导致后边的等待甚至崩溃

回收方法区

1
2
3
4
5
6
7
方法区垃圾回收主要回收两部分:废弃的常量和不再使用的类型.
废弃常量:
垃圾回收时,没有使用则可进行清理,其他类(接口),方法,字段的符合引用也和这个相似
不使用的类型:
类所有实例被回收,该类和子类的实例
类加载器已经被回收
java.lang.Class对象没有被引用

垃圾收集算法

1
2
3
从判断对象消亡的角度
引用计数式垃圾收集
追踪式垃圾收集

分代收集理论

1
年轻代    老年代

收集定义

1
2
3
4
5
部分收集(partial gc):指目标不是完整收集整个java堆叠垃圾收集,又分为
新生代收集(minor gc/young gc):指目标只是新生代的垃圾收集.
老年带收集(major gc/old gc):指目标只是老年代的垃圾收集.目前只有cms收集器会有单独的收集老年带的行为.
混合收集(mixed gc):指目标是手机整个新生代以及部分老年代的垃圾收集.目前只有g1收集器有这种行为.
整堆收集(full gc):收集整个java堆和方法区的垃圾收集.

标记清除

标记复制

标记整理

hotspot算法细节实现

1
2
3
4
5
6
7
8
9
10
11
12
根节点枚举
安全点:hotspot没有为每条指令生成oopmap,只是在"特定的位置"记录了这些信息,这些位置被称为安全点(safepoint)
选定:不能太少以致让收集器等待时间过程,也不能太过频繁以至于过分增大运行时的内存.选取标准是:是否具有让程序长时间执行的特征为标准进行选定的.
如何跑到最近的安全点然后停顿下来-->有2种可供选择:抢先式中断和主动式中断
抢先式中断:不需要线程的执行代码主动去配合,在垃圾收集发生时,系统首先把所有用户线程全部中断,如果发现用户线程中断的地方不在安全点,就恢复这条线程执行,让它一会再重新中断,直到跑到安全点.现在几乎没有虚拟机实现才用抢占式中断来暂停线程响应gc事件.
主动式中断:当垃圾收集需要中断线程的时候,不直接对线程操作,仅仅简单设置一个标志位,各个线程执行过程时会不停的主动轮询这个标志,一旦发现中断标志为真时就自己在最近的安全点上主动中断挂起.
轮询标志的地方和安全点是重合的,另外还要加上所有创建对象和其他需要在java堆上分配内存的地方,这事为了检查是否即将要发生垃圾收集,避免没有足够内存分配新对象.
程序执行时才能进入到安全点.
安全区域:
程序不执行就是没有分配处理器时间,典型的场景便是用户线程处于sleep或者blocked状态,这时候线程无法响应虚拟机的中断请求,不能再走到安全点地方去中断挂起自己,虚拟机也显然不可能持续等待线程重新被激活分配处理器时间,对于这种情况,就必须引入安全区域(safe region)来解决.
定义:能够确保在某一段代码片段之中,引用关系不回发生变化,因此,在这个区域中任意地方开始垃圾收集都是安全的.我们也可以把安全区域看作被扩展拉伸的安全点.
过程:当用户线程处于执行到安全区域里边的代码时,首先会标志自己已经进入了安全区域,那样当这段时间里虚拟机要发起垃圾回收收集就不必去管这些已经声明在安全区域内的线程了.当线程要离开安全区域内时,他要检查虚拟机是否已经完成根枚举(或者垃圾收集过程中其他需要暂停用户线程的阶段),如果完成了,那线程就当作没事发生过,继续执行;否则它就必须一直等待,知道收到可以离开安全区域的信号位置.

经典垃圾收集器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
serial:
年轻代:复制,单线程,stw
parnew:
年轻代:复制,多线程,stw
parallel scavenge:
年轻代:复制,多线程,stw
吞吐量收集器
-XX:MaxGCPauseMillis最大停顿时间
-XX:GCTimeRatio gc时间占比 比如99 1/1+99
serial old:
老年代:标记整理,单线程,stw
parallel old:
老年代:标记整理,多线程,stw
cms:
初始标记:stw
并发标记:多线程
重新标记:stw
并发清除:多线程

缺点:
1,cms对处理器资源非常敏感.事实上,面向并发设计的程序都对处理器资源比较敏感
默认回收线程数(核心数+3)/4,也就是说,核心数在4和以上,并发回收时垃圾收集线程只占用不超过25%
核心数在4一下,cms对用户程序的影响就可能比较大.
2,由于有浮动垃圾,有可能出现"Concurrent Mode Failure"失败进而导致领一次完全"Stop The World"的Full GC的产生.
并发标记 (?是否当次)
并发清除 产生的垃圾无法档次进行清除
需要预留一定的空间,-XX:CMSInitiatingOccu-pancyFraction配置触发百分比(jdk5=62,jdk6=92)
3,标记清除,会产生大量碎片,-XXCMSFullGCsBefere-Compaction几次后进行fullgc会进行碎片整理
g1:
将java堆分为多个大小相等的独立区域(region),每个region都会更具需要,扮演新生代的eden,survicor,old空间.收集器更具不同角色的region采用不同的策略去处理,这样无论是新创建的对象还是已经存活了一段时间,熬过几次收集到旧对象都获取很好的手机效果.
region还有一类特殊的Humongous区域,专门用来存储大对象.超过容量一半的对象即可判断为大对象.-XX:G1HeapRegionSize=1MB---32MB,为2的指数.
-XX:MaxGCPauseMillis指定停顿时间.
跨region
过程:
初始标记:stw,耗时很短
仅仅标记gc roots能直接关联的对象,并且修改TAMS指针的值,让下一阶段用户线程并发运行时,能正确在可用的region中分配新对象,耗时很短,而且借用进行minor gc的时候同步完成,所以实际并没有额外的停顿.
并发标记:这个阶段较长
从gc root开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这个阶段较长,但可与用户程序并发执行.当对象图扫描完成以后,还要重新处理SATB记录下的并发是有引用变动的对象.
最终标记:stw
用户处理并发阶段结束后仍遗留下来的最后那韶关亮的SATB记录.
筛选回收:
负责更新region的统计数据,对哥哥region的回收价值和成本进行排序,根据用户所期望的停顿时间来指定回收计划,可以自由选择任意多个region构成收集,然后把决定回收的那一部分region的存活对象复制到空的region中,在清理掉整个旧region的全部空间.
回收阶段本来也设计过和用户程序一起并发执行,但是很复杂,考虑只是回收一部分停顿时间段,放到zgc中完成.

对象分配和回收策略

对象有现在eden分配

大对象直接进入老年代

1
-XX:PretenureSizeThreshold=x(只对serial,parnew有效)  大于该值这直接进入老年代

长期存在的对象进入老年代

1
-XX:MaxTenuringThreshold=x配置年龄

动态对象年龄判断

1
达到配置年龄才能进生老年代,如果在survivor空间中相同年龄所有对象大小的总和大于survivor空间的一半,年龄大于或等于该年龄对象就可以直接进入老年代.

空间分配担保

1
2
3
4
5
发生minor gc之前,虚拟机必须先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,
成立,那麽这一次minor gc是确保安全的.
不成立,则-XX:HandlePromotionFailure是否允许
允许:检查老年代最大可用的连续空间是否大于历次晋升到老年代的对象的平均大小,若大于,则尝试进行一次minor gc,失败则full gc
若小于,则改为进行一次full gc

full gc触发

1
2
3
4
5
6
7
8
9
10
11
12
13
https://www.cnblogs.com/jiangxwa/p/10289474.html

1,system.gc()调用
-XX:+ DisableExplicitGC来禁止
2,老年带空间不足
3,元数据区不足
4,cms gc出现 promotion failed和concurrent mode failure
promotion failed:minor gc时,survivor放不下,放老年带,老年带也放不下.
concurrent mode failure:gc过程中,有新的对象生成,但是还有对象放入老年带,老年带空间不足造成的
处理方式
增大survivor,老年带或调低触发gc的占比
5,担保失败
6,堆中分配很大的对象
1
2
3
4
cpu 异常
jps
top -H -p2022
jstack pid
1
2
3
4
5
6
7
内存异常

mat 查看内存结构,查看结构数可以查看到位置

常见出现问题
静态对象
threadlocal