zoukankan      html  css  js  c++  java
  • gc日志

    http://www.importnew.com/20129.html

      在用代码分析之前,我们对内存(堆)的分配策略明确以下三点:

    • 对象优先在Eden分配。
    • 大对象直接进入老年代。
    • 长期存活的对象将进入老年代。一般情况下接受过15次Minor GC后晋升老年代
    • survivor 的“to”区满了之后(并非survivor2片都满,survivor永远有1个-from区是空的),对象进到old区
        对垃圾回收策略说明以下两点:
    • 新生代GC(Minor GC):发生在新生代的垃圾收集动作,因为Java对象大多都具有朝生夕灭的特性,因此Minor GC非常频繁,一般回收速度也比较快。
    • 老年代GC(Major GC/Full GC):发生在老年代的GC,出现了Major GC,经常会伴随至少一次Minor GC。由于老年代中的对象生命周期比较长,因此Major GC并不频繁,一般都是等待老年代满了后才进行Full GC而且其速度一般会比Minor GC慢10倍以上。另外,如果分配了Direct Memory,在老年代中进行Full GC时,会顺便清理掉Direct Memory中的废弃对象。

        下面我们来看如下代码:

    1
    2
    3
    4
    5
    6
    publicclass SlotGc{
        publicstatic void main(String[] args){
            byte[] holder = newbyte[32*1024*1024];
            System.gc();
        }
    }

    代码很简单,就是向内存中填充了32MB的数据,然后通过虚拟机进行垃圾收集。在Javac编译后,我们执行如下指令:java -XX:+PrintGC来查看垃圾收集的结果,得到如下输出信息:

    [GC 208K->134K(5056K), 0.0017306 secs]

        [Full GC 134K->134K(5056K), 0.0121194 secs]

        [Full GC 32902K->32902K(37828K), 0.0094149 sec

    注意第三行,“->”之前的数据表示垃圾回收前堆中存活对象所占用的内存大小,“->”之后的数据表示垃圾回收堆中存活对象所占用的内存大小,括号中的数据表示堆内存的总容量,0.0094149 sec 表示垃圾回收所用的时间。

    从结果中可以看出,System.gc(()运行后并没有回收掉这32MB的内存,这应该是意料之中的结果,因为变量holder还处在作用域内,虚拟机自然不会回收掉holder引用的对象所占用的内存。

    我们把代码修改如下:

    1
    2
    3
    4
    5
    6
    7
    8
    publicclass SlotGc{
        publicstatic void main(String[] args){
            {
            byte[] holder = newbyte[32*1024*1024];
            }
            System.gc();
        }
    }

    加入花括号后,holder的作用域被限制在了花括号之内,因此,在执行System.gc()时,holder引用已经不能再被访问,逻辑上来讲,这次应该会回收掉holder引用的对象所占的内存。但查看垃圾回收情况时,输出信息如下:

    [GC 208K->134K(5056K), 0.0017100 secs]

        [Full GC 134K->134K(5056K), 0.0125887 secs]

        [Full GC 32902K->32902K(37828K), 0.0089226 secs]

    很明显,这32MB的数据并没有被回收。下面我们再做如下修改:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    publicclass SlotGc{
        publicstatic void main(String[] args){
            {
            byte[] holder = newbyte[32*1024*1024];
            holder = null;
            }
            System.gc();
        }
    }

    这次得到的垃圾回收信息如下:

        [GC 208K->134K(5056K), 0.0017194 secs]

        [Full GC 134K->134K(5056K), 0.0124656 secs]

        [Full GC 32902K->134K(37828K), 0.0091637 secs]

    说明这次holder引用的对象所占的内存被回收了。我们慢慢来分析。

    首先明确一点:holder能否被回收的根本原因是局部变量表中的Slot是否还存有关于holder数组对象的引用。

    在第一次修改中,虽然在holder作用域之外进行回收,但是在此之后,没有对局部变量表的读写操作,holder所占用的Slot还没有被其他变量所复用(回忆Java内存区域与内存溢出一文中关于Slot的讲解),所以作为GC Roots一部分的局部变量表仍保持者对它的关联。这种关联没有被及时打断,因此GC收集器不会将holder引用的对象内存回收掉。 在第二次修改中,在GC收集器工作前,手动将holder设置为null值,就把holder所占用的局部变量表中的Slot清空了,因此,这次GC收集器工作时将holder之前引用的对象内存回收掉了。

    当然,我们也可以用其他方法来将holder引用的对象内存回收掉,只要复用holder所占用的slot即可,比如在holder作用域之外执行一次读写操作。如:

        public static void main(String[] args){
            {
                byte[] holder = new byte[32*1024*1024];
             //   holder = null;   导致内存回收
            }
            
            // 词句在holder作用域之外执行一次读写操作。也可导致内存回收
            //  int a= 0;
            
            System.gc();
        }

    为对象赋null值并不是控制变量回收的最好方法,以恰当的变量作用域来控制变量回收时间才是最优雅的解决办法。另外,赋null值的操作在经过虚拟机JIT编译器优化后会被消除掉,经过JIT编译后,System.gc()执行时就可以正确地回收掉内存,而无需赋null值。在下一个帖子有解说:https://blog.csdn.net/zero__007/article/details/52517712

     

    就这个例子来说,可以理解为直到System.gc()执行的那一刻,局部变量表中还有对placeholder的引用,因此在GC前的”=null”操作,实际是移除掉局部变量表中的placeholder引用,所以有”=null”的版本成功回收掉64M内存。
           那实验看起来不是证明”=null”某些情况下是有用的吗?实际上前面说了,”=null”作用仅仅是打断局部变量表中的引用。而做到这点并不一定非得placeholder = null,把这句替换成“int a = 1”也能达到效果,反正就把那个坑占了,不在乎扔进去的是“null”还是“1”。

    此前的ThreadLocal静态对象=null则不同,static修饰变量,生存期无限,除了显式=null打断GC Root外,无他法(https://blog.csdn.net/silyvin/article/details/79551635)

           最关键的是,上面实验建立在“未JIT的前提下”,在JIT编译器进行控制流和数据流分析后,生成的OopMap就提供比较精确的信息,不需要通过”=null”来告知对象使命已经完成。退一步说,这时即使有”=null”操作,也会被优化掉,生成出来的本地代码与没有”=null”操作的版本是一模一样的。对于在意性能的代码,必定是执行频率高,会被JIT的,而不会被JIT的,也不需要在意效率,因此”=null”没有意义。 

  • 相关阅读:
    Java Class/Method
    Data Types in Java
    Java overview && JVM
    Interface Vs. Abstract Class
    【javascript】Object三种定义方式
    【C#】导出Excel
    【javascript】简单原型链、借用构造函数
    Mysql表编码查看修改
    asp.net的mvc?
    连表
  • 原文地址:https://www.cnblogs.com/silyvin/p/9106570.html
Copyright © 2011-2022 走看看