关于JVM垃圾回收机制

堆内存空间

  • 新生代(Young Generation):Eden区、两个Survivor(Survivor0、Survivor1)区:
    1、新生代内存划分默认为8:1:1,新生代大小可由-Xmn来控制,Eden和Survivor比例可以用-XX:SurvivorRatio来控制;
    2、大部分对象在Eden区生成。当新对象生成时,Eden空间申请失败(空间不足),则会发起一次GC(Scavenge GC)。回收时先将Eden区存活的对象复制到一个Survivor0区,然后清空Eden区。当某次回收时Survivor0区存放满时,则将Eden区和Survivor0区存活的对象存放到Survivor1区,然后清空Eden和Survivor0区,清空后将Survivor0区和Survivor1区交换,即保持Survivor1区为空(有争议),如此反复。当Survivor1区不足以存放Eden和Survivor0区的存活对象时,就将存活的对象直接存到老年代。另外,当对象在Survivor区躲过一次GC的话,其年龄就会加1,默认情况下,如果对象的年龄达到15岁,就会直接移动到老年代。若是老年代也满了就会触发一次Full GC,也就是新生代、老年代都进行回收。
  • 老年代(Old Generation):
    在新生代中经历了N次垃圾回收后仍然存活的对象,就会被放到老年代中。因此,可以人为老年代中存放的都是一些生命周期较长的对象,内存也比新生代大很多(默认时1:2)。老年代满时触发Major即Full GC,一般来说,大对象会被直接分配到老年代,所谓大对象是指需要大量连续存储空间的对象,最常见的就是大数组。
  • 永久代(Permanent Generation):
    用于存放静态文件(class类、方法)和常量等,永久代对垃圾回收没有显著影响,在JavaSE8特性中已经被移除了,取而代之的是元空间(MetSpace)。

什么样的对象会被当作垃圾回收

  • 当一个对象的引用(地址)没有变量去记录的时候,该对象就会成为垃圾对象,并在垃圾回收器空闲的时候对其进行清扫

如何校验对象是否被回收

  • 可以重写Object类中的finalize方法,该方法在垃圾回收器执行的时候,被回收器自动调用执行

怎样通知垃圾回收

  • 可以调用System类的静态方法gc(),通知垃圾回收器去清理垃圾。(不建议使用,因为JVM有垃圾回收机制自动清理垃圾

代码实现

public class GarbageCollection {
    public static void main(String[] args) {
        /**
         * 问题一:什么样的对象是垃圾对象
         *      没有被变量记录的对象
         */
        new Demo(); //垃圾对象
        Demo d = new Demo(); //不是垃圾对象,有被变量记录
        d = null; //这样就变成垃圾对象了

        /**
         * 问题二:怎么确认垃圾对象被回收了
         *      Object类中有一个finalize方法,在垃圾处理器工作的时候会被调用,
         *      可以重写该方法,当回收垃圾时执行
         */
        // 创建一些垃圾对象看是否被清理了没,注意当垃圾量过少时 垃圾处理器会懒得处理
        for (int i = 0; i < 10000000; i++) {
            new Demo();
        }

        /**
         * 问题三:怎样通知垃圾回收器主动清理垃圾对象
         *      System类中有静态gc()方法,通知垃圾回收器来干活
         */
        for (int i = 0; i < 100; i++) {
            new Demo();
            System.gc(); //通知垃圾回收器干活
        }
    }
}

class Demo{

    @Override
    protected void finalize() throws Throwable {
        System.out.println("我是垃圾对象  被清理了!!(~﹃~)~zZ");
    }
}

问题扩展

  • 怎么判断某个对象是垃圾,即没有变量记录
    1、引用计数算法(java中不是使用此方法)
    每个对象中添加一个引用计数器,当有别人引用它的时候,计数器就会加1,当变量不引用它时计数器就会减1,当计数器为0的时候对象就可以当成垃圾。算法简单,但是最大问题就是在循环引用的时候不能够正确把对象当成垃圾。示例:
public class GarbageCollection_Math {
    public static void main(String[] args) {
        Demo_Gc d1 = new Demo_Gc(); //有变量引用计数器加1
        Demo_Gc d2 = new Demo_Gc(); 
        d1.demo = d2; 
        d2.demo = d1; //加1
        
        d1 = null; //减1,但实际d1已经是垃圾对象
        d2 = null;
    }
}

class Demo_Gc{
    public static Object demo;
}

2、根搜索方法
这是JVM一般使用的算法,根搜索算法是从离散数学中的图论导入的。程序把所有的引用关系看作一张图,从一个节点GC ROOT开始,寻找对应的引用节点,找到这个节点后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点成为了垃圾对象。
3、Java可以作为GC ROOT的对象有:1.虚拟机栈中引用的变量(本地变量表);2.方法去中静态属性引用的对象;3.方法去中常量引用的对象;4.本地方法去中引用的对象(Native对象)

注意事项

  • 尽量不要创建很大的对象
    原因:GC回收算法从来不对大对象(>=85000字节)堆进行内存压缩整理,在堆中大的内存块会浪费太多CPU时间
  • 不要频繁的new生命周期很短的对象
    这样频繁垃圾回收频繁压缩有可能会导致很多内存碎片
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值