面试官:你简历上写精通JVM,那你给我说一下垃圾回收的相关概念吧
前言
对于 JVM 来说,我们都不陌生,其是 Java Virtual Machine(Java 虚拟机)的缩写,它也是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。JVM 有自己完善的硬件架构,如处理器、堆栈等,还具有相应的指令系统,其本质上就是一个程序,当它在命令行上启动的时候,就开始执行保存在某字节码文件中的指令。
什么是垃圾
在 JVM 进行垃圾回收之前,首先就是判断哪些对象是垃圾,也就是说,要判断哪些对象是可以被销毁的,其占有的空间是可以被回收的。根据 JVM 的架构划分,我们知道, 在 Java 世界中,几乎所有的对象实例都在堆中存放,所以垃圾回收也主要是针对堆来进行的。
内存溢出与内存泄露
原理:没有空闲内存、并且垃圾收集器也无法提供更多内存
除非应用好吃呢关系占用内存增长速度比垃圾回收速度快才会导致OOM问题(实在不行还可以Full GC),所以不容易出现这个MMO
没有空闲内存的情况有两个原因
- (1)Java虚拟机的堆内存设置不够
- 可能还会存在内存泄露
- -Xms -Xmx可以调整
- (2)代码中创建大量大对象,并且长时间不能被垃圾收集器收集(存在引用)
内存泄露(Memory Leak)
严格拉说,只有对象不会再被程序使用,但是GC又不能回收他们的情况,才称为内存泄露。
宽泛来说“内存泄露”,是一些不好的实践导致对象的声明周期很长甚至导致OOM。
注意 内存泄露只是指虚拟内存大小
内存泄露实例
1. 单例模式(单例的生命周期与类一样长(static)若单例引用外部对象,外部对象不用也不能回收,就会导致内存泄露)
2. 一些提供close资源未关闭导致内存泄露(如Connection(),Socket,IO对象)
Stop The World
GC过程中应用线程会被暂停,没有任何响应。
- 可达性分析算法中枚举根节点(GC Roots)会导致Java执行线程停顿
- 分析工作必须要一个确保一致性的快照中进行
- 一致性指整个期间整个执行系统看起来都被冻结在某个时间点上
- 如果分析过程中对象引用关系还在不断变化,则分析结果的准确性无法保证
- 被STW中断的应用程勋线程会在完成GC后恢复,要减少STW的发生。
- STW时间和财通哪款GC无关,所有的GC都有这个时间
- G1也不能避免STW
- STW是JVM后台自动发起自动完成的
- System.gc()会导致STW发生。
STW代码体验
public class StopTheWorldDemo {
public static class WorkThread extends Thread {
List<byte[]> list = new ArrayList<byte[]>();
public void run() {
try {
while (true) {
for(int i = 0;i < 1000;i++){
byte[] buffer = new byte[1024];
list.add(buffer);
}
if(list.size() > 10000){
list.clear();
System.gc();//会触发full gc,进而会出现STW事件
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
public static class PrintThread extends Thread {
public final long startTime = System.currentTimeMillis();
public void run() {
try {
while (true) {
// 每秒打印时间信息
long t = System.currentTimeMillis() - startTime;
System.out.println(t / 1000 + "." + t % 1000);
Thread.sleep(1000);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
public static void main(String[] args) {
WorkThread w = new WorkThread();
PrintThread p = new PrintThread();
w.start();
p.start();
}
}
垃圾回收的并行与并发
并发(Concurrent)
- 操作系统中,是只一个时间段中有几个程序都处于已启动运行到运行完毕之间,这几个程序都是处于同一个处理器上运行
- 并发不是真正意义上的同时进行,只是CPU吧一个时间段划分成几个时间片段(时间区间),然后在几个时间区域之间来回切换。
并行(Parallel)
- 系统有一个以上CPU执行一个进程是,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,就是并行
- 决定并行的因素促使CPU的数量,而是CPU核心数量,比如一个CPU多个核也可以并行、
并发与并行
- 并发,多个事情,在同一个时间段同时发生
- 并行,多个时间,在同一个时间点同时发生
垃圾回收的并发与并行
并行
- 并行(Parallel):指的是多个垃圾收集线程并行工作,当时此时用户线程处于等待状态。
- ParNew、Parallel Scavenge、Parallel Old
- 串行(Serial)
- 相比并行的概念,单线程执行
- 如果内存不够程勋暂停,启动JVM垃圾回收器继续垃圾回收,回收万在启动程勋的线程
并发
- 并发(Concurrent)指用户线程与垃圾收集线程同时执行(不一定是并行,可能交替执行,不会暂停用户线程)
- 用户程勋在继续执行,而垃圾收集程序线程运行在另一个CPU上
- 如CMS G1
安全点与安全区域
安全点(Safe Point)
并不是所有的地方都能停顿下来开始GC,只有在特定的位置才能停顿下来GC,这和个位置的点就成为安全点。
安全点的选择很重要,如果太少可能导致GC等待时间太上,如果太频繁会导致运行性能问题。
放生GC是,如何保证所有线程都跑到最近的安全点停顿下来。
-
抢断式中断:(目前没有虚拟机采用了)
- 首先中断所有线程,如果还有线程不在安全点,就恢复现场,让线程跑到安全点
-
主动式中断
- 设置一个中断标志,线程运行到Safe Point的时候主动轮询这个标志,如果中断标志威震,则将自己进行中断挂起。
安全区域(Safe Region)
安全区域是指一段代码片段中,对象的引用关系不会发生变化,在这个区域中的任何位置开始都是安全的。
执行过程
- 当线程到了安全区域的代码是,手边标识已经进入了安全区域,如果这段时间内放生GC,JVM会忽略标识为安全区域的线程
- 当线程即将离开安全区域时,会检查JVM是否已经完成GC,如果完成了,则继续执行,否则线程必须等待收到可以离开安全区域的信号为止
强引用(Strong Reference)
无论怎么都不回收(OOM也不回收)
- 默认的引用类型(直接访问目标对象)
- 强引用的对象是可触及的,垃圾收集器永远不会回收被引用的对象。
- 强引用就是造成Java内存泄露的主要原因之一。
软引用(Soft Reference)
在系统将要发生内存溢出之前,将会这些对象列入回收范围之中进行第二次回收,如果这次回收后还没有足够的内存,才会抛出内存溢出异常
-
内存不足既回收。
软引用实现
User u=new User();
SoftReference<User> sr=new SoftReference<> (u);//这里的sr就是软引用
弱引用 (Weak Reference)
被弱引用关联的对象只能生存到下一次垃圾回收收集之前,当垃圾收集工作时,无论内存空间是否足够,都会回收掉被引用关联的对象
- 非必须的对象引用
- 只要发生GC就回收弱引用
- 软引用、弱引用都非常适合来保存那些可有可无的缓存信息数据。
WeakHashMap的介绍
- WeakHashMap就是使用弱引用来存储数据,当内存不足的时候优先丢弃。
WeakReference wr=new WeakReference();
wr就是弱引用。
虚引用(Phantom Reference)
一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来复活的一个对象的实例,为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。
- 不决定对象的生命周期
- 可以将一下资源释放的操作放在虚引用中执行和记录。
终结器引用(Final Reference)
- 用以实现对象的finalize()方法,也可以成为终结器引用
- 无序手动编码,其内部配合引用队列使用
- 在GC时,终结器引用入队,由Finalizer线程通过终结器引用找到被引用对象并调用他的finalize)_方法,第二次GC时才能回收被引用对象。
最后
感谢你看到这里,看完有什么的不懂的可以在评论区问我,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!
上一篇: LeetCode|234.回文链表
下一篇: leetcode 234. 回文链表