垃圾回收的相关感念

一、System.gc()的理解

调用会触发Full GC,进行垃圾回收释放内存,无法保证对垃圾收集器的调用,无法确定执行时间。

二、内存溢出和内存泄漏

内存溢出(OOM):没有空间内存,垃圾回收器也无法提供更多的可用内存。可能是堆内存设置不够或者是代码问题。在OOM之前一般情况下都会调用一次GC,当GC后空间还是不足才抛出OOM异常,当然也不是任何情况都调用,当出现一个超大对象,并且超过堆的最大值,那么JVM判断GC也无法解决问题,直接抛出OOM。

内存泄漏(Memory Leak):只有对象不再被使用了,但是GC又无法回收他们,才叫内存泄漏。但是某些时候,由于编写习惯不好,导致某些对象的生命周期很长,那么也可以成为内存泄漏。内存泄漏积累起来,可能会导致内存溢出。

1、单例模式:单例模式生命周期和应用程序一样长的,所以单例程序使用过程中,如果有对外部对象的引用的话,那么这个外部对象是不可能会被回收的,则会导致内存泄漏

2、一些提供clase的资源未被管理导致内存泄漏:数据库连接、网络连接、io连接必须手动close,否则不会回收

三、Stop The World

STW:发生GC的时候,会导致用户线程停顿,导致应用程序没有任何相应,进入卡死状态。因为垃圾回收的时候需要确定GC Roots,如果GC Roots不能变,所以GC的时候就需要STW,其实就是确保数据的一致性。所有的GC实践都会导致STW,只能说缩短STW的时间和减小出现频率。

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

1、程序中的并行和并发

并发(Concurrent):在一个时间段中,已启动和完毕之间,且这几个程序都在一个处理器上运行。一个CUP快速切换多个任务,并不是真正的同时执行,只不过依赖于CPU的快速切换

并行(Parallel):当多个CPU核心的时候,一个CPU核心执行一个进程在执行。

二者的对比:

并行:在一个时间段内发生了,相互之间抢占资源

并行:在一个时间点上发生了,相互之间不抢占资源

2、垃圾回收期中并行和并发

并行:多个垃圾回收线程并行工作

穿行:一个垃圾线程工作

并发:垃圾回收线程和用户线程同时执行 

五、安全点与安全区域

安全点:程序在执行的时候,并非所有的时间点都能停止,只有在特定的位置才能停下来,这时候才能执行GC。安全点太少则GC时间很长,太多则影响性能。选择规则:是否具有让程序长时间执行的特征。

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

  • 抢先式中断:首先中断所有线程,如果线程不在安全点,就恢复,让线程跑到安全点。
  • 主动式中断:设置一个中断标志位,各个线程运行到安全点的时候主动轮训这个标志位,判断是否需要挂起

安全区域:因为线程存在不执行的时候,无法响应JVM的中断请求。一个代码片段,对象的引用关系不会发生变化,则可以设置为安全区。

执行过程:

  • 当线程运行到安全区,首先标识已经进入安全区,如果发生GC的时候,JVM会忽略该线程的状态
  • 当线程即将离开安全区,会检查JVM是否完成GC,如果完成了,则继续运行,否则线程必须等待直到收到可以离开信号为止。

六、再谈引用

当内存空间还足够的时候,则保留,如果内存空间进行垃圾回收后还很紧张,就抛弃这些对象。

1、强引用 - 永远不回收

最常用的引用,无论什么情况,只要强引关系存在,GC就不会回收被引用的对象。平时使用的99%情况就是强引用。

Object obj = new Object(); obj就是强引用。强引用对象是可达的,GC永远不会回收。所以强引用也是java内存泄漏的主要原因之一。

2、软引用 - 内存不足则回收

内存溢出之前,将会把这些对象放入会后的范围之中,进行二次回收(第一次回收是垃圾)。缓存数据,保证正确执行(mybatis中就有一些结构使用软引用)。下面的代码需要把强引用消除,弱引用才能生效。

3、弱引用 - 只要GC就回收

只要GC,就会回收,跟软应用一样,可以用作缓存。

 

回收的时候相对更容易、速度更快,因为不需要任何判断。

WeakHashMap -> 可以使用缓存,因为使用弱引用,避免了强引用OOM问题。

4、虚引用 - GC通知

对象虚引用是否存在,不影响对象,也不会影响其生命周期,唯一目的就是能在这个对象被GC后接受到一个系统通知,也就是用于对象GC跟踪的。通过虚引用也无法获取到对象。

5、终结器引用

实现finalize()方法,也可以成为终结器引用。

猜你喜欢

转载自blog.csdn.net/liming0025/article/details/121727573