垃圾回收相关概念

一、System.gc()的理解

在默认情况下,通过System.gc()或者Runtime.getRuntime().gc()的调用(其实System.gc()的底层实现方法就是Runtime.getRuntime().gc()),会显示触发Full GC,同时对老年代和新生代进行回收,尝试释放被丢弃对象占用的内存。

然而System.gc()调用附带一个免责声明,无法保证对垃圾收集器的调用。

JVM实现者可以通过System.gc()调用来决定JVM的GC行为。而一般情况下,垃圾回收应该是自动进行的,无须手动触发,否则就太过于麻烦了。在一些特殊情况下,如我们正在编写一个性能基准,我们可以在运行之间调用System.gc()。

垃圾回收相关概念_第1张图片 如上代码并不一定会输出打印语句,也就说明,我们主动调用System.gc但不能确保垃圾回收一定会执行
垃圾回收相关概念_第2张图片 但是如果加上System.runFianlization()就会强制调用失去引用的对象的finalize()方法

 举例说明

执行System.gc()后buffer对象并不会被回收掉,会被放到老年代,因为buffer有引用
垃圾回收相关概念_第3张图片 buffer失去引用,执行System.gc()被回收掉
垃圾回收相关概念_第4张图片 这种情况要注意,虽然buffer的作用域已经结束,但是执行方法后buffer并不会被回收,存到老年代
垃圾回收相关概念_第5张图片 要想回收buffer的话可以这样,现在buffer的槽就会被value占用了,如下所示

垃圾回收相关概念_第6张图片

垃圾回收相关概念_第7张图片 这种情况下,buffer会被回收掉,实际就是方法调用结束后的弹出栈操作

 

二、内存溢出与内存泄漏

首先理解一下概念

内存溢出(OOM)

垃圾回收相关概念_第8张图片

垃圾回收相关概念_第9张图片

注意上一段蓝色字体隐藏的意思:在抛出OutOfMemoryError之前,通常垃圾收集器会被处罚,尽其所能去清理出空间。

垃圾回收相关概念_第10张图片

内存泄漏(Memory Leak)

也称作“内存渗漏”。严格来说,只有对象不会再被程序用到了,但是GC又不能回收他们的情况,才叫内存泄漏

垃圾回收相关概念_第11张图片

举例内存泄漏的情况:

1.单例模式

单例的声明周期和应用程序是一样长的,所以单例程序中,如果持有对外部对象的引用的话,那么这个外部对象是不能被回收的,则会导致内存泄漏的产生。

2.一些提供close的资源未关闭导致的内存泄漏

数据库连接(dataSource.getConnection()),网络连接(socket)和IO连接必须手动close,负责是不能被回收的。

三、Stop The World

简称STW,指的是GC事件发生过程中,会产生应用程序的停顿。停顿产生时整个应用程序线程都会被暂停,没有任何响应,有点像卡死的感觉,这个停顿成为STW。

  • 可达性分析算法中枚举根节点(GC Roots)会导致所有Java执行线程停顿。因为分析工作必须在一个能确保一直性的快照中进行,此外,一致性指整个分析期间整个执行系统看起来像被冻结在某个时间点上,如果出现分析过程中对象引用关系还在不断变化,则分析结果的准确性无法保证。

被STW中断的应用程序线程会在完成GC之后恢复,频繁中断会让用户感觉像是网速不快造成电影卡带一样,所以我们需要减少STW的发生。

STW事件和采用哪款GC无关,所有的GC都有这个事件。哪怕是G1也不能完全避免STW的情况发生,只能说垃圾回收器越来越优秀,回收效率越来越高,尽可能地缩短了暂停时间。

STW是JVM在后台自动发起和自动完成的,在用户不可见的情况下,把用户正常的工作线程全部停掉。

开发中不要用System.gc(),会导致STW的发生。

四、垃圾回收的并行与并发

首先回忆一下程序中的并行与并发。

并行:指在同一时刻,有多条指令在多个处理器上同时执行。所以无论从微观还是从宏观来看,二者都是一起执行的。决定并行的因素不是CPU的数量,而是CPU的核心数量,比如一个CPU多个核也可以并行。

并发:指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。

二者对比

并发,指的是多个事情,在同一时间段内同时发生了。

并行,指的是多个事情,在同一时间点上同时发生了。

并发的多个任务之间是互相抢占资源的。

