jvm& memory (4)gc

本文转自:  http://spaces.msn.com/songsun/

 

不同的JVM实现对堆结构的设计有所不同,这里先说说共性的,然后再比较classic  vm和hotspot  vm在gc方面的差异。

先 大致说说gc的过程,通常有两种情形会导致gc发生,一种是显式的System.gc()调用而java进程未禁止显示gc,第二种是隐式的,即内存管理 器(MM)在alloc内存时发生failure,MM进而作gc以便释放出空间用于分配(当然,假如gc后还是没有空间可满足分配数值,  OutOfMemory就发生了)。gc过程分3步走,第1步,mark阶段,标示出allocbits和markbits,allocbits  代表当前java  heap中存在的对象所占内存的bit映射,markbits代表所有reachable对象所占内存的bit映射,是allocbits的子集。这一 步,一般是要锁定java  heap的,但有些gc器能做到并发/并行mark(后面有解释)。第2步,sweep阶段,即将markbits和allocbits的补集所代表的 内存区域作回收,这也包括对class的回收,假如classgc未被禁用,而jvm确实找到了未被使用的class,那么除了java  heap中的Class对象被回收,method  area里该class的method  data也被释放。第3步,compact阶段(可选的,jvm会自行判断是否进行该步),整理碎片,两种技巧,一种是移动琐碎的对象区域,使之连续, 一种是移动琐碎的free区域,也使之连续。这步也必须要锁定java  heap。compact时还要做一个工作,即将所移动的对象的原始引用值作同步更新。这时存在一个问题,假如某thread  stack里的一个浮点数碰巧看起来像是一个对象引用值(内存泄露也会因此情形而发生),那么jvm因为无法断定而放弃移动该对象,等待以后再移动(据 称,hotspot  vm对精确判定引用值的问题解决的较好)。

再解释一下mark阶段,为了得到markbits,首先要收集 root  references,也就是java  thread  stacks  refs,  jni  global  refs,thread  moniters,interned  strings,and  soft/weak  refs等,它们是对象引用的源头,从root  referent  object顺藤摸瓜,即可把所有reachable  object标示出来。再解释一下bits映射,每一个bit代表每一个内存GRAIN(是分配的最小粒度),假设GRAIN是8  byte,那么对于64M的堆,需要额外的1M的bits来映射,这部分额外开支需要gc器向c  heap申请,当堆扩展和收缩时,markbits也要随之变动。mark阶段工作量比较大,因为要扫描和遍历,所以后期的jvm都采用多种新的方式如  concurrent(普通线程也参与mark)或parallel(多cpu有效)进行mark。

再说堆的扩张 (Expansion)和收缩(Shrinkage)。当内存不足时,gc清理一遍后还发现free空间不能满足分配的尺寸要求,那么它有两种手段继续推 进,如果最大堆(-Xmx)未达到,那么扩展堆,如果已达到,那么强制回收soft/weak  referents,如果此时还不能满足,那么OutOfMemory就发生了。而当经过一段运行时间后,jvm判断free区域较多(不同的jvm判 定依据有所不同),那么就会收缩java  heap。收缩的好处有两个,1是减轻OS的负担,2又减少了jvm内存管理器的维护成本。注意,不论再怎么收缩,也不可能小于最小堆(-Xms)。默 认的ms值和mx值各vm提供商有所不同,请参阅其文档。

个人认为主流的jvm主要有3种,sun  classic,  ibm  classsic,sun  hotspot。sun  classsic用于1.3.0以前,之后sun的jvm都是hotspot。而ibm的jdk一直是classsic,不知其5.0的vm是否会采用  hotspot。sun的是主流无可置疑啦,为什么扯上ibm的呢,因为看过一个民间的性能评测结果,ibm  1.42vm比sun的1.42vm甚至略好。sun  classic使用间接句柄分配对象,所以堆分为两部分,一块放object,一块放handle。这个好处是compact方便得很,缺点也是很严重 的缺点,是对象访问不直接,要多一层指针转换,对性能大有影响。ibm  classic的具体细节的资料比较缺乏,但根据其white  paper,似乎使用的是直接句柄(否则性能无法比sun的好),另外它将java  heap划分成system  heap和一般heap,仅将jvm的系统class和对象分配到system  heap里,它还支持并行和并发的gc。sun  hotspot使用直接句柄,似乎又采用了一些高级手段,做到了精确辨别,因此快而且回收准确,另外提供了多种的gc器,适用于不同场合。值得一提的是 它的代生gc器(Generational  Copying  Collection),根据对象生命周期模型(小对象居多,生命周期短),使用类似stack的方式,将同一时期产生的对象放在同一容器  (nursery)中,这批对象基本上会同时消亡,这样,回收时可直接将容器释放,避免了繁琐的逐个对象释放的过程,因此可以减少gc的花费时间 (duration)和频度(frequency),并且容器是现成的,所以分配时,寻找free空间很方便,所以提高了分配的效率。

本篇再加上前面的几篇,已经把JVM和Memory相关的主要机理和过程都已经覆盖了,下一篇主要是关于tuning和trouble  shooting,请关注。

补: 再说一下内存泄露(memory  leak),与c/c++的内存泄露的概念不同,java的memory  leak是指无用(unused)的对象因为仍然是reachable的,所以不能被gc,因而造成内存的被白白占用。造成memory  leak的原因是多样的,比如程序未及时给变量赋null,再比如上文提到的浮点数被碰巧当成对象引用,或者jni程序里声明的global  ref忘了做撤销。对于服务器程序来说,memory  leak的逐渐积累,只要运行时间足够,必然造成OutOfMemory。如何确定jvm是否有内存泄露呢,假使你没有监测工具也不要紧,打开-  verbose:gc开关,运行系统足够长的时间(约收集20到50次gc的数据),然后将verbosegc的信息作收集,察看java  heap的谷值(gc后java  heap的大小,峰值则表示gc前java  heap的大小)是否有递增的倾向,如果倾向明显,那么很有可能是memory  leak问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值