• 通过jvisualvm监控fullgc


    jvisualvm添加visualgc插件,查看内存的状态转移

    默认新生代:老生代比例为1:2,默认eden区和s1,s2区的比例为 8:1:1,通过设置很小的jvm堆参数,比如9M,这样新生代为3M,老生代为6M

    设置一个对象为1M,并且不释放内存,可以很快看到内存的走向,从eden区转向s1,再移到old区,经过几次之后old区也满了,就产生fullgc,接着oom

    public class Visual {
    
        byte[] bytes = new byte[1024 * 1024 * 1];
    
        public static void main(String[] args) throws InterruptedException {
            case2();
        }
    
        private static void case1() throws InterruptedException {
            List<Visual> list = new ArrayList<>();
            for (; ; ) {
                list.add(new Visual());
                System.out.println(new Date().toString());
                Thread.sleep(100L);
            }
        }
    
        private static void case2() throws InterruptedException {
            List<Visual> list = new ArrayList<>();
            for (; ; ) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 1000; i++) {
                            list.add(new Visual());
                            try {
                                Thread.sleep(100L);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }).start();
    
                Thread.sleep(10000L);
            }
        }
    }

    cms垃圾回收:

    young gc(MinorGC)时是通过标记复制算法,从eden区复制到s1区,从s1区和s2区来回复制

    fullgc(MajorGC)是通过标记压缩算法,通过计算GCRoot并遍历,然后标记压缩清除

    新生代需要经历多少次GC晋升到老年代

    用法: -XX:MaxTenuringThreshold=3
    该参数主要是控制新生代需要经历多少次GC晋升到老年代中的最大阈值。在JVM中用4个bit存储(放在对象头中),所以其最大值是15。
    但并非意味着,对象必须要经历15次YGC才会晋升到老年代中。例如,当survivor区空间不够时,便会提前进入到老年代中,但这个次数一定不大于设置的最大阈值。

    内存分配及回收策略——新生代如何进入老年代


    新生代GC(Minor GC) 指发生在新生代的垃圾收集动作,因为Java对象大多都具备朝生夕灭的特性,所以Minor GC非常频繁,一般回收速度也比较快。 老年代GC(Major GC/Full GC) 指发生在老年代的GC,出现了Major GC,经常会伴随至少一次的Minor GC(但非绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择过程)。 Major GC的速度一般会比Minor GC慢10倍以上。 对象优先在Eden分配 大多数情况下,对象在新生代Eden区中分配,当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。 大对象直接进入年老代 大对象即需要大量连续内存空间的Java对象,如长字符串及数组。经常出现大对象导致内存还有不少空间时就提前触发垃圾收集以获取足够的连续空间来安置他们。 虚拟机提供了一个-XX:PretenureSizeThreshold参数,令大于这个设置值的对象直接在老年代分配。 这样做的目的是避免在Eden区及两个Survivor区之间发生大量的内存复制(新生代采用复制算法收集内存)。 长期存活的对象将进入年老代 虚拟机给每个对象定义了一个对象年龄计数器,在对象在Eden创建并经过第一次Minor GC后仍然存活,并能被Suivivor容纳的话,将会被移动到Survivor空间,并对象年龄设置为1。每经历过Minor GC,年龄就增加1岁,当到一定程度(默认15岁,可以通过参数-XXMaxTenuringThreshold设置),就将会晋升年老代。 动态对象年龄判定 为了更好地适应不同程序内存状况,虚拟机并不硬性要求对象年龄达到MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入年老代。 空间分配担保 在发生Minor GC之前,虚拟机会先检查年老代最大可用的连续空间是否大于新生代所有对象的总空间,如果条件成立,那么Minor GC可以确保是安全的。 如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。 如果允许,那么会继续检查年老代最大可用连续空间是否大于历次晋升到年老代对象的平均大小,如果大于,将尝试进行一次Minor GC,尽管这次Minor GC是有风险的。 如果小于,或者HandlePromotionFailure设置不允许冒险,那这时候改为进行一次Full GC。 下面解释一下“冒险”是冒了什么风险,新生代使用复制收集算法,但为了内存利用率,只使用其中一个Survivor空间来作为轮换备份,因此当出现大量对象在MinorGC后仍然存活的情况(最极端的情况就是内存回收后新生代中所有对象都存活),就需要老年代进行分配担保,把Survivor无法容纳的对象直接进入老年代。 与生活中的贷款担保类似,老年代要进行这样的担保,前提是老年代本身还有容纳这些对象的剩余空间,一共有多少对象会活下来在实际完成内存回收之前是无法明确知道的,所以只好取之前每一次回收晋升到老年代对象容量的平均大小值作为经验值,与老年代的剩余空间进行比较,决定是否进行Full GC来让老年代腾出更多空间。 取平均值进行比较其实仍然是一种动态概率的手段,也就是说,如果某次Minor GC存活后的对象突增,远远高于平均值的话,依然会导致担保失败(Handle Promotion Failure)。 如果出现了HandlePromotionFailure失败,那就只好在失败后重新发起一次Full GC。 虽然担保失败时绕的圈子是最大的,但大部分情况下都还是会将HandlePromotionFailure开关打开,避免Full GC过于频繁,

      

  • 相关阅读:
    PHP中GBK和UTF8乱码解决方案
    Ubuntu下的PHP开发环境架设
    Windows 7 IE主页被篡改,如何修复?
    提高代码质量:如何编写函数
    PhpStorm 10 破解方法
    PHP 常用的header头部定义汇总
    kindle 退出演示模式
    好程序与差程序Good Programming, Bad Programming
    如何让你的一天能有26小时?不完全是开玩笑
    毕业若干年,才知道自己原来认为的很多都是错的想法的成熟
  • 原文地址:https://www.cnblogs.com/sidesky/p/12895521.html
Copyright © 2020-2023  润新知