Java 垃圾收集与内存分配

《深入理解Java虚拟机》 垃圾收集器与内存分配策略 阅读笔记


垃圾收集起源于Lisp语言,了解垃圾回收机制对于排查内存相关问题大有裨益。首先需要了解,垃圾回收需要解决三个问题:哪些内存需要回收?什么时候回收?如何回收?下面就这三个问题做一些记录和整理。

1、哪些内存需要回收?

上一篇笔记记录了Java内存区域的划分 http://blog.csdn.net/lisha20082008/article/details/38454821

可以了解到,Java垃圾收集主要集中在线程共享的Java堆区,方法区(也叫永久代)存在少量的垃圾回收。其中堆区可以细分为新生代和老年代,新生代又可以细分为一块Eden和两块Survivor,Sun的Hotspot虚拟机Eden和Survivor默认比例8:1。方法区需要回收废弃的常量和无用的类(JSP、OSGI这类频繁自定义ClassLoader的都需要虚拟机具备类卸载功能)。



2、什么时候回收?

当为对象分配空间发现不够的时候进行回收,因为垃圾回收也是需要消耗资源的,垃圾收集器会暂停当前进行的操作,进行垃圾收集工作,叫做“Stop the World”。

对象的分配策略取决于垃圾收集器的组合以及参数,一般来说,对象优先分配在新生代的Eden区,当Eden区没有足够空间分配的时候,虚拟机将发动一次Minor GC, 大对象(占用连续空间的对象)直接分配在老年代,长期存活的对象也会晋升到老年代。

新生代一般使用复制收集算法,对于Sun Hotspot虚拟机来说,会将一个Eden和一个Survior区作为分配的内存,当分配不够的时候,将两个区存活的对象拷贝到另一个Survivor区里。但是这里可能会出现一个问题,存活的对象太多,一个Survivor区装不下,这里只能依靠老年代进行分配担保(Handle Promotion),发生Minor GC时,如果检测到晋升到老年代的对象平均大小大于老年代剩余空间的大小,那么老年代存放变老的对象都不够了,更没法担保了,于是会发生Full GC(发生在老年代的GC,比新生代慢10倍以上),如果检测到用户设置的参数允许担保失败,那么也只发生Minor GC。


3、如何回收

如何回收涉及到两个问题,也是垃圾回收的关键。(1)怎么判断对象死了 (2)垃圾收集器里面的具体收集算法

(1)判断对象是否存活

引用计数法:每个对象都维护一个计数器,如果计数器为0,那么则表示可以回收了。显著的缺点是无法解决循环引用的问题。

根搜索算法(GC Roots Tracing): 维护一系列GC Roots,如果对象到GC Roots不可达,那么表示对象已死。其中GC Roots 包括 虚拟机栈 栈帧中的本地变量里的对象,本地方法栈JNI引用的对象,方法去中类静态属性对象、常量。

这里有一个两次标记的过程,如果发现一个对象死掉了,并不是立即回收的。第一次标记并把没有必要执行finalize方法的对象筛选出来杀死,有必要执行的放到F-Queue队列当中(一个低优先级的队列),GC对F-Queue中的对象进行小规模的二次标记,如果对象在finalize方法中把自己和其他存活的对象关联起来,就不会被回收。


(2)怎样进行垃圾回收

复制算法:分成两块,一块用完了之后,把活下来的拷贝到另一块。Sun Hotspot的分配方式是一大两小,提高利用率。复制的优点是不会出现内存碎片。用于新生代



标记-清除算法:标记出死了的对象,清除掉即可。缺点是效率低,并且会产生碎片,下次分配大对象的时候就不够用了,提前触发垃圾回收。用于老年代。



标记-整理算法:存活的对象向一端移动,避免清除后的碎片,用于老年代。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值