zoukankan      html  css  js  c++  java
  • java学习day01---GC

    GC垃圾回收

    垃圾回收(GC)是Java虚拟机(JVM)垃圾回收器提供的一种用于在空闲时间不定时回收无任何对象引用的对象所占据的内存空间的一种机制。GC在执行时会进行可达性分析, 如果内存的对象不可达, 则会被回收.

    finalize()方法的作用:

     //finalize方法会在对象被回收(GC)之前执行, 可以对对象的回收进行监控, 也可以在对象回收之前进行一些资源释放操作
     @Override
     protected void finalize() throws Throwable {
      System.out.println("==finalize()==");
    }

    创建一个类测试GC:

    //通过JVM参数检测是否触发了GC操作:-XX:+PrintGCDetails
    public class TestGC01 {
        static Map<String,Object> objectPool=new HashMap<>();
        public static void main(String[] args) throws InterruptedException {
            //构建一个实例对象,并通过P1引用指向这个对象
             Point p1=new Point(10,20);//p1为一个强引用
             objectPool.put("point", p1);//Spring中的singleton作用域
             p1=null; //Spring中prototype作用域对象的销毁
             objectPool.clear();//Spring中Singleton作用域的销毁
            //请问对于p1引用的这个对象何时会被标识垃圾对象,何时会被回收,如何确定此对象被回收了
            //1)当p1引用不再指向构建的Point对象时,此对象会被GC系统认为是垃圾对象。
            //2)当JVM系统触发了GC操作时,对象可能会被回收。
            //3)此对象会被JVM中的垃圾回收系统(GC系统)进行回收。(释放内存)
            //触发GC操作?(GC系统触发时会对内存中的对象进行可达性分析,就是检测是否还可以通过引用
            //访问到此对象,假如不能通过任何引用访问此对象,这个对象就会被标识垃圾)
            //1.手动GC
            //System.gc();
            //2.自动GC(满足了GC条件时或者说内存使用达到一定的GC启动标准)
             List<byte[]> list=new ArrayList<>();
             for(int i=0;i<100000;i++) {
                 list.add(new byte[1024*1024]);
             }
        }
    }

    1.在JAVA中我们可以使用的对象引用方式有四种:

    引用:如果Reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。

    1)强引用:此引用引用的对象, 生命力最强。(对象不会被GC)

    2)软引用:此引用引用的对象, 在内存不足时可能会被GC。

    3)弱引用:此引用引用的对象, 在GC执行时可能直接会被销毁(即便是内存充足)

    4)虚引用:用的最少, 类似没有引用,主要用于记录对象的销毁。---了解

    说明: 软引用和弱引用通常会应用在一些缓存产品的设计中

    2.JAVA中的新生代,老年代,持久代:

    新生代(Young Gen):年轻代主要存放新创建的对象,内存大小相对会比较小,垃圾回收会比较频繁。年轻代分成1个Eden Space和2个Suvivor Space(命名为A和B)。当对象在堆创建时,将进入年轻代的Eden Space。垃圾回收器进行垃圾回收时,扫描Eden Space然后把存活的对象复制到A Suvivor Space,如果A满了就触发垃圾回收机制,如果对象仍然存活,则复制到B Suvivor Space,如果B Suvivor Space已经满,则复制到Old Gen。同时,在扫描Suvivor Space时,如果对象已经经过了几次的扫描仍然存活,JVM认为其为一个持久化对象,则将其移到Old Gen。扫描完毕后,JVM将Eden Space和A Suvivor Space清空,然后交换A和B的角色(即下次垃圾回收时会扫描Eden Space和B Suvivor Space。这么做主要是为了减少内存碎片的产生。    

    老年代(Tenured Gen):年老代主要存放JVM认为生命周期比较长的对象(经过几次的Young Gen的垃圾回收后仍然存在),内存大小相对会比较大,垃圾回收也相对没有那么频繁(譬如可能几个小时一次)。年老代主要采用压缩的方式来避免内存碎片(将存活对象移动到内存片的一边,也就是内存整理)。当然,有些垃圾回收器(譬如CMS垃圾回收器)出于效率的原因,可能会不进行压缩。

    持久代(Perm Gen):持久代主要存放类定义、字节码和常量等很少会变更的信息。

    JVM区域总体分两类,heap区和非heap区。heap区又分:Eden Space(伊甸园)、Survivor Space(幸存者区)、Tenured Gen(老年代-养老区)。 非heap区又分:Code Cache(代码缓存区)、Perm Gen(永久代)、Jvm Stack(java虚拟机栈)、Local Method Statck(本地方法栈)。

    HotSpot虚拟机GC算法采用分代收集算法:

    1、一个人(对象)出来(new 出来)后会在Eden Space(伊甸园)无忧无虑的生活,直到GC到来打破了他们平静的生活。GC会逐一问清楚每个对象的情况,有没有钱(此对象的引用)啊,因为GC想赚钱呀,有钱的才可以敲诈嘛。然后富人就会进入Survivor Space(幸存者区),穷人的就直接kill掉。

    2、并不是进入Survivor Space(幸存者区)后就保证人身是安全的,但至少可以活段时间。GC会定期(可以自定义)会对这些人进行敲诈,亿万富翁每次都给钱,GC很满意,就让其进入了Genured Gen(养老区)。万元户经不住几次敲诈就没钱了,GC看没有啥价值啦,就直接kill掉了。

    3、进入到养老区的人基本就可以保证人身安全啦,但是亿万富豪有的也会挥霍成穷光蛋,只要钱没了,GC还是kill掉。

    3.FQA:

    1)当对象存在引用时, 是否可能在系统触发GC时被回收? 可能,要看引用类型,弱,软,虚 引用都有可能被回收

    2)当GC系统没有启动时, 对象是否可能被回收? 可能,看对象分配在哪了? 如果分配在栈上,不需要通过GC就能被回收

    3)当GC系统触发时, 假如对象要被销毁了, 会执行finalize方法吗?

    finalize()并不是必须要执行的,它只能执行一次或者0次。如果在finalize中建立对象关联,则当前对象可以复活一次。Finalizer线程不保证一定执行finalize方法,因为此线程的优先级很低,获得CPU资源有限;而且这样会避免finalize执行缓慢或者发生死循环,从而导致整个GC奔溃

    回收时间:

    即触发GC的时间,在新生代的Eden区满了,会触发新生代GC(Mimor GC),经过多次触发新生代GC存活下来的对象就会升级到老年代,升级到老年代的对象所需的内存大于老年代剩余的内存,则会触发老年代GC(Full GC)。当程序调用System.gc()时也会触发Full GC。

    [GC (System.gc()) [PSYoungGen: 1966K->600K(37888K)] 1966K->608K(123904K), 0.0036263 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
    [Full GC (System.gc()) [PSYoungGen: 600K->0K(37888K)] [ParOldGen: 8K->517K(86016K)] 608K->517K(123904K), [Metaspace: 2646K->2646K(1056768K)], 0.0087037 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
     Heap
     PSYoungGen      total 37888K, used 983K 
          eden space 32768K, 3% 
        from space 5120K, 0%
        to   space 5120K, 0%
        ParOldGen       total 86016K, used 517K 
      object space 86016K, 0% 
        Metaspace       used 2654K, capacity 4486K, committed 4864K, reserved 1056768K
      class space    used 282K, capacity 386K, committed 512K, reserved 1048576K

    堆里面的分区:Eden,survival (from+ to),老年代,各自的特点?

    堆里面分为新生代和老生代(java8 取消了永久代,采用了 Metaspace),新生代包含 Eden+Survivor 区,survivor 区里面分为 from 和 to 区,内存回收时,如果用的是复制算法,从 from 复制到 to,当经过一次或者多次 GC 之后,存活下来的对象会被移动到老年区,当 JVM 内存不够用的时候,会触发 Full GC,清理 JVM 老年区当新生区满了之后会触发 YGC,先把存活的对象放到其中一个 Survice 区,然后进行垃圾清理。
    
    因为如果仅仅清理需要删除的对象,这样会导致内存碎片,因此一般会把 Eden 进行完全的清理,然后整理内存。
    
    那么下次 GC 的时候,就会使用下一个 Survive,这样循环使用。
    
    如果有特别大的对象,新生代放不下,就会使用老年代的担保,直接放到老年代里面。
    因为 JVM 认为,一般大对象的存活时间一般比较久远。  

    参考文章:

    1.Java 新生代、老年代、持久代、元空间 
    2.Java内存与垃圾回收调优 
    3.Java面试之JVM中的新生代和老年代

  • 相关阅读:
    LAMP LNMP 和 LNMPA
    nginx版本如何选择?
    如何看apache的版本号
    CLR Via CSharp读书笔记(12):泛型
    要先怀疑外部代码的错误,再检测是不是自己代码的问题
    redis的那些事
    An Illustrated Guide to SSH Agent Forwarding
    Using sshagent with ssh
    dtach
    [原创]bind DNS IP列表的精确获取
  • 原文地址:https://www.cnblogs.com/zl0305/p/13219103.html
Copyright © 2011-2022 走看看