JVM GC(垃圾回收机制)
在学习Java GC之前,我们需要记住一个单词:stop-the-world。它会出现在任何一种GC算法中。stop-the-world意味着JVM因为需要执行GC而停止了应用程序的执行。当stop-the-world发生时,除GC所需的线程外,所有的线程都进入等待的状态,直到GC任务完成。
GC优化很多时候就是减少stop-the-world的发生。
JVM GC回收哪个区域的垃圾?
需要注意的是,JVM GC只回收堆区和方法区内的对象。而栈区的数据,在超出作用域后会被JVM自动释放掉,所以其不在JVM GC的管理范围内。
JVM GC怎么判断对象可以被回收?
-对象没有引用
-作用域发生未捕获异常
-程序在作用域正常执行完毕
-程序执行了System.exit()
-程序发生意外终止(被杀线程等)
在Java程序中不能显示的分配和注销缓存,因为这些事情JVM都帮我们做了,那就是GC。
有时候我们可以将相关的对象设置成null来试图显式的清除缓存,但是并不是设置成null就会一定被标记成可回收,有可能发生逃逸。
将对象设置成null至少没有什么坏处,但是使用System.gc()便不可取了,使用System.gc()时并不是马上执行GC操作,而是会等待一段时间,甚至不执行,而且System.gc()如果被执行,会触发Full GC,这非常影响性能。
JVM GC什么时候执行?
eden区空间不够存放新对象的时候,执行Minro GC。升到老年代的对象大于老年代剩余空间的时候执行Full GC,小于HandlePromotionFailure参数时被强制Full GC。调优主要是减少Full GC的触发次数,可以通过NewRatio控制新生代转老年代的比例,通过Max Tenuring Threshold设置对象进入老年代的年龄阀值。
按代的垃圾回收机制
新生代(Young generation):绝大多数最新被创建的对象都会被分配到这里,由于大部分在创建后很快变得不可达,很多对象被创建在新生代,然后“消失”。对象从这个区域“消失”的过程我们称为:Minor GC。
老年代(Old generation):对象没有变得不可达,并且从新生代周期中存活下来,会被拷贝到这里。其区域分配的空间要比新生代多。也正因为其较大的空间,发生在老年代的GC次数要比新生代少的多。对象从老年代消失的过程,称之为:Major GC或者Full GC。
持久层(Permanent generation)也称为方法区(Method area):用来保存类常量以及字符串常量。注意,这个区域不是用来存储那些从老年代存活下来的对象,这个区域也可能发生GC。发生在这个区域的GC时间也被算为Major GC。只不过这个区域发生GC的条件非常苛刻,必须符合以下三种条件才会被回收:
所有实例被回收
加载该类的ClassLoader被回收
CLass对象无法通过任何途径访问(包括访问)
如果老年代的对象需要引用新生代的对象,会发生什么?
为了解决这个问题,老年代中存在一个card table,他是一个512byte大小的块。所有老年代的对象指向新生代对象的引用都会被记录在这个表中。当针对新生代执行GC的时候,只需要查询card table来执行是否可以被回收,而不用查询整个老年代。这个card table由一个write barrier来管理。write barrier给GC带来了很大的性能提升,虽然由此可能带来一些开销,但完全是值得的。
默认的新生代(Young generation)、老年代(Old generation)所占空间比例为1:2
新生代空间的构成与逻辑
为了更好的理解GC,我们来学习新生代的构成,它用来保存那些第一次被创建的对象,它被分成三个空间
-一个伊甸园(Eden)
-两个幸存者空间(From Survivor、To Survivor)
默认新生代空间分配:Eden:From:To = 8:1:1
每个空间的执行顺序如下:
绝大多数刚刚被创建的对象会存放在伊甸园空间(Eden)
在伊甸园空间执行第一次GC(Minor GC)之后,存活的对象被移动到其中一个幸存者空间(Survivor)
此后,每次伊甸园空间执行GC后,存活的对象会被堆积在同一个幸存者空间。
当一个幸存者空间饱和,还在存活的对象会被移动到另一个幸存者空间。然后会清空已经饱和的那个幸存者空间。
在以上步骤中重复N次(N = MaxTenuringThreshold(年龄阈值设定,默认15))依然存活的对象,就会被移动到老年代。
从上面的步骤可以发现,两个幸存者空间,必须有一个是保持空的。如果两个两个幸存者空间都有数据,或两个空间都是空的,那一定是你的系统出现了某种错误。
我们需要重点记住的是,对象在刚刚被创建之后,是保存在伊甸园空间的(Eden)。那些长期存活的对象会经由幸存者空间(Survivor)转存到老年代空间(Old generation)。
也有例外出现,对于一些比较大的对象(需要分配一块比较大的连续内存空间)则直接进入到老年代。一般在Survivor 空间不足的情况下发生。
并且Survivor并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。