垃圾收集策略与算法
程序计数器、虚拟机栈、本地方法栈随线程而生 , 也随线程而灭;栈帧随着方法的开始而入栈 , 随着方法的结束而出栈 。这几个区域的内存分配和回收都具有确定性 , 在这几个区域内不需要过多考虑回收的问题 , 因为方法结束或者线程结束时 , 内存自然就跟随着回收了 。
而对于 JAVA 堆和方法区 , 我们只有在程序运行期间才能知道会创建哪些对象 , 这部分内存的分配和回收都是动态的 , 垃圾收集器所关注的正是这部分内存 。
判定对象是否存活
若一个对象不被任何对象或变量引用 , 那么它就是无效对象 , 需要被回收 。
一 引用计数法(无法回收循环引用的对象)
在对象头维护着一个 counter 计数器 , 对象被引用一次则计数器 +1;若引用失效则计数器 -1 。当计数器为 0 时 , 就认为该对象无效了 。
引用计数算法的实现简单 , 判定效率也很高 , 在大部分情况下它都是一个不错的算法 。但是主流的 Java 虚拟机里没有选用引用计数算法来管理内存 , 主要是因为它很难解决对象之间循环引用的问题 。
例子:对象 objA 和 objB 都有字段 instance , 令 objA.instance = objB 并且 objB.instance = objA , 由于它们互相引用着对方 , 导致它们的引用计数都不为 0 , 于是引用计数算法无法通知 GC 收集器回收它们 。
二 可达性分析法(更好)
【JVM垃圾收集策略与算法】所有和 GC Roots 直接或间接关联的对象都是有效对象 , 和 GC Roots 没有关联的对象就是无效对象 。
GC Roots 是指:
- Java 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 本地方法栈中引用的对象
- 方法区中常量引用的对象
- 方法区中类静态属性引用的对象
- GC Roots 并不包括堆中对象所引用的对象 , 这样就不会有循环引用的问题 。
判定对象是否存活与“引用”有关 。在 JDK 1.2 以前 , Java 中的引用定义很传统 , 一个对象只有被引用或者没有被引用两种状态 , 我们希望能描述这一类对象:当内存空间还足够时 , 则保留在内存中;如果内存空间在进行垃圾手收集后还是非常紧张 , 则可以抛弃这些对象 。很多系统的缓存功能都符合这样的应用场景 。
文章插图
在 JDK 1.2 之后 , Java 对引用的概念进行了扩充 , 将引用分为了以下四种 。不同的引用类型 , 主要体现的是对象不同的可达性状态reachable和垃圾收集的影响 。
强引用(Strong Reference)
类似 "Object obj = new Object()" 这类的引用 , 就是强引用 , 只要强引用存在 , 垃圾收集器永远不会回收被引用的对象 。但是 , 如果我们错误地保持了强引用 , 比如:赋值给了 static 变量 , 那么对象在很长一段时间内不会被回收 , 会产生内存泄漏 。
软引用(Soft Reference)
软引用是一种相对强引用弱化一些的引用 , 可以让对象豁免一些垃圾收集 , 只有当 JVM 认为内存不足时 , 才会去试图回收软引用指向的对象 。JVM 会确保在抛出 OutOfMemoryError 之前 , 清理软引用指向的对象 。软引用通常用来实现内存敏感的缓存 , 如果还有空闲内存 , 就可以暂时保留缓存 , 当内存不足时清理掉 , 这样就保证了使用缓存的同时 , 不会耗尽内存 。
弱引用(Weak Reference)
弱引用的强度比软引用更弱一些 。当 JVM 进行垃圾回收时 , 无论内存是否充足 , 都会回收只被弱引用关联的对象 。
虚引用(Phantom Reference)
虚引用也称幽灵引用或者幻影引用 , 它是最弱的一种引用关系 。一个对象是否有虚引用的存在 , 完全不会对其生存时间构成影响 。它仅仅是提供了一种确保对象被 finalize 以后 , 做某些事情的机制 , 比如 , 通常用来做所谓的 Post-Mortem 清理机制 。
文章插图
回收堆中无效对象
对于可达性分析中不可达的对象 , 也并不是没有存活的可能 。
推荐阅读
- 如何收集转移财产的证据?
- 什么是JVM?
- 垃圾食品的制作过程
- 从阿里、头条面试回来,面试官最喜欢问的Jvm和Redis你了解多少?
- 垃圾桶图片 医疗废弃物专用垃圾桶属于医疗器械吗
- 数据库主机及数据库日志收集
- 什么是垃圾外链,如何辨别?
- 一线大厂JAVA面试精选:JVM+RabbitMQ+Mybatis+Redis+分布式+微服务
- Java必知必会:JVM是啥
- 你的苹果手机多久清理一次垃圾 苹果手机缓存怎么清理