并行的多个任务之间是不互相抢占资源的。

只有在多CPU或者CPU多核的情况下,才会发生并行。否则,看似同时发生的事情,其实都是并发执行的。

并发和并行,在谈论垃圾收集器的上下文语境中,它可以解释如下:

并行:指多条垃圾收集线程并行工作,但此时用户线程仍处于等待状态。

串行:相较于并行的概念,单线程执行。如果内存不够,则程序暂停,启动JVM垃圾回收器进行垃圾回收。回收完,再启动程序的线程。

垃圾回收相关概念_第12张图片

并发:指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),垃圾回收线程在执行时不会停顿用户程序的运行。如CMS、G1

垃圾回收相关概念_第13张图片

五、安全点与安全区域

安全点

程序执行时并非所有地方都能停下来开始GC,只有在特定的位置才能停顿下来开始GC,这些位置成为“安全点”。

垃圾回收相关概念_第14张图片

如何在GC发生时,检查所有线程都跑到最近的安全点停顿下来呢?

垃圾回收相关概念_第15张图片

安全区域

垃圾回收相关概念_第16张图片

安全区域是指在一段代码片段中,对象的引用关系不会发生变化,在这个区域中的任何位置开始GC都是安全的。我们也可以吧Safe Region看做是被扩展了的Safepoint。

实际执行时:

  1. 当线程运行的Safe Region的代码时,首先标识已经进入Safe Region,如果这段时间发生GC,JVM会忽略标识为Safe Region状态的线程;
  2. 当线程即将离开Safe Region时,会检查JVM是否已经完成GC,如果完成,则继续进行,否则线程必须等待直到收到可以安全离开Safe Region的信号为止。

六、引用

再谈引用

垃圾回收相关概念_第17张图片

基本概念

垃圾回收相关概念_第18张图片

1.强引用:不回收

在Java程序中,最常见的引用类型是强引用(普通系统99%以上都是强引用),也就是我们最常见的普通对象引用,也是默认的引用类型

当在Java语言中使用new操作符创建一个新对象,并将其赋值给一个变量的时候,这个变量就成为指向该对象的一个强引用。

强引用的对象是可触及的,垃圾回收器就永远不会回收掉被引用的对象

对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应引用复制为null,就是可以当做垃圾被收集了,当然具体回收时机还是要看垃圾收集策略。

相对的,软引用,弱引用和虚引用是软可触及、弱可触及和虚可触及的,在一定条件下都是可以被回收的。所以,强引用是造成Java内存泄漏的主要原因之一。

垃圾回收相关概念_第19张图片

2.软引用:内存不足即回收

软引用是用来描述一些还有用,但非必须的对象。只被软引用引用着的对象,在系统将于发生内存溢出异常之前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。

垃圾回收相关概念_第20张图片

在JDK1.2版之后提供java.lang.ref.SoftReference类来实现软引用。

垃圾回收相关概念_第21张图片

3.弱引用:发现即回收

弱引用也是用来描述那些非必须对象,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。在系统GC时,只要发现弱引用,不管系统堆空间使用是否充足,都会回收掉只被弱引用关联的对象。

但是由于垃圾回收器的线程通常优先级很低,因此并不一定能很快地发现持有弱引用的对象,在这种情况下,弱引用对象可以存在较长时间

垃圾回收相关概念_第22张图片

垃圾回收相关概念_第23张图片

弱引用对象与软引用对象最大的不同就在于,当GC在进行回收时,需要通过算法检查是否回收软引用对象,而对于弱引用对象,GC总是进行回收。弱引用对象更容易、更快被回收

面试题:你开发中使用过WakHashMap吗?底层是使用了WeakReference。

4.虚引用:对象回收跟踪

垃圾回收相关概念_第24张图片

为一个对象设置虚引用关联的唯一目的在于跟踪垃圾回收过程。比如:能在这个对象被收集器回收时收到一个系统通知

虚引用必须和引用队列一起使用。虚引用在创建时必须提供一个引用队列作为参数。大概垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象后,将这个虚引用加入引用队列,以通知应用程序对象的回收情况。

由于虚引用可以跟着对象的回收时间,因此,可以将一些资源释放操作放置在虚引用中执行和记录

垃圾回收相关概念_第25张图片

5.终结器引用

垃圾回收相关概念_第26张图片

 

你可能感兴趣的:(Java底层原理,jvm,java,编程语言)