掌握JVM性能调优-GC机制-释放Java应用潜能!-深入Java (jvm的理解)
JVM 性能调优是一个复杂的过程,需要结合具体的应用程序特性和需求来进行调优。不同的应用场景可能需要不同的调优策略。对于开发人员来说,进行程序的性能优化是很有挑战的工作,也是很有意义的一件事。
JVM 内存模型
JVM 架构应该已经使用了一些像这样的 JVM 配置:
JAVA_OPTS=-server -Xms2560m -Xmx2560m -XX:NewSize=1536m -XX:MaxNewSize=1536m -XX:MetaspaceSize=768m -XX:MaxMetaspaceSize=768m -XX:InitialCodeCacheSize=64m -XX:ReservedCodeCacheSize=96m -XX:MaxTenuringThreshold=5″
那么 JVM 是如何驻留在内存上的?
JVM 消耗主机操作系统内存上的可用空间。在 JVM 内部,存在独立的内存空间(堆、非堆、缓存),以存储运行时数据和编译后的代码。
堆内存
以下是有关服务器应用程序堆大小的一般准则:
- 年轻代(Young Generation)
- 老年代(Old Generation)
非堆内存
- 高速缓存存储器
什么是 GC?
Java 通过一个称为废品收集器的程序提供自动内存管理。移除不再使用的对象。
上面的一切都是在堆中完成的,堆是运行时动态内存分配的空间,用于包含所有 java 对象。除了堆之外,还有堆栈,其中包含支持线程执行的局部变量和函数调用。
Java 废品收集的实际工作原理
许多人认为废品收集会收集并丢弃死对象。事实上,Java 废品收集的作用恰恰相反!活动对象被跟踪,其他所有对象都被指定为废品。
这种根本性的误解可能会导致许多性能问题。让我们从堆开始,它是用于动态分配的内存区域。在大多数配置中,操作系统会提前分配堆,以便在程序运行时由 JVM 管理。这有几个重要的影响:
- 新对象简单地分配在已用堆的末尾
- 一旦某个对象不再被引用并且因此应用程序代码无法访问该对象,废品收集器就会将其删除并回收未使用的内存
每个对象树必须有一个或多个根对象。只要应用程序可以到达这些根,那么整棵树都是可以到达的。但是这些根对象什么时候被认为是可达的呢?
称为废品收集根,它是特殊对象始终是可访问的,任何在其根处具有废品收集根的对象也是如此。Java中有四种GCroot:
- GC 根是 JVM 本身引用的对象,因此可以防止其他所有对象被废品收集
因此,一个简单的 Java 应用程序具有以下 GC 根:
- 程序计数器
- 虚拟机栈
- 本地方法栈
- 方法区
标记并清除废品
为了确定哪些对象不再使用,JVM 间歇性地运行所谓的标记和清除算法。正如所直觉的,这是一个简单的两步过程:
- 标记活动对象
- 删除未使用的对象
活动对象在上图中表示为蓝色。当标记阶段结束时,每个活动对象都被标记。因此,所有其他对象(上图中的灰色数据结构)都无法从 GC根访问,这意味着应用程序无法再使用无法访问的对象。此类对象被视为废品,GC 应在以下阶段中删除它们。
标记阶段需要注意以下重要方面:
- 标记过程在应用程序运行时进行,这意味着 GC 可能会暂停应用程序线程。
- 标记过程可能需要大量时间,具体取决于应用程序正在使用的对象数量。
删除未使用的对象
对于不同的 GC 算法,未使用对象的删除略有不同,但所有此类 GC 算法都可以分为三步:清除(sweeping)、压缩(compacting)和复制(copying)。
标记和清除算法在概念上使用最简单的废品处理方法,即忽略此类对象。这意味着在标记阶段完成后,未访问对象占用的所有空间都被视为空闲,因此可以重用以分配新对象。该方法需要使用所谓的空闲列表记录每个空闲区域及其大小。空闲列表的管理增加了对象分配的开销。
这种方法还有另一个弱点——可能存在大量空闲区域,但如果没有一个区域足够大来容纳分配,分配仍然会失败(在 Java 中会出现 OutOfMemoryError 错误)。它通常被称为标记-清除算法。
Mark-Sweep-Compact 算法通过将所有标记的对象(即活动的对象)移动到堆的另一部分,从而删除未使用的对象。这将所有活动对象集中在一起,从而减少了应用程序的内存碎片。但是,此算法可能会导致 GC 暂停应用程序线程,具体取决于要移动的对象数量。
复制算法将活动对象复制到新分配的内存区域,并释放原始区域中所有未使用的对象。与 Mark-Sweep-Compact 算法相比,此算法不会产生内存碎片,但它需要更多的内存开销,具体取决于要复制的对象数量。
性能调优
JVM 性能调优是一个复杂的过程,涉及到许多因素。以下是一些提示,可以帮助您优化应用程序的性能:
- 使用合适的 GC 算法。对于堆大小较小且对象生命周期较短的应用程序,标记-清除算法可能是最佳选择。对于堆大小较大且对象生命周期较长的应用程序,复制算法可能是更好的选择。
- 调整堆大小。堆大小是影响 GC 性能的重要因素。堆大小应该足够大,以避免频繁的 GC 暂停,但它也不应该太大,以至于导致应用程序内存不足。
- 使用内存分析工具。有许多可用于分析应用程序内存使用的工具。这些工具可以帮助您识别内存泄漏和其他性能问题。
- 监控 GC 性能。使用 Java Management Extensions(JMX)等工具监控 GC 性能可以帮助您识别潜在问题。
结论
JVM 性能调优是一个复杂的过程,需要结合具体的应用程序特性和需求来进行调优。不同的应用场景可能需要不同的调优策略。通过理解 JVM 内存模型和废品回收的实际工作原理,您可以提高应用程序的性能并降低内存消耗。
JVM 调优工具有哪些?
Java虚拟机(JVM)调优是提高Java应用程序性能的重要步骤之一。 以下是一些常用的Java JVM调优工具: - VisualVM:这是Oracle官方提供的免费工具,可以监视和分析Java应用程序的性能。 - JConsole:这是Oracle官方提供的免费工具,可以监视和分析Java应用程序的性能。 - JDK自带的JIT编译器:可以通过调整JIT编译器的参数来提高Java应用程序的性能。 - JProfiler:这是一个商业工具,可以提供更详细的分析和报告,以及更多的性能优化选项。
JVM性能调优(2) —— 内存设置和查看GC日志
1)JVM内存分配有如下一些参数:
一般 -Xms 和 -Xmx 设置一样的大小,-XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 设置一样的大小。-Xms 等价于 -XX:InitialHeapSize,-Xmx等价于-XX:MaxHeapSize;-Xmn等价于-XX:MaxNewSize。
2)在IDEA中可以按照如下方式设置JVM参数:
3)命令行启动时可以按照如下格式设置:
1)设置GC参数:
可以在启动时加上如下参数来查看GC日志:
例如,我在IDEA中添加了如下JVM启动参数:
启动程序之后打印出了如下的一些日志:
从第三行 CommandLine flags 可以得到如下的信息:
2)查看默认参数:
如果要查看JVM的默认参数,就可以通过给JVM加打印GC日志的参数,就可以在GC日志中看到JVM的默认参数了。
还可以在启动参数中添加 -XX:+PrintFlagsFinal 参数,将会打印系统的所有参数,就可以看到自己配置的参数或系统的默认参数了:
3)GC日志:
之后的日志就是每次废品回收时产生的日志,每行日志说明了这次GC的执行情况,例如第四行GC日志:
详细内容如下:
2020-09-25T13:00:41.631+0800:GC发生的时间点。 4.013:系统运行多久之后发生的GC,单位秒,这里就是系统运行 4.013 秒后发生了一次GC。 GC (Allocation Failure):说明了触发GC的原因,这里是指对象分配失败导致的GC。 PSYoungGen:指触发的是年轻代的废品回收,使用的是 Parallel Scavenge 废品回收器。 K->K:对年轻代执行了一次GC,GC之前年轻代使用了 K,GC之后有 K 的对象活下来了。 (K):年轻代可用空间是 K,即 461 M,为什么是461M呢?因为新生代大小为 512M,Eden 区占 409.6M,两块 Survivor 区各占 51.2M,所以年轻代的可用空间为 Eden+1个Survivor的大小,即460.8M,约为461M。 K->K:GC前整个堆内存使用了 K,GC之后堆内存使用了 K。 (K):整个堆的大小是 K,即 973M,其实就是年轻代的 461M + 老年代的 512 M 0. secs:本次GC耗费的时间 Times: user=0.00 sys=0.00, real=0.01 secs:本次GC耗费的时间 4)JVM退出时的GC情况:
程序结束运行后,还会打印一些日志,就是第12行之后的日志,这部分展示的是当前堆内存的使用情况:
详细内容如下:
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。