分类 JVM 下的文章

JVM垃圾回收算法

Mark-Sweep 标记清除算法

标记清除算法是最基础的回收算法,回收过程包括“标记”和“清除”两个阶段,过程大概是这样的:

mark_sweep

1:标记阶段时,标识出可回收的引用。

2:清除阶段时,对标记阶段所标识为的对像进行回收。

这个算法存在的最大问题是,通过该算法回收的内存区域是不连续的,会产生大量的无法被利用的内存碎片,而如果找不到连续足够多的空间,就需要触发另一个GC来进去回收。

另外,sweep阶段,当所管理的内存区域很大时(无论是否全部使用),效率也会受到影响。

但是,GC算法只是一些理论算法,而GC回收器则是基于这些算法的具体实现,通过对回收器机制的扩展和加入一些补偿措施,可以规避算法自身的缺陷。例如标记清除算法,当前最稳定高效的tenure代的回收器 ,CMS回收器,便是基于这种算法。CMS通过利用多核/线程,将并发标记阶段、并发清除阶段变为并行,通过通过一些额外的补偿方案实现内存碎片的整理,使其成为很优秀的垃圾回收器。

后续会单独用一篇博客来描述具体垃圾回收器,本文还是继续描述回收算法。需记住加收算法本身缺陷是可以通过具体实现来规避的就行了,并不是基于某种回收算法实现的回收器,就一定有该种算法存在的缺陷,如CMS。


Copying 复制算法

复制算法的出现,是为了解决标记清除所存存的问题。复制算法将容量划分两块区域,其过程如下:

gc_copying
阅读全文

Java 8与Linux OOM killer

由于想体验新版的Lambda和Stream API,把服务器上的JRE换成了Java 8.而之后网站偶尔出现无法访问,java进程莫名奇妙地退出。怀疑是虚拟机内存溢出,但tomcat与应用的日志都没有崩溃的相关日志输出。

经过几次在线监控,发现java进程占用超过系统50%的内存。首先,网站应该占用不了这么多的内存,虽然我没有用-Xmx指定heap内存大小,但按规范,默认应该分配是物理内存的1/4。其它的Permgen区域,也应该无法使用这么多的内存。

这点我先放了放。猜测linux会不会自动杀死占用内存过多的进程? (没有太多的linux维护经验,也没有系统的学习过linux)。 结果在linux syslog中发现了这段。

#oom killer
kernel: java invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0

按关键字google一下,查到了linux的oom-killer机制:linux会在内存紧张时,杀死占用太多内存的进程,以保证系统正常运行。

查到这个原因后便开始分析内存使用的情况。
由于是在linux上,惯手的内存工具缺失,jmap运行很久也拿不到内存dump(吐个嘲先,我用jmap导出内存dump,一个多小时都没有导出来,而且期间jvm会处于假死状态,网站无法访问)。 就打算用jstat来看下各个段的大致使用情况(主要使用jstat -gcutil/jstat -gccapacity/jmap -heap)。

发现Permgen段使用量为97%,而且已经使用了120多M了 (其实当时jstat中的标记是M:指代Java8中的Metaspace,而Permgen标记应该为P,这犯了个傻,误认为是PermGen了)
阅读全文