JVM垃圾收集和优化

总览

在对系统进行性能相关问题的故障排除时,内存优化是一个需要深入分析每个系统在内存中存储的内容,存储时间和访问模式的地方。 这篇文章是要在背景信息上进行注释,并在此工作中要注意一些要点,这些工作要专门针对基于Java的实现,因为深入了解JVM行为在此过程中非常有益。

Java语言通过在很大程度上照顾内存管理,从而将重点放在其余逻辑上,为开发人员提供了许多便利。 仍然对Java到底是如何做的有一个很好的了解,合理化我们在Java实现中遵循的几种最佳实践,并帮助更好地设计程序,并认真思考某些方面,从长远来看,这些方面以后可能导致内存泄漏和系统稳定性。 Java Garbage Collector在这方面起着重要作用,它负责通过删除内存垃圾来释放内存。

这些信息可广泛获得,但我在这里汇总以供参考。 :)

JVM使Java代码能够以独立于硬件和OS的方式运行。 它在由操作系统充当物理机的另一种抽象的操作系统分配给自己的进程的内存位置上运行。

JVM可以基于[1]中发布​​的开放标准来实现,众所周知的实现是Oracle Hotspot JVM,几乎与Android OS中使用的开放源代码版本OpenJDK,IBM J9,JRockit和Dalvik VM有所不同。

简而言之,JVM使用从平台分配给它的资源来加载并执行已编译的Java字节代码,该JVM在其上运行。

类加载器

将字节码加载到JVM内存中(加载,链接(验证,准备,解决–>如果发出失败的NoClassDef发现异常),初始化),引导类加载器,扩展类加载器,应用程序类加载器

内存和运行时数据区

尽管它并不全面,但它涵盖了以下几个重要部分。

  • 本机方法堆栈– Java本机库堆栈,它与平台有关,主要是用C语言编写的。
  • JVM堆栈(每个线程保留当前执行的方法堆栈跟踪。如果未设置适当的中断,则递归方法调用可能导致堆栈被填充和溢出(java.lang.StackOverFlowError) 。-Xss JVM选项允许配置堆栈大小。),PC寄存器(程序计数器,指向每个线程要执行的下一条指令。)
  • 方法区域(存储类数据,大小受XX:MaxPermSize限制 ,PermGen空间默认为64MB,如果要服务于加载数百万个类的大型服务器应用程序,则可以通过增加调整来避免OOM问题:PermGen空间。来自Java 8)自此以后,尽管允许对其进行微调和限制,但默认情况下java8中将此PermGen空间称为没有限制的元空间),Heap(Xms,Xmx),运行时常量池

执行引擎

该引擎执行通过类加载器分配给运行时数据区域的字节码。 它利用解释器,垃圾收集器,热点分析器,JIT编译器来优化程序执行。

有关JVM体系结构的更多详细信息,请参见[2]。

现在我们知道了垃圾收集器在JVM体系结构中的位置。 让我们深入了解内部。

垃圾收集器

这是Java自动内存管理过程,它删除了不再使用的对象。 接下来是问题,它如何决定是否使用该对象。

它定义了两类对象,
活动对象 –从另一个对象引用的可访问对象。 最终,引用链接将到达根,该根是创建整个对象图的主线程。 死对象 –只是躺在堆中的其他对象未引用的不可访问对象。

此分类和垃圾回收基于以下两个事实。
1.大多数对象在创建后很快就变得无法访问。 通常,短暂对象仅存在于方法上下文中。 2.老物件很少指年轻物件。 例如,寿命长的缓存几乎不会从中引用较新的对象。

新创建的对象实例驻留在Java堆中,该堆可以进行不同的生成,如下所示。 垃圾收集是通过称为“垃圾收集器”的守护程序线程完成的,该线程将对象引导通过堆中的不同空间。
垃圾收集分3个步骤进行。

1.标记 –从根开始并遍历对象图,将可访问对象标记为活动对象。
2.扫描 –删除未标记的对象。 3.紧凑 –对内存进行碎片整理,使活动对象的分配连续。 这被认为是最耗时的过程。

堆区域划分如下。
旧的(终身使用的)代 –可以存活很长时间的对象,请留在这里,直到其被标记为无法访问并在遍及整个堆的主要垃圾收集中清理为止。
年轻一代 –进一步分为3个伊甸园空间和2个幸存者空间。 垃圾收集分为两个阶段:“次要”或“主要”。 这两个垃圾回收都是停止运行的操作,它们停止所有其他内存访问。 应用程序可能不会感觉到次要GC,因为它仅扫描年轻一代空间会很小。

垃圾收集器

内存生命周期如下图所示,如上图所示。
1.新创建的对象驻留在Eden空间中。 (就像人类从伊甸园开始的一样:)在伊甸园空间变满之前,它一直在不断增加新的物体。

2.当Eden空间已满时,将运行次要GC,标记活动对象,然后将这些活动对象移至“幸存者从”空间,然后扫掠空闲的Eden空间。

3.然后在程序运行时继续用新对象填充Eden空间。 现在,当Eden空间已满时,我们先前也已将“幸存者来自”空间中的对象移动了。 次要GC在这两个空间中运行标记对象,然后将剩余的活动对象整体移至其他幸存者空间。 想知道为什么不将有生命的物体从伊甸园空间复制到“幸存者从”的剩余空间,而不是全部转移到另一个幸存者空间? 好吧,事实证明,在紧凑的步骤中,将所有其他对象移到另一个对象上要比压缩其中带有对象的区域更为有效。

