wordpress关闭站点,wordpress横幅广告,创建网站平台,电气工程WordPress模板什么是JVM的垃圾回收#xff1f;
垃圾回收#xff08;Garbage Collection, GC#xff09;是Java虚拟机#xff08;JVM#xff09;自动管理内存的一种机制#xff0c;其目的是自动回收不再使用的对象所占用的内存空间#xff0c;以防止内存泄漏和提升内存利用效率。
运…什么是JVM的垃圾回收
垃圾回收Garbage Collection, GC是Java虚拟机JVM自动管理内存的一种机制其目的是自动回收不再使用的对象所占用的内存空间以防止内存泄漏和提升内存利用效率。
运行时数据区
运行时数据区即JVM的内存区域其中包含程序计数器、java虚拟机栈、本地方法栈、方法区、堆区。
其中程序计数器、java虚拟机栈、本地方法栈线程私有所占内存会随线程的结束而回收因此在大多数情况下不需要特别关注它们的垃圾回收。
方法区和堆区是线程共享的垃圾回收的重点主要在这两个区域尤其是堆区。 方法区的垃圾回收
方法区的主要内容
类元数据存储类的结构信息如字段、方法、接口等。运行时常量池存储类文件中的常量池包括字面量和符号引用的运行时表示。静态变量类的静态字段属于类而非实例。即时编译器编译后的代码存储JIT编译器生成的本地代码。
垃圾回收的主要目标
1. 类卸载
原因当一个类加载器被卸载时所有由该加载器加载的类也应该被卸载以释放内存。条件要卸载一个类必须确保该类的所有实例都已经被回收并且没有对该类的任何引用包括反射引用存在。过程可达性分析 标记阶段标记所有可以访问的类。清除阶段清除没有标记到的类释放其占用的内存。困难类卸载相对较为复杂因为需要确保没有剩余的引用这在长时间运行的应用中可能较难达成。
2. 运行时常量池回收
原因运行时常量池中的常量可能会大量增加特别是字符串常量和符号引用。过程可达性分析 标记阶段标记所有可以访问的常量。清除阶段清除未被标记的常量释放内存。
引用计数法和可达性分析法
1.引用计数法
引用计数法会为每个对象维护一个引用计数器当对象被引用时加1取消引用时减1。
引用计数法的优点是实现简单C中的智能指针就采用了引用计数法但是它也存在缺点主要有两点
每次引用和取消引用都需要维护计数器对系统性能会有一定的影响存在循环引用问题所谓循环引用就是当A引用BB同时引用A时会出现对象无法回收的问题。
2.可达性分析法
可达性分析法Reachability Analysis是判断对象是否可被回收的一种算法它通过一系列称为“GC Roots”的对象作为起点沿着这些起点引用的路径进行遍历。如果一个对象在遍历过程中不可达即没有任何引用链能从GC Roots到达该对象则该对象被认为是不可达的可以被回收。
GC Roots包括 活动线程 所有存活的线程对象包括各个线程的栈帧中的本地变量表中的引用即当前活动线程的所有局部变量。 方法区中的静态引用 类静态字段引用的对象即由方法区中的类的静态变量所引用的对象。 方法区中的常量引用 运行时常量池中的常量引用例如字符串常量池中的字符串对象。 本地方法栈中的引用 JNI中的本地引用包括JNI接口通过NewGlobalRef创建的全局引用。 同步锁定的对象 当前被锁定的对象即被同步块或方法锁定的对象因为这些对象在持有者线程中被引用。
这些GC Roots构成了对象图中最初始的可达节点从这些节点开始垃圾回收器将追踪所有可达对象。如果一个对象未能从这些GC Roots到达则说明它是不可达的可以被垃圾回收。每次GC都会从这些根节点出发确定哪些对象存活并需要保留哪些对象可以被回收以释放内存
方法区垃圾回收的特点
回收频率低方法区的垃圾回收频率低于堆区因为方法区中的数据变动相对较少。回收复杂度高类卸载和常量池的回收需要精确管理引用确保没有遗漏复杂度较高。影响性能方法区的垃圾回收可能会对性能产生较大影响尤其是在需要频繁加载和卸载类的情况下。
JVM对方法区垃圾回收的支持
永久代PermGen在JDK 7及之前方法区位于永久代。永久代的垃圾回收由Full GC执行。元空间Metaspace在JDK 8及之后方法区改为元空间。元空间是直接使用本地内存而不是堆内存垃圾回收机制有所改进。
总结
方法区的垃圾回收虽然不如堆区频繁但依然是JVM内存管理的重要部分尤其是在长时间运行和高动态性的应用中。主要涉及类卸载和常量池的回收确保内存的有效利用和防止内存泄漏。 常见的引用方式
可达性算法中描述的对象引用一般指的是强引用即是GCRoot对象对普通对象有引用关系只要这层关系存在普通对象就不会被回收。除了强引用之外Java中还设计了几种其他引用方式 软引用 弱引用 虚引用 终结器引用
软引用
软引用相对于强引用是一种比较弱的引用关系如果一个对象只有软引用关联到它当程序内存不足时就会将软引用中的数据进行回收。在JDK 1.2版之后提供了SoftReference类来实现软引用软引用常用于缓存中。
这样做有什么好处如果对象A是一个缓存平时会保存在内存中如果想访问数据可以快速访问。但是如果内存不够用了我们就可以将这部分缓存清理掉释放内存。即便缓存没了也可以从数据库等地方获取数据不会影响到业务正常运行这样可以减少内存溢出产生的可能性。 特别注意 软引用对象本身也需要被强引用否则软引用对象也会被回收掉。 软引用的使用方法
软引用的执行过程如下
1.将对象使用软引用包装起来new SoftReference对象类型(对象)。
2.内存不足时虚拟机尝试进行垃圾回收。
3.如果垃圾回收仍不能解决内存不足的问题回收软引用中的对象。
4.如果依然内存不足抛出OutOfMemory异常。
软引用对象本身怎么回收呢
如果软引用对象里边包含的数据已经被回收了那么软引用对象本身其实也可以被回收了。
SoftReference提供了一套队列机制
软引用创建时通过构造器传入引用队列在软引用中包含的对象被回收时该软引用对象会被放入引用队列通过代码遍历引用队列将SoftReference的强引用删除
示例
public class SoftReferenceDemo3 {public static void main(String[] args) throws IOException {ArrayListSoftReference softReferences new ArrayList();ReferenceQueuebyte[] queues new ReferenceQueuebyte[]();for (int i 0; i 10; i) {byte[] bytes new byte[1024 * 1024 * 100];SoftReference studentRef new SoftReferencebyte[](bytes,queues);softReferences.add(studentRef);}SoftReferencebyte[] ref null;int count 0;while ((ref (SoftReferencebyte[]) queues.poll()) ! null) {count;}System.out.println(count);}
}
弱引用
弱引用的整体机制和软引用基本一致区别在于弱引用包含的对象在垃圾回收时不管内存够不够都会直接被回收。在JDK 1.2版之后提供了WeakReference类来实现弱引用弱引用主要在ThreadLocal中使用。
虚引用和终结器引用
这两种引用在常规开发中是不会使用的。 虚引用也叫幽灵引用/幻影引用不能通过虚引用对象获取到包含的对象。虚引用唯一的用途是当对象被垃圾回收器回收时可以接收到对应的通知。Java中使用PhantomReference实现了虚引用直接内存中为了及时知道直接内存对象不再使用从而回收内存使用了虚引用来实现。 终结器引用指的是在对象需要被回收时终结器引用会关联对象并放置在Finalizer类中的引用队列中在稍后由一条由FinalizerThread线程从队列中获取对象然后执行对象的finalize方法在对象第二次被回收时该对象才真正的被回收。在这个过程中可以在finalize方法中再将自身对象使用强引用关联上但是不建议这样做。
堆区的垃圾回收
堆区的垃圾回收GC是Java虚拟机JVM内存管理的核心部分涉及到清理不再被使用的对象以释放内存。
1. 堆区的内存结构
堆区是JVM内存的一部分专门用于存储所有对象实例。通常堆区被分为以下几个区域 年轻代Young Generation 存储新创建的对象。通常分为三个部分 Eden区对象首先分配到Eden区。Survivor区S0和S1Eden区经过垃圾回收后存活的对象会被移动到Survivor区的一个区域。Survivor区有两个部分S0和S1它们交替使用。 老年代Old Generation 存储经过多次垃圾回收仍然存活的对象。老年代的垃圾回收频率较低因为对象在这里的生命周期较长。 永久代PermGenJava 8之前版本存在后被元空间取代元空间存在于直接内存不在堆中 存储类元数据、常量池、方法区等信息的一部分内存区域。永久代存在于JVM堆内存中但其管理和内存回收机制与堆区的其他部分有所不同。
2. 垃圾回收算法
垃圾回收器使用不同的算法来管理内存。常见的算法包括 标记-清除算法Mark-and-Sweep 标记从GC Roots开始遍历并标记所有可达对象。清除回收所有未被标记的对象。优点简单直观。缺点清除后会产生内存碎片可能导致内存空间不连续。 复制算法Copying .准备两块空间From空间和To空间每次在对象分配阶段只能使用其中一块空间From空间.在垃圾回收GC阶段将From中存活对象复制到To空间将两块空间的From和To名字互换。优点高效的垃圾回收减少了碎片。缺点需要额外的内存空间作为备用区域。 标记-整理算法Mark-Compact 结合了标记-清除和复制算法的优点。首先标记所有可达对象然后将这些对象整理到内存的一端最后清理未被使用的内存。优点消除了碎片问题。缺点整理过程可能比较复杂和耗时。 分代收集算法Generational Collection 根据对象的年龄将堆区划分为不同的代。年轻代和老年代使用不同的回收算法年轻代通常使用复制算法而老年代使用标记-整理算法。优点提高了回收效率减少了老年代GC的频率。缺点管理多个代的复杂性。
3. 垃圾回收器
由于垃圾回收器分为年轻代和老年代除了G1之外其他垃圾回收器必须成对组合进行使用。具体的关系图如下 1. 年轻代-Serial垃圾回收器
Serial是是一种单线程串行回收年轻代的垃圾回收器。 使用的算法 年轻代复制算法。 优点 单CPU处理器下吞吐量非常出色 缺点 多CPU下吞吐量不如其他垃圾回收器堆如果偏大会让用户线程处于长时间的等待 适用场景 Java编写的客户端程序或者硬件配置有限的场景 2. 老年代-SerialOld垃圾回收器
SerialOld是Serial垃圾回收器的老年代版本采用单线程串行回收
-XX:UseSerialGC 新生代、老年代都使用串行回收器。 使用的算法 老年代标记-整理算法 优点 单CPU处理器下吞吐量非常出色 缺点 多CPU下吞吐量不如其他垃圾回收器堆如果偏大会让用户线程处于长时间的等待 适用场景 与Serial垃圾回收器搭配使用或者在CMS特殊情况下使用 3. 年轻代-ParNew垃圾回收器
ParNew垃圾回收器本质上是对Serial在多CPU下的优化使用多线程进行垃圾回收
-XX:UseParNewGC 新生代使用ParNew回收器 老年代使用串行回收器 使用的算法 年轻代复制算法。 优点 多CPU处理器下停顿时间较短 缺点 吞吐量和停顿时间不如G1所以在JDK9之后不建议使用 适用场景 JDK8及之前的版本中与CMS老年代垃圾回收器搭配使用 4. 老年代- CMS(Concurrent Mark Sweep)垃圾回收器
CMS垃圾回收器关注的是系统的暂停时间允许用户线程和垃圾回收线程在某些步骤中同时执行减少了用户线程的等待时间。
参数XX:UseConcMarkSweepGC 使用的算法 老年代标记清除算法 优点 系统由于垃圾回收出现的停顿时间较短用户体验好 缺点 1、内存碎片问题 2、退化问题 3、浮动垃圾问题 适用场景 大型的互联网系统中用户请求数据量大、频率高的场景比如订单接口、商品接口等 CMS执行步骤 1.初始标记用极短的时间标记出GC Roots能直接关联到的对象。 2.并发标记, 标记所有的对象用户线程不需要暂停。 3.重新标记由于并发标记阶段有些对象会发生了变化存在错标、漏标等情况需要重新标记。 4.并发清理清理死亡的对象用户线程不需要暂停。 5. 年轻代-Parallel Scavenge垃圾回收器
Parallel Scavenge是JDK8默认的年轻代垃圾回收器多线程并行回收关注的是系统的吞吐量。具备自动调整堆内存大小的特点。 使用的算法 年轻代复制算法。 优点 吞吐量高而且手动可控。为了提高吞吐量虚拟机会动态调整堆的参数 缺点 不能保证单次的停顿时间 适用场景 后台任务不需要与用户交互并且容易产生大量的对象。比如大数据的处理大文件导出 常用参数 Parallel Scavenge允许手动设置最大暂停时间和吞吐量。Oracle官方建议在使用这个组合时不要设置堆内存的最大值垃圾回收器会根据最大暂停时间和吞吐量自动调整内存大小。 最大暂停时间-XX:MaxGCPauseMillisn 设置每次垃圾回收时的最大停顿毫秒数 吞吐量-XX:GCTimeRation 设置吞吐量为n用户线程执行时间 n/n 1 自动调整内存大小, -XX:UseAdaptiveSizePolicy设置可以让垃圾回收器根据吞吐量和最大停顿的毫秒数自动调整内存大小 6. 老年代-Parallel Old垃圾回收器
Parallel Old是为Parallel Scavenge收集器设计的老年代版本利用多线程并发收集。
参数 -XX:UseParallelGC 或
-XX:UseParallelOldGC可以使用Parallel Scavenge Parallel Old这种组合。 使用的算法 老年代标记-整理算法 优点 并发收集在多核CPU下效率较高缺点 暂停时间会比较长 适用场景 与Parallel Scavenge配套使用 7. G1 – Garbage First 垃圾回收器
参数1 -XX:UseG1GC 打开G1的开关JDK9之后默认不需要打开
参数2-XX:MaxGCPauseMillis毫秒值 最大暂停的时
回收年代和算法 年轻代老年代复制算法 优点 对比较大的堆如超过6G的堆回收时延迟可控不会产生内存碎片并发标记的SATB算法效率高缺点 JDK8之前还不够成熟 适用场景 JDK8最新版本、JDK9之后建议默认使用
总结
垃圾回收器的组合关系虽然很多但是针对几个特定的版本比较好的组合选择如下
JDK8及之前
ParNew CMS关注暂停时间、Parallel Scavenge Parallel Old (关注吞吐量)、 G1JDK8之前不建议较大堆并且关注暂停时间
JDK9之后:
G1默认