前言
每次说起Java的进阶学习,总是绕不过jvm这个话题。在jvm学习的开篇中,首先学到的就是jvm内存结构,然后就是gc垃圾回收机制。但对于许多日常开发来说,学习jvm内存结构之后,还能知道使用Xms、Xmx来调整heap大小,而学习GC可能对开发的帮助不太明显。
灵感一闪
但是通过gc,可以更好的定位程序运行中的问题。为什么这么说呢,前两天遇到了一个问题,消费kafka解析数据,因为数据量突增,导致部分主机上消费kafka一直积压,一共积压了80亿条数据,为了将这部分数据消费掉,增加主机的同时,每台主机也增加了一个进程。
同时对于kafka中topic的分区也做了调整,从160增加到200。上面的所有操作目的都是提高并发,最后效果也是显而易见,整体消费积压在逐步减少,但是有些分区积压并未减少。
我登录主机查看进程还在,查看日志发现部分线程数据解析量1200w/min,有的2w/min,但是一台主机上两个进程的日志都记录在了同一个log文件中,无法区分到底是哪个进程出现了问题。
这时候学习gc的优势就体现出来了,我先使用jps找到两个进程对应的PID,然后 jstat -histo 分别查看两个进程的GC情况。其中一个进程FGC的次数为0,一个FGC次数已经200多了。
所以我断定第二个进程有问题,通过ps查看启动时间,这个进程是最早启动的进程,因为那时候还没有提高并发,大量的原始数据在被读进了jvm的heap之后,程序没有足够的解析能力,导致数据一直存放在内部queue中,最后触发Full GC导致STW,从而暂停所有应用程序线程,所以最终现象就是程序解析能力下降。
GC
上面说了那么多,其实就是为了讲明白一件事:学习GC有用。那么该如何学习GC呢?GC的学习主要从以下几个方面开始。
什么是GC
GC 通常指的是“垃圾回收”(Garbage Collection),这是一种自动管理内存的机制,主要用于编程语言中。它的主要作用是自动检测和释放不再使用的内存,从而避免内存泄漏和提高程序的稳定性。
在Java中,不需要开发者手动释放内存,GC 会定期检查内存中不再被引用的对象,并将其占用的内存空间回收,以便存放新增的对象。
上面提到的内存,指得就是jvm的内存区域。很多人都知道jvm的区域划分为堆(heap)、方法区(java8中移除方法区、并修改为使用内存的metaspace)、虚拟机栈等。而 GC 针对的区域主要是heap。
gc分类
在 Java 中,我们听到最多的就是Young GC(Minor GC) 和 Full GC。直接从字面意思理解,YGC就是对年轻代(Young)进行垃圾回收,FGC就是对新生代和老生代全部(Full)进行垃