我们用以下参数运行:-Xmx10M -Xms10M M  将jvm的大小设置为10M,不允许扩展,按引用计数法,t1和t2相互引用,他们的引用计数都不可能为0,那么他们将永远不会回收,在我们的环境中JVM共10M,t1 t2占用8m,那么剩下的2M,是不足以创建t3的,理论上应该抛出OOM。但是,程序正常运行了,这说明JVM应该是回收了t1和t2的我们加上-XX:+PrintGCDetails运行,将打印GC的回收日记:


[GC [DefNew: 252K->64K(960K), 0.0030166 secs][Tenured: 8265K->137K(9216K), 0.0109869 secs] 8444K->137K(10176K), [Perm : 2051K->2051K(12288K)], 0.0140892 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]
com.mail.czp.Test@2ce908
Heap
 def new generation   total 960K, used 27K [0x029e0000, 0x02ae0000, 0x02ae0000)
  eden space 896K,   3% used [0x029e0000, 0x029e6c40, 0x02ac0000)
  from space 64K,   0% used [0x02ad0000, 0x02ad0000, 0x02ae0000)
  to   space 64K,   0% used [0x02ac0000, 0x02ac0000, 0x02ad0000)
 tenured generation   total 9216K, used 4233K [0x02ae0000, 0x033e0000, 0x033e0000)
   the space 9216K,  45% used [0x02ae0000, 0x02f02500, 0x02f02600, 0x033e0000)
 compacting perm gen  total 12288K, used 2077K [0x033e0000, 0x03fe0000, 0x073e0000)
   the space 12288K,  16% used [0x033e0000, 0x035e74d8, 0x035e7600, 0x03fe0000)
No shared spaces configured.


  从打印的日志我们可以看出,GC照常回收了t1  t2,这从侧面证明jvm不是采用这种策略判定对象是否可以回收的。

  二,根搜索算法,这是当前的大部分虚拟机采用的判定策略,GC线程运行时,它会以一些特定的引用作为起点称为GCRoot,从这些起点开始搜索,把所用与这些起点相关联的对象标记,形成几条链路,扫描完时,那些没有与任何链路想连接的对象会判定为可回收对象。具体那些引用作为起点呢,一种是类级别的引用:静态变量引用、常量引用,另一种是方法内的引用,如之前的test()方法中的Date d对new Date()的引用,在我们的测试代码中,在创建t3时,jvm发现当前的空间不足以创建对象,会出发一次GC,虽然t1和t2相互引用,但是执行t1=t2=null后,他们不和上面的3个根引用中的任何一个相连接,所以GC会判定他们是可回收对象,并在随后将其回收,从而为t3的创建创造空间,当进行回收后发现空间还是不够时,会抛出OOM。

  接下来我们该讨论GC 是怎么回收的了,目前版本的Hotspot虚拟机采用分代回收算法,它把heap分为新生代和老年代两块区域,如下图:

  默认的配置中老年代占90% 新生代占10%,其中新生代又被分为一个eden区和两个survivor区,每次使用eden和其中的一个survivor区,一般对象都在eden和其中的一个survivor区分配,但是那些占用空间较大的对象,会直接在老年代分配,比如我们在进行文件操作时设置的缓冲区,如byte[] buffer = new byte[1024*1024],这样的对象如果在新生代分配将会导致新生代的内存不足而频繁的gc,GC运行时首先会进行会在新生代进行,会把那些标记还在引用的对象复制到另一块survivor空间中,然后把整个eden区和另一个survivor区里所有的对象进行清除,但也并不是立即清除,如果这些对象重写了finalize方法,那么GC会把这些对象先复制到一个队列里,以一个低级别的线程去触发finalize方法,然后回收该对象,而那些没有覆写finalize方法的对象,将会直接被回收。在复制存活对象到另一个survivor空间的过程中可能会出现空间不足的情况,在这种情况下GC回直接把这些存活对象复制到老年代中,如果老年代的空间也不够时,将会触发一次Full GC,Full gc会回收老年代中那些没有和任何GC Root相连的对象,如果Full GC后发现内存还是不足,将会出现OutofMemoryError。

  Hotspot虚拟机下java对象内存的分配和回收算完结了,后续我们将讨论java代码的重构。