垃圾回收是 Java 编程语言的一项关键功能,可自动管理在 Eden 区中创建的对象的内存分配和释放。
Java 中的垃圾回收功能,让开发人员可以专注于编写代码,而无需担心内存管理,使 Java 成为构建复杂和大规模应用程序的热门选择。但是,了解垃圾回收的工作原理对于 Java 开发人员优化其代码性能并避免常见的内存相关错误至关重要。
本指南探讨了 Java 垃圾回收的基础知识,包括其优点、不同类型的回收器以及编码时要遵循的最佳实践。现在,我们来深入了解垃圾回收的工作原理。
OutofMemoryError 是一种错误,当程序或应用程序尝试分配的内存量超过可用内存时发生。Java 虚拟机 (JVM) 或其他平台在尝试运行应用程序时,如果内存不足,将会发生此错误。
当应用程序或程序尝试创建新对象,但 JVM 无法分配内存来容纳这些对象时,通常会发生 OutofMemoryError。当应用程序使用过多内存并且未正确释放时,也可能会发生此错误。
发生 OutofMemoryError 时,应用程序通常会崩溃并终止。此错误在处理大量元数据的程序(例如图像或视频处理应用程序)或处理大型数据库的程序中很常见。
要解决此错误,可能需要增加应用程序的可用内存量或优化应用程序的内存使用情况。这可以通过修改 JVM 参数,或使用内存性能分析器工具识别内存泄漏或低效内存使用来实现。
在 Java 中,所有对象都存储在堆上,堆是保留用于对象动态分配的内存的一部分。当程序的任何部分不再引用对象时,它就有资格进行垃圾回收。
Java 中的垃圾回收器会定期扫描堆内存以查找未使用的对象。垃圾回收过程涉及多个步骤,包括标记、清扫和压缩。
标记 - 垃圾回收的第一步涉及标记程序仍在引用的所有对象。这是通过从一组根对象(例如全局变量、局部变量和方法参数)开始,然后跟踪可从这些根到达的所有对象来完成的。无法从根到达的对象将被视为符合垃圾回收条件。
清扫 - 在标记阶段之后,垃圾回收器将清扫 Java 堆,以识别和回收不再被引用的对象所使用的内存。这涉及到释放未用对象所使用的内存,并将其添加回可用内存池。
压缩 - 在某些垃圾回收算法中,清扫阶段之后是压缩阶段,在此阶段,将重新排列剩余对象使用的内存,最大限度地减少碎片。这涉及将对象移得更近,并创建更大的连续可用内存块。
Java 虚拟机 (JVM) 自动执行垃圾回收,因此,程序员无需手动管理内存。垃圾回收器在单独的线程上运行,通常在后台运行,因此,不会影响程序的正常执行。
Java 中的垃圾回收算法主要有两种类型:完全垃圾回收和增量垃圾回收。
完全垃圾回收是一个过程,其中,垃圾回收器(编程语言运行时系统的一部分)搜索程序使用的所有内存并编译程序不再使用的任何对象。然后,这些对象就会被标记为垃圾,可以从内存中删除。
完全垃圾回收通常由使用自动内存管理的编程语言(例如 Java 或 Python)的运行时系统执行。在此过程中,垃圾回收器会暂停程序执行以搜索垃圾对象,这可能会导致程序性能暂时变慢。
当程序使用的内存量达到某个阈值或程序请求新的内存块而又没有足够的可用内存时,通常会触发完全垃圾回收。完全垃圾回收的目的是回收程序不需要的内存,以供程序的其他部分或同一台机器上运行的其他程序使用。
增量垃圾回收是一种内存管理技术,编程语言和运行环境使用它来自动回收程序不再需要的内存。它通过识别内存中未使用的对象并释放它们占用的内存来实现这一点,以便程序的其他部分可以重新使用这些内存。
在增量垃圾回收中,垃圾回收器会定期扫描程序的内存,以查找新生代堆内存中无法访问的对象。垃圾回收器在此扫描过程中不会停止程序的执行,而是将扫描过程划分为多个小的、可管理的部分,称为“增量”。在每次增量期间,垃圾回收器都会扫描程序的部分内存,找出不需要的对象,并将其标记为可重用。
通过使用增量,垃圾回收器可以以小块的形式回收内存,而不会长时间中断程序的执行。这有助于确保程序保持响应速度,并且不会因垃圾回收过程而遇到严重的暂停或延迟。
但是,增量垃圾回收的效率可能低于其他类型的垃圾回收技术,例如,标记和清扫或分代垃圾回收,因为它需要更频繁地扫描程序内存。此外,使用增量会给程序执行带来一些开销,因为垃圾回收器需要在两次增量回收之间维护状态信息。
总体而言,Java 中的垃圾回收有诸多好处,使其成为开发人员的宝贵工具。以下是使用 Java 垃圾回收的一些好处:
无需手动内存管理:借助垃圾回收,开发人员无需手动管理内存分配和释放。这意味着程序员可将更多精力放在编写代码上,而不是管理内存,有助于减少错误并提高工作效率。
防止内存泄漏:垃圾回收有助于防止内存泄漏,当程序没有释放不再需要的内存时,就会发生内存泄漏。这可能会导致程序消耗的内存超过所需量,从而引发程序性能下降,并最终崩溃。
动态内存分配:Java 的垃圾回收支持动态内存分配,即在运行时根据需要分配内存。这有助于防止内存分配错误,并提高程序运行效率。
更好的性能:垃圾回收可以通过减少管理内存所花费的时间来帮助提高程序的性能。这可以缩短执行时间,提高程序响应速度。
内存优化:垃圾回收可以优化内存的使用,将程序的某一部分未使用的内存重新用于程序的其他部分。这有助于减少内存使用量,提高整体程序效率。
Java 垃圾回收的自动管理内存、防止内存泄漏、实现动态内存分配、提高性能和优化内存使用的能力让开发人员受益,垃圾回收可以帮助开发人员编写更好、更高效的程序。
在 Java 中,当 JVM(Java 虚拟机)确定堆即将满或一定时间过后,将自动触发垃圾回收。
在 Java 中,有几个事件可以触发垃圾回收:
堆空间分配:当 JVM 需要为新对象分配内存时,如果堆中没有足够的空间,它就会触发垃圾回收,回收未使用的内存或将其存储到 Survivor 区中。
System.gc() 方法调用:可以通过调用 System.gc() 方法显式请求垃圾回收,尽管不能保证它会运行。
老年代阈值:当老年代堆空间(存储长期活动的对象)的堆大小达到某个阈值时,也可以触发垃圾回收。
PermGen/Metaspace 阈值:在 Java 8 之前的 Java 版本中,当 PermGen(永久代)或 Metaspace(在 Java 8 及更高版本中可用)内存区域的大小达到某个阈值时,也会触发垃圾回收。
基于时间:有时,可以根据时间间隔触发垃圾回收。例如,JVM 可能每小时或每天触发一次垃圾回收,而不管内存使用情况如何。
值得注意的是,Java 中垃圾回收的确切行为可能会因 JVM 实现过程和配置的不同而异。
要请求 Java 虚拟机 (JVM) 运行垃圾回收器,可按照以下步骤操作:
调用 System.gc() 方法:此方法用于请求 JVM 运行垃圾回收器。不保证垃圾回收器将在调用此方法后立即运行。
使用 -XX:+DisableExplicitGC JVM 标志:该标志禁用显式垃圾回收请求。这意味着,即使调用 System.gc()或 Runtime.getRuntime().gc(),也不会触发垃圾回收器。
需要注意的是,通常不建议显式请求运行垃圾回收器,因为 JVM 设计为自动管理内存分配和垃圾回收。明确的垃圾回收请求有时会对性能产生负面影响。
当编程语言中的对象不再被程序的任何部分引用时,该对象就符合垃圾回收要求。自动垃圾回收是由编程语言运行时环境执行的回收内存的过程。
在大多数现代编程语言中,垃圾回收由运行时环境自动执行。用于垃圾回收的特定算法可能因编程语言和实施方式的不同而异,但一般原则是相同的:运行时环境定期扫描堆(用于动态分配对象的内存部分)以识别程序中无法再从任何活动对象到达的对象。一旦确定某个对象不可达,它就会被标记为垃圾,其内存可以被回收。
对象符合垃圾回收条件的具体时间取决于运行时环境所使用的特定垃圾回收算法。有些算法比其他算法更激进,可以更快地回收内存,而其他算法可能会延迟垃圾回收以优化性能。不过,一般来说,程序员无需担心手动管理内存的问题,因为运行时环境会自动处理。
Java 使用多种垃圾回收器,包括:
串行垃圾回收器:串行垃圾回收器是 Java 的默认垃圾回收器,通常用于不需要高吞吐量的中小型应用程序。此类回收器有助于防止常见的“暂时中止” (stop the world) 事件发生。
并行垃圾回收器:并行垃圾回收器专为高吞吐量应用程序而设计,尤其适用于需要大型堆的应用程序,因为它使用多个 CPU 来加快处理速度。务必注意,运行垃圾回收器时,此类回收器会冻结应用程序线程。
并发标记清除 (CMS) 回收器:CMS 回收器专为需要较短暂停时间的应用程序而设计,对于有许多活动对象的应用程序非常有用。
G1 垃圾回收器:G1 垃圾回收器专为大型堆而设计,可混合处理短期和长期活动的对象。它使用多个线程来同时扫描和压缩堆。
每款垃圾回收器都有自己的优点和缺点,选择使用哪款回收器取决于应用程序的具体需求。还可以配置和调整垃圾回收器设置,以优化特定应用程序的性能。
垃圾回收和内存泄漏都与计算机程序中的内存管理有关,但它们具有不同的含义和影响。
如前所述,垃圾回收通常由编程语言或运行时环境执行,有助于确保程序消耗的内存不会超过所需量。垃圾回收可识别出可供程序其他部分或计算机上运行的其他程序使用的内存。
另一方面,当一个程序无法释放为它分配的内存时,即便不再需要该内存,也会发生内存泄漏。因此,随着时间的推移,程序会继续消耗内存,导致可用内存最终耗尽,从而可能导致程序或整个操作系统崩溃。内存泄漏通常由程序中的错误引起,可能难以识别和修复。
总之,垃圾回收是一个自动释放不再需要的内存的过程。为程序分配了内存但未释放内存时,就会发生内存泄漏,导致内存使用量逐渐累积。
总之,垃圾回收是 Java 编程的一个重要方面,可通过回收未使用的内存来确保高效的内存管理。Instana 可观测性为开发人员提供了功能强大的工具,可以实时监控和优化垃圾回收流程。
通过使用 Instana,开发人员可以快速识别内存泄漏,优化垃圾回收设置,并解决与垃圾回收相关的性能问题。
借助 Instana 的全面监控功能,开发人员可以深入了解各自的 Java 应用程序的内存使用情况和垃圾回收行为,帮助他们提供高性能且可靠的软件。
通过遵循本指南中概述的最佳实践,开发人员可以使用 Instana 来优化垃圾回收过程,并提高 Java 应用程序的整体性能。利用 Instana 的可观测性,开发人员就能提前发现可能出现的任何问题,确保应用程序始终以最佳状态运行。
完全托管的单租户服务,用于开发和交付 Java 应用程序。
使用开发运维软件和工具,在多种设备和环境中构建、部署和管理云原生应用程序。
云应用程序开发意味着一次构建、快速迭代和随处部署。