这是垃圾收集机制系列文章的第二篇。希望您已经阅读了Java垃圾收集简介的第一部分。Java垃圾收集是一个自动进程,用于管理程序在运行时使用的内存。JVM通过GC自动执行,将程序员从繁重的申请和释放内存的操作中解放出来。Java垃圾回收GC初始化是一个自动过程,程序员不需要在代码中主动初始化GC。Java提供了System.gc()和Runtime.gc()两个钩子来请求JVM调用GC进程。虽然需要系统机制为程序员提供调用GC的机会,但实际上这是由JVM决定的。JVM可以选择拒绝启动GC的请求,因此无法保证这些请求实际上会调用垃圾收集。这是JVM根据内存堆空间Eden区的使用情况做出的决定。JVM规范把这个选择权留给了每个JVM的具体实现,所以实际上JVM如何选择取决于不同JVM的实现(但请记住,你不能依赖这两个方法的调用,它们是不能保证的)。毫无疑问,我们知道垃圾回收过程是无法强制执行的。但是我刚刚发现调用System.gc()确实有意义的场景。阅读本文,您将了解System.gc()调用可用的特定场景。JavaGarbageCollectionProcess垃圾收集是回收未使用的内存空间并使其可供未来实例使用的过程。EdenSpace:实例创建时,最初存放在堆内存空间的新生代的Eden区。注意:如果你不理解这些术语,我建议你先阅读介绍内存模型、JVM架构和这些术语详细解释的文章:garbage-collection-introduction-tutorialSurvivorSpace(S0andS1):aspart在minorcollectioncycle中,仍然存活的对象(以及对它的引用)从eden区域移动到survivor空间S0。同样,垃圾收集器扫描S0并将活动实例移动到S1。无用的对象(没有对它们的引用)被标记和回收。垃圾收集器(有四种垃圾收集器可用,将在下一篇文章中介绍)决定这些标记的实例是在扫描期间移出内存还是在单独的迁移过程中执行。老年代:老一代或第一代是堆内存的第二个逻辑部分。当垃圾回收器在做minorGC循环时,S1幸存者区中存活的实例会被提升到老年代。S1区域中不再引用的对象被标记并清除。MajorGC:Java垃圾回收期间实例生命周期的最后阶段。MajorGC在垃圾回收时扫描属于OldGeneration部分的堆内存。如果实例没有被任何引用关联,它们将被标记和清除;如果它们仍然通过引用关联,它们将继续存在于老年代。MemoryFragmentation:一旦实例从堆内存中删除,它们原来的位置将被释放,以供以后分配实例。显然这些空闲空间在内存空间中容易产生碎片。为了更快地分配实例地址,需要对内存进行碎片整理。根据不同垃圾收集器的策略,回收的内存会在回收过程的同时或在GC的另一个独立进程中进行压缩和合并。垃圾回收过程中的对象销毁——Finalization在移除一个对象并回收其内存空间之前,Java垃圾回收器会调用每个实例的finalize()方法,使实例对象有机会释放其占用的资源。finalize()方法虽然保证在内存空间被回收之前执行,但是具体的执行时间和执行顺序并不能保证。多个实例之间finalize()的执行顺序是无法提前预测的,甚至有可能是并行执行的。程序不应该预设实例执行finalize()方法,也不应该使用finalize()方法来回收资源。默认忽略finalize过程中抛出的任何异常,并取消对象的销毁过程。JVM规范没有讨论弱引用的垃圾回收,这一点说的很清楚。确切的细节留给实施者。垃圾收集由守护进程执行一个对象什么时候变成可垃圾收集的?活线程无法访问的所有实例其他对象无法访问的循环引用对象Java中有许多不同类型的引用。实例的可回收性取决于它的引用类型。在编译过程中,Java编译器有一个优化机制。编译器可以选择将null分配给实例,从而将实例标记为可回收。classAnimal{publicstaticvoidmain(String[]args){Animallion=newAnimal();System.out.println("Mainiscompleted.");}protectedvoidfinalize(){System.out.println("RestinPeace!");}}在这个上面在类中,除了初始化行之外,实例lion没有在任何地方使用。因此作为一种优化,Java编译器可以在初始化行之后立即分配lion=null。这样finlizer可以在Main方法的SOP之前打印结果。安息!主要完成。但是结果的顺序是不确定的,它取决于JVM的实现和运行时的内存使用情况。从这里我们可以知道的一点是,当编译器发现一个实例在后续程序中不再被引用时,可以选择提前释放实例内存。这是一个实例何时变得可回收的更好示例。实例的所有属性都可以存储在寄存器中,然后从寄存器中读取,以后在任何情况下都不会将值写回实例对象。这样,虽然这个实例以后还会被使用,但是这个实例对象还是可以被标记为可回收的。什么时候可以被垃圾回收可以简单的想当它被赋值为null的时候,也可以复杂到上面的点。JVM的实现者做出了一些权衡。目标是留下最少的痕迹、缩短响应时间并提高吞吐量。为了实现这些目标,JVM实现者可以在垃圾回收中选择更好的模式或算法来回收内存。当调用finalize()时,JVM释放当前线程的所有同步块。GCScopeclassGCScope{GCScopet;staticinti=1;publicstaticvoidmain(Stringargs[]){GCScopet1=newGCScope();GCScopet2=newGCScope();GCScopet3=newGCScope();//没有对象是t1可以GC。t=t2;//没有对象可以GCt2.t=t3;//没有对象可以GCt3.t=t1;//没有对象可以GCt1=null;//没有对象可以GC,t3.t仍然有对t1的引用t2=null;//没有对象可以被GC,t3.t.t仍然有对t2的引用t3=null;//所有3个对象都可以被GC(没有一个被引用)//只有每个对象的变量t相互循环引用,形成一个孤立的引用环,没有外部引用}protectedvoidfinalize(){System.out.println("Garbagecollectedfromboject"+i);i++;}}GCOutOfMemoryError示例程序当内存溢出时,垃圾回收机制不保证安全。事实上,内存溢出会导致程序崩溃并抛出OutOfMemoryError。importjava.util.LinkedList;importjava.util.List;publicclassGC{publicstaticvoidmain(String[]args[]){Listl=newLinkedList();//进入内***循环直接向链表添加元素do{l.add(newString("Hello,World!");}while(true);}}线程“main”中的输出异常java.add(LinkedList.java:338)atcom.javapapers.java.GCScope.main(GCScope.java:12)
