JVM-垃圾回收相关概念

96 阅读5分钟

System.gc()

  • 在默认情况下,通过System.gc()或者Runtime().getRuntime().gc()的调用,会显示触发Full GC,同时对老年代和新生代进行回收,尝试释放被丢弃对象占用的内存。
  • 然而System.gc()调用附带一个免责声明,无法保证对垃圾回收器的调用。

内存溢出(OOM)

  • 没有空闲内存,且进行gc后仍然没内存。

内存泄漏

  • 严格来说,只有对象不会再被程序用到了,但是GC又不能回收他们的情况,才叫内存泄漏。
  • 例如:
  1. 单例模式:单例的生命周期和应用程序是一样的长的,所以单例程序中,如果持有对外部对象的引用的话,那么这个外部对象是不能被回收的,则会导致内存泄漏的产生。
  2. 一些提供close的资源未关闭导致内存泄漏,数据库连接,网络连接,io连接必须手动close。否则无法回收。

STW

  • 指的是GC事件发生过程中,会产生应用程序的停顿。停顿产生整个应用程序线程都会被暂停,没有任何响应,可达性分析算法中枚举根节点会导致所有Java执行线程停顿。分析工作必须在一个能确保一致性的快照中执行。一致性指整个分析期间整个执行系统看起来像被冻结在某个时间点上。如果出现分析过程中对象引用关系还在不段变化,则分析结果的准确性无法保证。

垃圾回收的并行与并发

  • 并行:指多条垃圾收集线程并行工作,但此时用户线程仍处于等待状态。(ParNew,Parallel Scavenge, Parallel Old)
  • 并发:指的是用户线程与垃圾线程同时执行(但不一定是并行,可能会交替执行),垃圾回收线程在执行时不会停顿用户程序的运行。(CMS,G1)

安全点与安全区域

  • 安全点:程序执行时并非在所有地方都能停顿下来开始GC,只有在特定的位置才能停顿下来开始GC,这些位置称为安全点。如何在GC发生时,检查所有线程都跑到最近的安全点停顿下来呢?
  1. 抢先式中断:(目前没有虚拟机采用),首先中断所有线程,如果还有线程不在安全点,就恢复线程,让线程跑到安全点。
  2. 主动式中断:设置一个中断标志,各个线程运行到Safe Point的时候主动轮训这个标志,如果中断标志为真,则将自己进行挂起中断。
  • 安全区域:如果程序不执行,如线程处于sleep状态或blocked状态,这时候线程无法响应JVM的中断请求,“走”到安全点去中断挂起,JVM也不太可能等待该线程被唤醒,对于这种情况,就需要安全区域来解决。安全区域是指在一段代码片段中,对象的引用关系不会发生变化,在这个区域中的任何位置开始GC都是安全的,我们也可以把Safe Region看做是被扩展了的SafePoint。
  1. 当线程运行到Safe Region的代码时,首先标识已经进入了Safe Region,如果这段时间发生GC JVM会忽略标识为Safe Region 状态的线程。
  2. 当线程即将离开Safe Region时,会检查JVM是否已经完成GC,如果完成了,则继续运行,否则线程必须等待直到收到可以安全离开Safe Region的信号为止。

引用

这些引用都是可达的。

强引用

  • 最传统的“引用定义”,是指在程序代码之中普遍存在的引用赋值,即类似“Object obj = new Object()”这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器永远不会回收掉被引用的对象。强引用是造成内存泄漏的主要原因之一。

软引用

  • 在系统将要发生内存溢出之前,将会把这些对象列入回收范围之中进行第二次回收。如果这次回收后还没有足够的内存,才会抛出内存溢出异常。软引用通常用来实现内存敏感的缓存。比如:高速缓存就有用到了软引用。有空间就可以暂时保存缓存,如果没有空间就清理掉。
  • Object obj = new Object();
  • SoftReference<Object> sf = new SoftReference<Object>(obj);
  • obj = null;

弱引用

  • 只被弱引用关联的对象只能存活到下一次垃圾收集之前,当垃圾收集器工作时,无论内存空间是否足够,都睡回收掉弱引用关联的对象。
  • 弱引用也可以用作缓存,比软引用更容易被回收
  • Object obj = new Object();
  • WeakReference<Object> wf = new WeakReference<Object>(obj);
  • obj = null;

虚引用

  • 一个对象是否有虚引用的存在,完全不会对其生存时间造成影响,也无法通过虚引用来获得一个对象的实例,为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。当试图通过虚引用的get()方法取得对象时,总是null。
  • 虚引用必须和引用队列一起使用。虚引用在创建时必须提供一个引用队列作为参数。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象后,将这个虚引用加入引用队列,已通知应用程序对象的回收情况,可以跟踪对象的回收时间。
  • Object obj = new Object();
  • PhantomQueue pq = new PhantomQueue();
  • PhantomReference<Object> pf = new PhantomReference<Object>(obj,pq);
  • obj = null;