15.垃圾回收相关概念
一、System.gc()的理解
在默认情况下,通过System.gc()或者Runtime.getRuntime().gc()的调用,
会显示触发 Full gc,同时对老年代和新生代进行垃圾回收,尝试去释放被丢弃对象占用的空间。
然而System.gc()附带一个免责声明,无法保证垃圾收集器的调用(不一定会执行)
**JVM实现着者可以通过System.gc()调用来决定JVM的gc行为。而一般情况,垃圾回收应该是自动执行的,无需手动触发,否则就太过于麻烦了。在一些特殊情况下,如我们正在编写性能测试的,就需要活动调用。
**
二、内存溢出(OOM)与内存泄露
2.1、内存溢出(OOM)
在抛出OOM之前,一定会触发一次 Full GC
大多数情况下,GC会进行各种年龄段的垃圾回收,实在不行了就放大招,来一次独占式的Full Gc操作,这时候会回收大量的内存,供应用程序继续使用。
javadoc中对outOfMemoryError的解释是,没有空闲内存,并且垃圾收集器也无法提供更多内存。
2.2、内存泄露(Memory Leak)
对象不再被程序用到,但是GC又不能回收他们的情况,称为内存泄露
举例:
1 单例模式:
单例的生命周期和应用程序一样长,所以单例模程序中,如果持有对外部对象引用的话,那么这个外部引用是无法被回收的,会导致内存泄漏
2 一些资源提供close方法,每调用会导致内存泄漏
我们没有及时的关闭资源 ,如数据库连接,网络连接和io等。
三、Stop The World(STW)
stop-the-world ,简称STW,指的是GC事件发生过程中,会产生应用程序的停顿。停顿产生时整个应用程序线程都会被暂停,没有任何响应,有点像卡死的感觉,这个停顿称为STW。
可达性分析算法中枚举根节点(GC Roots)会导致所有Java执行线程停顿。
√分析工作必须在一个能确保一致性的快照中进行
√一致性指整个分析期间整个执行系统看起来像被冻结在某个时间点上
√如果出现分析过程中对象引用关系还在不断变化,则分析结果的准确性无法保证
- 被sTw中断的应用程序线程会在完成GC之后恢复,频繁中断会让用户感觉像是网速不快造成电影卡带一样,所以我们需要减少sTw的发生。
注意:开发中不要用System.gc(),会导致用户线程终止
四、垃圾回收的并发与并行
4.1、并发:
在操作系统中,是指一个时间段有几个程序都处于一起动运行到运行完毕之间,且这几个程序都在同一处理器中运行。
并发并不是真正意义上的”同时进行”只是CPU来回切换,看似像并发而已。
4.2、并行:
当系统中一个或以上的CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另外一个进程,两个进程互相不抢CPU资源,可以同时进行,我们称之为“并行”。
4.3、二者对比:
4.4、垃圾回收的并行与并发
并行:多条垃圾回收器并行工作,但是用户线程仍然等待
串行:单线程执行;如果内存不足,程序暂停,垃圾回收,在启动程序;
并发:用户线程和垃圾回收线程同时执行(但不一定是并行的,可能会交替执行),垃圾回收线程在执行时,不会停顿用户线程.
- 用户程序继续进行,而垃圾收集程序线程运行在另一个CPU上
- 如:CMS、G1
五、安全点与安全区域
5.1、安全点
程序执行时,并非在所有地方都能停顿下来GC,只有在特定的位置才能停顿下来GC,这些位置称为 安全点。
如何在发生GC时,检查所有的线程都跑到最近的安全点停顿下来了呢?
5.2、安全区域
安全区域是指在一段代码片段中,对象的引用关系不会发生变化,在这个区域中的任何位置开始GC都是安全的。我们也可以把 safe Region看做是被扩展了的 safepoint。
六、再谈引用:强引用(不回收)
存在引用关系绝不回收
强引用都是可触及的,即可达的
强引用是java内存泄漏的主要原因之一
七、再谈引用:软引用(内存不足回收)
当内存足够时,不会回收你,发生溢出之前,即使你存在引用关系,也回收你
八、再谈引用:弱引用(检查->发现及回收)
无论内存溢出不溢出,GC发现及回收,即使引用关系,也回收
九、再谈引用:虚引用(对象回收跟踪)
唯一目的:就是能在这个对象被垃圾回收时收到一个系统通知