4.此循环将在幸存者空间之间重复移动对象,直到达到配置的阈值(-XX: MaxTenuringThreshold 。 (它跟踪每个对象生存了多少个GC循环)。 当达到阈值时,这些对象将被移至保管空间。

5.随着时间的流逝,如果使用权空间也被填满,则主GC将启动并遍历整个堆内存空间,以执行GC步骤。 这种暂停可以在人际互动中感觉到,这是不希望的。

当内存泄漏或长时间驻留大量高速缓存时,使用期限将被占用。 在这种时候,这些对象甚至可能没有被检测为死亡。 这会导致主要GC频繁运行,因为它检测到永久性空间已满,但是由于无法清除任何内容,因此无法清理足够的内存。

当内存不足时,日志中的错误“ java.lang.OutOfMemoryError”将清楚地提示我们。 另外,如果我们看到频繁使用大量内存的CPU频繁运行,则可能是由于某些内存处理问题需要引起注意而导致GC频繁运行的征兆。

在专注于JVM微调(专注于内存利用率)时,主要的决定因素是响应性/延迟吞吐量中更关键的因素。 如果在批处理中吞吐量是最重要的,那么如果可以提高主要吞吐量,我们可以在运行主要GC时暂停一下来妥协。 因为应用程序偶尔的响应速度可能不是那里的问题。
另一方面,如果像在基于UI的应用程序中一样,响应性至关重要,则应尝试避免使用大型GC。 也就是说,这样做并没有帮助。 例如,我们可以通过增加年轻一代的空间来延迟大型GC。 但是随后,小型GC将开始花费大量时间,因为它现在需要遍历并压缩巨大的空间。 因此,要拥有正确的尺寸,就需要谨慎地实现年轻人和老年人之间的正确比例。 有时,这甚至可以进入应用程序设计细节,以通过对象创建模式和缓存位置来微调内存使用情况。 这将是另一篇文章的主题,该文章分析堆转储和火焰图,以确定要缓存的最佳内容。

垃圾收集器

由于垃圾回收的作用对应用程序的性能有很大的影响,因此工程师们花费了大量的精力来改进它。 结果是,我们可以根据需求选择最佳的垃圾收集器。 以下是不完整的选项列表。

1.串行收集器

在单个线程中运行。 仅适用于基本应用。

一个线程执行垃圾回收。 它只会使世界处于标记和重新标记阶段。 其余的工作在应用程序运行时完成,并且不等待旧的版本已满。 当内存空间大,具有大量CPU来满足并发执行时,以及当应用程序要求最短的暂停时间和响应能力成为关键因素时,这是一个不错的选择。 过去,这是大多数Web应用程序中最受欢迎的。

3.并行收集器

该收集器使用多个CPU。 它等待旧的一代充满或接近充满,但是当它运行时,它停止了世界。 多个线程进行标记,清除和压缩,使垃圾收集更快。 当内存不是很大并且CPU数量受到限制时,这是一个很好的选择,可以满足对吞吐量的要求,可以承受暂停。

4. G1(垃圾优先)收集器(1.7以上)

通过允许配置(例如,GC运行时暂停时间),此选项可提高垃圾收集的可预测性。 据说在并行性和并发性两方面都具有优势。 它将内存划分为多个区域,每个区域都被视为伊甸园,幸存者或保有权空间。 如果该区域有更多无法访问的对象,则将首先对该区域进行垃圾收集。

版本中的默认垃圾收集器

  • Java 7 –并行GC
  • Java 8 –并行GC
  • Java 9 – G1 GC
  • Java 10 – G1 GC
  • Java 11 – G1 GC(ZGC与Epsilon一起作为实验功能提供)
  • Java 12 – G1 GC(引入了Shenandoah GC。仅适用于OpenJDK。)

垃圾收集器的优化参数

除非有默认设置要解决的问题,或者是经过长时间的考虑,并且经过长时间的生产级别的负载模式验证后才能确定,否则调优JVM的经验法则是不这样做的。 这是因为Java Ergonomics已经取得了很大进步,并且如果应用程序不难看的话,大多数时候将能够执行很多优化。 在[5]中可以找到选项的完整列表,包括配置堆空间的大小,阈值,要使用的垃圾收集器的类型等。

诊断

除堆转储外,以下配置还有助于通过GC行为诊断内存问题。

-XX:-PrintGCDetails –打印垃圾收集的详细信息。
-Xloggc:<文件名> –将GC日志记录详细信息打印到给定文件。
-XX:-UseGCLogFileRotation –完成上述配置后,启用GC日志文件旋转。 -XX:-HeapDumpOnOutOfMemoryError –如果发生OOM错误,则转储堆内容以进行进一步分析。 -XX:OnOutOfMemoryError =” <cmd args =””>; <cmd args =””>” –如果发生OOM错误,则要运行的命令集。 遇到错误时允许执行任何自定义任务。

我们将在另一篇文章中讨论诊断和分析细节。

干杯!

[1] – https://docs.oracle.com/javase/specs/index.html


[2] – https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5.6


[2] – Oracle垃圾收集调优指南–

https://docs.oracle.com/javase/9​​/gctuning/ergonomics.htm#JSGCT-GUID-DB4CAE94-2041-4A16-90EC-6AE3D91EC1F1

[3] –新的Java垃圾收集器–

https://blogs.oracle.com/javamagazine/understanding-the-jdks-new-superfast-garbage-collectors

[4] –可用的收藏家–

https://docs.oracle.com/cn/java/javase/13/gctuning/available-collectors.html#GUID-F215A508-9E58-40B4-90A5-74E29BF3BD3C

[5] – JVM选项–

https://www.oracle.com/technetwork/articles/java/vmoptions-jsp-140102.html

翻译自: https://www.javacodegeeks.com/2020/05/jvm-garbage-collection-and-optimizations.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值