Java 垃圾回收机制 [ 内存管理 GC]

来源:互联网 发布:安广网络明光营业厅 编辑:程序博客网 时间:2024/05/16 07:30

一.    什么是Java垃圾回收

    

    当新建一个对象的时候,一个变量指向该对象,同时系统会在堆中分配一块空间给该对象;当对象不再被任何变量引用的时候,这块内存就成为了垃圾,等待GC回收。因为在Java中没有像C++中那样的delete语句来完成内存回收。其本意是想代码编写者可以更方便的实现自己的功能,不用考虑内存的问题。但事实并非如此。

 

二.    GC的特征

    1.    GC 只负责回收堆内存中的对象,而不会回收物理资源;

    2.    GC 无法被代码编写者精确控制,之知道当变量不再不被引用时才会被回收;

    3.    GC在回收任何对象之前,总会调用它的finalize 方法,该方法可能是该对象重新激活,从而导致回收失败;

 

三.    GC机制

    在开始了解GC机制之前,我们先来看一下在Java中的内存分配机制。

    每当申请一个Java 线程时,它拥有自己的内存栈(stack).用来存放局部变量和返回值;在栈里的数据名字是已经舍弃了的,其名字由JVM自己生成的;栈是在线程启动时分配的。

    但所有的线程共享一个内存堆(heap),所有运行时的内存分配都在堆上进行.换句话说就是所有的对象都是在堆上创建的。

    其中堆栈的大小可以在Java虚拟机启动时,在JAVA_OPTS进行配置:

    -Xms:初始Heap大小,使用的最小内存,cpu性能高时此值应设的大一些;

    -Xmx:java heap最大值,使用的最大内存;

    (上面两个值是分配JVM的最小和最大内存,取决于硬件物理内存的大小,建议均设为物理内存的一半。)

    -Xss:每个线程的Stack大小;

 

    那我们看看GC到底是负责那方面的内存回收:

    static 变量是在在静态存储区域分配的,内存在程序编译时就分配好了,当程序死亡时,才回去进行释放。

    各种原始数据类型的局部变量,都是在栈上创建的。当程序退出该变量的作用范围时,这些变量的内存会被自动释放。

    对象都是在堆中创建的,程序运行的时候用new 创建对象,对象创建时会在堆中0为其分配内存。

    可以看到在堆中的内存是没有一个明确的释放时间的,其空间由自动的存储管理系统进行控制.也就是Garbage Collector。垃圾收集器通常作为一

