欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-05-06 14:09:48
...

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。