垃圾回收相关概念

System.gc()

通过System.gc() 或者 Runtime.getRuntime().gc() 的调用,会显示地触发FGC
System.gc()调用附带一个免责声明,无法保证对垃圾收集器的调用(不能确保垃圾收集器的执行时间)
System.runFinalization(); //强制调用失去引用的对象的finalize()方法

内存溢出

没有空闲内存,并且垃圾收集器也无法提供更多的内存

内存泄漏(Memory Leak)

对象不会再被程序用到了,但是GC又不能回收他们的情况,叫内存泄漏
很多时候一些不太好的实践(或疏忽)会导致对象的生命周期变得很长甚至导致OOM,也可以叫做宽泛意义上的“内存泄漏”
内存泄漏并不会立刻引起程序崩溃,但是一旦发生内存泄漏,程序中的可用内存会被逐步蚕食,最终导致OOM

举例:

  • 单例模式:单例的生命周期和应用程序是一样长的,所以单例程序中,如果持有对外部对象的引用的话,那么这个为外部对象是不能被回收的,则会导致内存泄漏的产生
  • 一些提供close的资源未关闭导致内存泄漏:数据库连接(dataSource.getConnection())、网络连接(socket)和io连接必须手动close,否则不能被回收

 

 

STW

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

 

并发(Concurrent)

并发是几个线程在同一个处理器上运行,并发并不是真正意义上的“同时进行”。只是采用了时间片算法
由于CPU处理的速度非常快,只要时间间隔处理得当,即可让用户感觉是多个应用程序同时在进行

 

并行(Parallel)

真正意义上的“同时进行”
决定并行的因素并不是CPU的数量,而是CPU的核心数量,比如一个CPU多个核也可以并行

 

垃圾回收的并行与并发

垃圾回收的并行:多条GC线程并行工作,此时用户线程处于等待状态
垃圾回收的串行:单线程执行GC,此时用户线程处于等待状态
垃圾回收的并发:用户线程与GC线程交替执行

 

安全点(Safepoint)

程序执行时并非所有地方都能停顿下来开始GC,只有特定位置才能停顿下来开始GC,这些位置称为“安全点”
安全点太少可能导致GC等待时间太长,太多可能导致运行时的性能问题。大部分指令的执行时间都非常短暂,通常根据“是否具有让程序长时间执行的特征”为标准选择安全点,比如:方法调用、循环跳转、异常跳转等

如何在GC发生时,检查所有线程都跑到最近的安全点:
抢先式中断(目前没有虚拟机采用):首先中断所有线程。如果还有线程不在安全点,就恢复线程,让线程跑到安全点再次中断
主动式中断:设置一个中断标志,各个线程运行到安全点的时候主动轮询这个标志,如果中断标志为真,则将直接进行中断挂起

 

安全区域(Safe Region)

当程序“不执行”的时候,例如线程处于sleep状态或blocked状态,这时候线程无法响应JVM的中断请求,“走”到安全点去中断挂起,JVM也不太可能等待线程被唤醒,这时就需要安全区域来解决
安全区域是指在一段代码片段中,对象的引用关系不会发生变化,在这个区域中的任何位置开始GC都是安全的。
实现细节:
当线程运行到安全区域的代码时,首先标识已经进入安全区域,如果这段时间发生GC,JVM会忽略标识为安全区域状态的线程
当线程即将离开安全区域时,会检查JVM是否已经完成GC,如果完成了则继续运行,否则线程必须等待直到收到可以安全离开可以离开安全区域的信号为止

 

引用

我们希望有这样一类对象:当内存空间足够时,这些对象可以保留在内存中;如果进行GC后内存还是很紧张,则可以抛弃这些对象
强引用(String Reference):赋值引用。无论任何情况下,只要强引用关系还在,就永远不会被GC
软引用(Soft Reference):系统将要发生内存溢出之前,将会把这些对象列入回收范围之中进行第二次回收。如果这次回收还没有足够的内存,则抛OOM
弱引用(Weak Reference):无论内存空间是否足够,都会被GC(一定被GC)
虚引用(Phantom Reference):虚引用不会对对象的生存时间构成影响,无法通过虚引用获得一个对象的实例。虚引用关联的目的是这个对象被GC时会收到一个系统通知
这四种引用强度依次减弱

 

软引用通常用来实现内存敏感的的缓存,比如:高速缓存就有用到软引用。

//创建软引用对象
SoftReference<User> userSoftRef = new SoftReference<User>(new User());
//从软引用中重新获得强引用对象
System.out.println(userSoftRef.get());

  

 为一个对象设置虚引用关联的目的是跟踪垃圾回收过程

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

Object obj = new Object();
ReferenceQueue queue = new ReferenceQueue();
PhantomReference<Object> pf = new PhantomReference<Object>(obj, queue);
obj = null;

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

无法通过虚引用来获取被引用的对象。通过虚引用的get()方法返回的总是null

 

终结器引用(Final Reference)
用于实现对象的finalize()方法
GC时,终结器引用入队,由Finalizer线程通过终结器引用找到被引用对象并调用它的finalize()方法

 

垃圾回收性能指标

吞吐量:用户程序运行时间与总运行时间的比例(总运行时间=用户程序运行时间+内存回收时间)
垃圾收集开销:垃圾回收时间与总运行时间的比例
暂停时间:执行垃圾收集时,用户线程被暂停的时间(STW的时间)
内存占用:堆区所占用的内存大小
快速:一个对象从诞生到被回收所经历的时间
收集频率:垃圾回收操作发生的频率

 

显示GC日志参数

-XX:+PrintGC   输出GC日志。类似 -verbose:gc
-XX:+PrintGCDetails   输出GC详细日志
-XX:+PrintGCTimesStamps   输出GC时间戳(以基准时间的形式)
-XX:+PrintGCDateStamps   输出GC的时间戳(以日期的形式)
-XX:+PrintHeapAtGC   在进行GC的前后打印出堆的信息
-Xloggc:../logs/gc.log   日志文件的输出路径

 

 

posted @ 2021-04-28 11:01  455994206  阅读(69)  评论(0)    收藏  举报