个独立的线程运行.

 

    (一)    分代复制垃圾收集器

    

    JVM使用分代复制(generational copying)算法。分代复制算法基于这样一个事实——超过95%的对象的生存期都非常短。分代复制算法根据对象的生存期将对象分为两代。所有新创建的对象都在一个栈结构的内存区域进行分配,这块区域叫做eden。这首先使得内存分配的速度提高了。因为此时只需要更新eden 的指针和检查eden是否溢出即可。当eden 区域已经全部分配给对象时,大部分对象已经”死亡”。垃圾收集器只有将少量未死亡的长期对象(tenured 对象)复制到另一块内存中去,然后直接更新eden 的指针即可。其分代是根据该段内存生命周期的长短来划分的。 

    生存于eden 中的对象称为新生代(young generation)。

    生命较长的对象区域称为旧生代(old generation)。

    其中复制是指,当eden 的内存区域分配完毕,GC 就会对eden 进行一次次要垃圾收集(minor collection),将仍然被引用的对象复制到old generation,并更新指向这些对象的引用,然后将eden 全部清空。

    分代复制的效率非常高,因为清空eden花费的时间极少,主要的时间花费在复制上。因此,适当调整eden 的大小,增长或缩短较小的垃圾收集发生的周期,是的较小的垃圾收集发生时。更多的对象已经死亡,从而减少需要复制的对象的数量,加快程序的速度。当old generation 的内存区域全部分配完时,垃圾收集器会进行一次主要垃圾收集(major collection)。主要垃圾收集通常比次要垃圾收集慢,因为所有”存活”的对象都会被遍历到。可见次要垃圾回收是指将“存活”对象复制到old generation 内存区域中。

    

    其中 young generation = eden + survivor space*2

 

    (二)    标记垃圾收集器

    在旧生代中,通常采用标记垃圾收集器,这种垃圾收集器从一组根引用开始,遍历所有的对象,如果一个对象被根引用,那么就标记为“存活”;而存活对象引用的对象也被标记为“存活”,如此循环递归,其余对象则被标记为“死亡”。死亡的对象将会被回收。根据对存活对象处理方法的不同,标记垃圾收集器又分为两类:

    1.    标记紧缩垃圾收集器(mark-and-compact collector) 将所有存活的对象复制到一个连续的内存区域中,因此可以有效地减少内存碎片。

    2.    标记清除垃圾收集器(mark-and-sweep collector) 保留所有的存活对象,而将所有的死亡对象的内存空降记录到一个自由空间列表中,虽然连续的死亡对象空间会被合并,但与标记紧缩垃圾收集器相比,这种方法仍然会产生较多的内存碎片。

 

    (三)    增量垃圾收集器

    运行标记紧缩垃圾收集器,或者标记清除垃圾收集器,对运行的程序来说,都是一场灾难.因为这些垃圾收集器在运行的时候,会停止JVM 中其他程序的线程。而且这些垃圾收集器每次都会收集尽可能多的垃圾内存。所以每次停顿的时间是不可预测的。分代复制垃圾收集器的情况稍好一点,但也会造成明显的停顿。

    增量垃圾收集器能够提供接近于常数的固定的暂停时间,这个暂停短到用户可以忽略它,因此增量垃圾收集器特别适用于操纵大量数据的应用程序,以及对交互性要求比较高的程序。它的基本原理是时间较长的旧生代的垃圾收集分为许多的间隔来完成,每次只收集一部分内存。对于用户来说好像完全没有停顿一样。但是增量垃圾收集器并不能精确地保证停顿的时间,根据JVM 的实现可以应用与不同要求。

 

    (四)    次要收集和主要收集

   次要收集: 当eden 空间被分配完时,就会发生一次次要垃圾收集(minor collection)。eden中仍然存活的对象会被复制到survivor space1 中,其他对象直接丢弃,其占用的内存被回收。在一次次要垃圾回收之后,JVM 继续在eden中创建对象。当eden的空间再次被分配完的时候,又会发生一次次要垃圾收集。将eden中存活的对象复制到另一个survivor space2 中,并且为survivor中的每一个对象计算年龄age和存活期threshold。age是对象在复制到old generation之前经历过的次要垃圾收集的次数。threshold则是表示在这一次的次要垃圾收集将会被复制到old generation的对象的age。age 小于threshold 的将会被复制survivor space2 中这些对象被称为aged 对象(老化对象)。而清空的survivor space1 为下一次的次要垃圾收集中复制的目的地。

    主要收集:在旧生带中的内存也告罄的时,就会发生一次主要垃圾收集。主要垃圾收集采用标记紧缩的方法,在旧生带中标记出所有存活的对象,然后将其它对象回收.主要垃圾收集通常要比次要垃圾收集的时间长的多,因为标记和回收都是很费时的操作。在典型的配置中,旧生带也比新生带要大(以达到多进行次要收集少进行主要收集的目的)。

 

主要参考:

http://wenku.baidu.com/view/8e94283710661ed9ad51f3b9.html

http://blog.csdn.net/hedpatczw/article/details/7745991

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 苹果7lcould满了怎么办 e招贷不用了怎么办 牙活动了怎么办还疼 30岁掉了一颗牙怎么办? 在淘宝上交话费交错了怎么办 演出队在小区旁边扰民怎么办 雷雨天加了油怎么办 戴ok镜眼睛重影怎么办 乌龟背上长白色的花纹怎么办? 全自动洗衣机里面掉个硬币怎么办 跆拳道课上孩子乱动说话怎么办? 孩子不愿意上跆拳道课了怎么办 车座位里面倒了汤怎么办 腿被棍子打肿了怎么办 刚买的手机碎屏怎么办 被木棍么么打到头项怎么办 大王卡用到40g怎么办 王卡40g用完了怎么办 父亲把母亲打成重伤怎么办 狗狗脖子摔歪了怎么办 吃鸡游戏中重伤怎么办 吃鸡游戏摔伤了怎么办 现在没能力偿还网贷怎么办 上海找夜场工作被骗了怎么办? 买了烂尾的楼盘怎么办 孕七个月蛀牙疼怎么办 宝宝只吃一边奶怎么办 实房付了定金后悔了怎么办 开车时遇到意外事故时怎么办 没干过影楼门市怎么办 uplay下载游戏速度0k怎么办 缓刑期间被别人打成轻伤怎么办 缓刑期间被别人打了怎么办 有缓刑被别人打怎么办 检察院不予立案怎么办公安局取保 监外执行过了又犯罪怎么办 监视居住期间再次犯罪怎么办 前妻有贷款跑了怎么办 未成年犯有缓刑罪继续犯罪怎么办 英朗出现检修esc怎么办 照相时奖状拿反了怎么办