目录
一、System.gc()的理解
System.gc或Runtime.getRuntime().gc()的调用,会显示触发FullGC,同时会对老年代和新生代进行回收,尝试释放被丢对象占用的内存。然而System.gc调用无法保证对垃圾收集器的调用,即运行这行代码并不一定会产生垃圾回收。
一些特殊情况下,比如编写性能基准,我们可以在运行之间调用System.gc
二、内存溢出与内存泄露
(一)OOM
1、java 虚拟机的堆内存设置不够
2、代码创建大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)
(二)内存泄露
只有对象不再被程序用到了,但是GC又不能回收他们的情况,才叫内存泄露
实际情况有一些疏忽导致对象的生命周期变的很长甚至OOM,宽泛意义上的内存泄露
举例
- 单例的生命周期和程序是一样长,如果单例程序中,持有对外部对象的引用的话,那么这个外部对象是不能被回收的,导致内存泄露
- 一些提供close的资源未关闭导致内存泄露,如数据库链接,网络链接,和IO
三、StopTheWorld
指的是在垃圾回收线程工作时停止用户线程的一种情况,也称为STW。
四、垃圾回收的并行与并发
首先理解最基本的并行与并发
并发:同一时间段内,几个程序都在同一个处理器上运行 CPU切换
并行:一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互相不抢占资源,可以同时进行,我们称之为并行 并行因素取决于CPU的核心数量
并发的多个任务之间抢占资源,并行多个任务之间不互相抢占资源
垃圾回收器中的
- 串行指的是单个垃圾回收线程串行执行,例如Serial收集器
- 并行指的是多个垃圾回收线程并行执行,例如ParNew收集器
- 并发指的是垃圾回收线程和用户线程并发执行,例如CMS收集器
五、安全点与安全区域
(一)安全点
程序执行并非在所有地方都能停顿下来开始GC,只有特定的位置才能停顿下来开始GC,这些位置称为安全点
- 抢先式中断
- 中断所有线程,如果还有线程不在安全点,就恢复线程,让线程跑到安全点
- 没有虚拟机采用
- 主动式中断
- 设置一个中断标志,各个线程运行到安全点的时候,主动轮询这个标志,如果标志为真,则将自己进行中断挂起
(二)安全区域
如果线程处于sleep或者blocked状态,这时候线程无法响应jvm中断请求,走到安全点去中断挂起。对于这种情况,就需要安全区域来解决
安全区域是指在一段代码片段中,对象的引用关系不会发生变化,在这个区域中任何位置开始GC都是安全的。
当线程运行到安全区域代码时,首先标志已经进入了安全区域,如果GC,JVM会忽略标识为安全区域状态的线程
当线程即将离开安全区域时,会检查JVM是否已经完成GC,如果完成了,则继续运行。否则线程必须等待直到收到可以安全离开安全区域的信号为止
六、引用
Java中有五种引用
(一)强引用
强引用是最传统的引用定义,程序代码中普遍存在的引用赋值,类似new Object这种引用关系,无论任何情况下,强引用存在,垃圾收集器永远不会回收掉被引用的对象
强引用是造成java内存泄露的主要原因之一,强引用可以直接访问目标对象
(二)软引用
若某个对象被软引用,当gc一次后内存仍然紧张时,该对象在第二次gc会被回收
(三)弱引用
若某个对象被弱引用,当gc发生时,该对象会被回收
注:软和弱引用不一定要配合引用队列(引用队列:每个引用自身也会占用一定的内存,当其指向的对象被垃圾回收时,也可以将引用入队,然后通过referenceHandler来定期查看队列是否有新的引用入队,然后进行清除引用回收内存)
(四)虚引用
一个对象是否有虚引用存在,完全不会对其生存时间构成影响。唯一目的就是在这个对象被收集器回收时收到一个系统通知。它不能单独使用,也无法通过虚引用获取被引用的对象。
比如bytebuffer被回收时,其分配的直接内存没有释放,此时虚引用入队,Cleaner通过底层调用Unsafe.freeMemory()方法来释放直接内存(由Reference Handler线程调用虚引用相关方法释放直接内存)
(五)终结器引用
Object有一个finallize方法。当我们重写该方法且该对象没有强引用指向它时,虚拟机会生成一个终结器引用,在第一次gc的时候将引用加入引用队列(此时对象并没有被回收),然后会有一个优先级较低的finallizeHandler线程查看队列是否有终结器引用,如果有就调用其指向对象的finallize终结方法,然后下一次gc的时候该对象和对应的终结器引用就可以被回收了
注:虚和终结器引用一定要配合引用队列