JVM自动内存管理,Minor GC与Full GC的触发机制

JVM自动内存管理,Minor GC与Full GC的触发机制

1、java垃圾回收机制

GC 就是Java垃圾回收机制。主流的JVM(HotSpot)采用的是分代收集算法。与c++不同的是,Java采用类似于树形结构的可达性分析法来判断对象是否还存在引用。即:从gcroot开始,把所有的可以搜索得到的对象标记为存活对象。
缺点:

  • 有可能不知不觉浪费了很多内存
  • JVM花费大量时间来进行垃圾回收
  • 容易内存泄漏

理解Java的垃圾回收机制,就要从:“什么时候”,“对什么东西”,“做了什么”三个方面来具体分析。
第一:“什么时候”即就是GC触发的条件。GC触发的条件有两种。(1)程序调用System.gc时可以触发;(2)系统自身来决定GC触发的时机。系统判断GC触发的依据:根据Eden区和From Space区的内存大小来决定。当内存大小不足时,则会启动GC线程并停止应用线程。
第二:“对什么东西”笼统的认为是Java对象。但是准确来讲,GC操作的对象分为:通过可达性分析法无法搜索到的对象和可以搜索到的对象。对于搜索不到的方法进行标记。
第三:“做了什么”最浅显的理解为释放对象。但是从GC的底层机制可以看出,对于可以搜索到的对象进行复制操作,对于搜索不到的对象,调用finalize()方法进行释放。

	具体过程:
		当GC线程启动时,
		会通过可达性分析法把Eden区和From Space区的存活对象复制到To Space区,
		然后把Eden Space和From Space区的对象释放掉。
		当GC轮训扫描To Space区一定次数后,
		把依然存活的对象复制到老年代,然后释放To Space区的对象。

对于用可达性分析法搜索不到的对象,GC并不一定会回收该对象。要完全回收一个对象,至少需要经过两次标记的过程:
第一次标记:对于一个没有其他引用的对象,筛选该对象是否有必要执行finalize()方法,如果没有执行必要,则意味可直接回收。(筛选依据:是否复写或执行过finalize()方法;因为finalize方法只能被执行一次)。
第二次标记:如果被筛选判定位有必要执行,则会放入FQueue队列,并自动创建一个低优先级的finalize线程来执行释放操作。如果在一个对象释放前被其他对象引用,则该对象会被移除FQueue队列。

年轻代(Young Generation)
对象在被创建时,内存首先是在年轻代进行分配(注意,大对象可以直接在老年代分配)。当年轻代需要回收时会触发Minor GC(也称作Young GC)。

年轻代由Eden Space和两块相同大小的Survivor Space(又称From Space和To Space)构成,Eden区和Servior区的内存比为8:1,可通过-Xmn参数来调整新生代大小,也可通过-XX:SurvivorRadio来调整Eden Space和Survivor Space大小。不同的GC方式会按不同的方式来按此值划分Eden Space和Survivor Space,有些GC方式还会根据运行状况来动态调整Eden、From Space、To Space的大小。

年轻代的Eden区内存是连续的,所以其分配会非常快;同样Eden区的回收也非常快(因为大部分情况下Eden区对象存活时间非常短,而Eden区采用的复制回收算法,此算法在存活对象比例很少的情况下非常高效)。如果在执行垃圾回收之后,仍没有足够的内存分配,也不能再扩展,将会抛出OutOfMemoryError:Java Heap Space异常。

老年代(Old Generation)
老年代用于存放在年轻代中经多次垃圾回收仍然存活的对象,可以理解为比较老一点的对象,例如缓存对象;新建的对象也有可能在老年代上直接分配内存,这主要有两种情况:一种为大对象,可以通过启动参数设置-XX:PretenureSizeThreshold=1024,表示超过多大时就不在年轻代分配,而是直接在老年代分配。此参数在年轻代采用Parallel Scavenge GC时无效,因为其会根据运行情况自己决定什么对象直接在老年代上分配内存;另一种为大的数组对象,且数组对象中无引用外部对象。

当老年代满了的时候就需要对老年代进行垃圾回收,老年代的垃圾回收称作Full GC。老年代所占用的内存大小为-Xmx对应的值减去-Xmn对应的值。

Minor GC(新生代GC)的触发条件

当Eden区满时,触发Minor GC。

Full GC(老年代GC)的触发条件

(1)直接调用System.gc

(2)老年代空间不足(新生代存活下来的对象转入、大对象的创建等引起)

    调优策略:

    尽量做到让对象在Minor GC阶段被回收 
    让对象在新生代多存活一段时间 不要创建过大的对象及数组

(3)方法区空间不足(系统中要加载的类、反射的类和调用的方法较多等导致)

    调优策略:

    增大方法区空间 转为使用CMS GC

(4)Minor GC 时,survivor放不下,对象只能放入老年代,而此时老年代也放不下

    调优策略:

    增大survivor space、老年代空间

(5)通过Minor GC后进入老年代的平均大小大于老年代的连续可用内存(Minor GC 时会做一个判断,统计之前晋升到老年代的对象的平均大小)

例如 应用程序第一次触发Minor GC后,有 5MB的对象晋升到老年代,那么当下一次Minor GC发生时,首先检查老年代的剩余空间是否大于 5MB,如果小于 5MB,则执行Full GC。

你可能感兴趣的:(java)