Java 内存管理26 Oct 2024 | 13 分钟阅读 在 Java 中,内存管理是对象分配和释放的过程,称为内存管理。Java 会自动进行内存管理。Java 使用一种称为垃圾回收器的自动内存管理系统。因此,我们不需要在应用程序中实现内存管理逻辑。Java 内存管理分为两个主要部分
JVM 内存结构JVM 在堆中创建各种运行时数据区域。这些区域在程序执行期间使用。内存区域在 JVM 退出时被销毁,而数据区域在线程退出时被销毁。 ![]() 方法区方法区是堆内存的一部分,它在所有线程之间共享。它在 JVM 启动时创建。它用于存储类结构、超类名称、接口名称和构造函数。JVM 在方法区存储以下类型的信息
堆区域堆存储实际对象。它在 JVM 启动时创建。用户可以根据需要控制堆。它可以是固定大小或动态大小。当我们使用 new 关键字时,JVM 会在堆中创建一个对象实例。而该对象的引用存储在栈中。每个正在运行的 JVM 进程只有一个堆。当堆满时,就会进行垃圾回收。例如 上面的语句创建了一个 StringBuilder 类的对象。对象分配到堆中,引用 sb 分配到栈中。堆分为以下部分
年轻代年轻代是 Java 堆内存中新对象最初分配的区域。当此区域填满时,会触发Minor Garbage Collection(GC)。年轻代分为以下三个部分
让我们详细讨论上面两个。
老年代老年代,也称为Tenured Generation,是 Java 堆内存中的一个关键区域,用于存储生命周期长的对象。当对象在年轻代中经历多次垃圾回收周期后仍然存活时,就会用到老年代。与年轻代不同,老年代由一个连续的内存空间组成。 老年代中的垃圾回收(GC) Major GC/ Full GC: 当老年代填满时,会发生更全面、耗时更长的事件。它们会清理年轻代和老年代,并可能涉及内存压缩以优化空间使用,通常会导致更长的暂停时间。 永久代永久代,通常称为Perm Gen。它是 Java 虚拟机(JVM)中一个专用的区域,用于存储应用程序类和方法的元数据。需要注意的是,Perm Gen 与存储实例数据的 Java 堆是分开的。 此区域由 JVM 在运行时动态填充,具体取决于应用程序主动使用的类,并且它还包含来自 Java SE 库的类和方法。存储在 Perm Gen 中的对象会在完整垃圾回收周期期间进行垃圾回收,从而可以清理不再需要的类和方法元数据。 Java 堆内存开关1. -xms 描述:它设置 JVM 启动时的初始堆大小。这是 JVM 在启动时尝试为其堆分配的内存量。 示例:-xms512m 以 512 兆字节的初始堆大小启动 JVM。 2. -xmx 描述:设置最大堆大小。它限制了 JVM 可以为其堆分配的最大内存量,从而影响 Java 应用程序可以用于所有对象和进程的内存量。 示例:-xmx2g 将堆大小限制为 2 吉字节。 3. -xmn 描述:它设置年轻代的尺寸。堆的其余部分(年轻代之外)专用于老年代。 示例:-xmn256m 为年轻代分配 256 兆字节。 4. -XX:PermGen 描述:设置永久代的初始大小,用于存储 JVM 的元数据,例如类和方法数据。请注意,此设置仅适用于 Java 8 之前的 JVM,之后 PermGen 被 Metaspace 取代。 示例:-XX:PermGen=128m 将 PermGen 的初始大小设置为 128 兆字节。 5. -XX:MaxPermGen 描述:设置永久区的最大大小。控制此限制对于防止 JVM 在 PermGen 中空间不足时发生的OutOfMemoryError至关重要。 示例:-XX:MaxPermGen=256m 将 PermGen 的大小限制为 256 兆字节。 6. -XX:SurvivorRatio 描述:它指定年轻代中伊甸园和幸存者空间之间的比例。如果年轻代大小为 10m,则 Survivor Ratio 设置为 2,则 5m 将分配给伊甸园空间,而每个幸存者空间将分配 2.5m。 示例:-XX:SurvivorRatio=6 意味着伊甸园空间的大小是每个幸存者空间的六倍。 7. -XX:NewRatio 描述:它提供了老年代与年轻代(Young Generation)大小的比例。比例为 2 意味着老年代是年轻代的两倍大。 示例:-XX:NewRatio=3 将老年代设置为年轻代大小的三倍。 引用类型有四种引用类型:强引用、弱引用、软引用和虚引用。这些引用类型之间的区别在于,它们指向的堆上的对象在不同的标准下才符合垃圾回收的条件。 强引用:它非常简单,就像我们在日常编程中使用它一样。任何附加了强引用的对象都不符合垃圾回收的条件。我们可以通过以下语句创建强引用 弱引用:它在下一次垃圾回收过程之后就不再存在了。如果我们不确定何时会再次请求数据,在这种情况下,我们可以为它创建一个弱引用。如果垃圾回收器处理,它会销毁该对象。当我们再次尝试检索该对象时,我们会得到一个 null 值。它定义在 java.lang.ref.WeakReference 类中。我们可以通过以下语句创建弱引用 软引用:当应用程序内存不足时,它会被收集。垃圾回收器不会收集软可达的对象。所有软引用的对象都会在抛出 OutOfMemoryError 之前被收集。我们可以通过以下语句创建软引用 虚引用:它在 java.lang.ref 包中可用。它定义在 java.lang.ref.PhantomReference 类中。只有虚引用指向的对象可以在垃圾回收器想要收集时被收集。我们可以通过以下语句创建虚引用 栈区域栈区域在线程创建时生成。它可以是固定大小或动态大小。栈内存是按线程分配的。它用于存储数据和中间结果。它包含对堆对象的引用。它还持有值本身,而不是对堆中对象的引用。存储在栈中的变量具有一定的可见性,称为作用域。 栈帧:栈帧是一个数据结构,包含线程的数据。线程数据代表当前方法中线程的状态。
本地方法栈它也被称为 C 栈。它是用 Java 以外的语言编写的本地代码的栈。Java Native Interface (JNI) 调用本地栈。本地栈的性能取决于操作系统。 PC 寄存器每个线程都有一个与之关联的程序计数器(PC)寄存器。PC 寄存器存储返回地址或本地指针。它还包含当前正在执行的 JVM 指令的地址。 垃圾回收器的工作原理垃圾回收器概述当 Java 程序执行时,它以不同的方式使用内存。堆是对象驻留的内存部分。它是唯一参与垃圾回收过程的内存部分。它也被称为可垃圾回收堆。所有垃圾回收都确保堆有尽可能多的可用空间。垃圾回收器的功能是查找并删除无法访问的对象。 对象分配当分配一个对象时,JRockit JVM 会检查对象的大小。它区分小对象和大对象。大小取决于 JVM 版本、堆大小、垃圾回收策略和使用的平台。对象的大小通常在 2 到 128 KB 之间。 小对象存储在线程本地区域(TLA)中,TLA 是堆中的一块空闲块。TLA 不与其他线程同步。当 TLA 填满时,它会请求新的 TLA。 另一方面,不适合 TLA 的大对象直接分配到堆中。如果一个线程正在使用年轻代空间,它会直接存储在老年代空间。大对象需要更多的线程同步。 Java 垃圾回收器做什么?JVM 控制垃圾回收器。JVM 决定何时执行垃圾回收。我们也可以请求 JVM 运行垃圾回收器。但无论如何,都不能保证 JVM 会遵守。如果 JVM 检测到内存不足,它会运行垃圾回收器。当 Java 程序请求垃圾回收器时,JVM 通常会很快地处理该请求。它不保证请求被接受。 需要理解的关键点是“对象何时才有资格进行垃圾回收?” 每个 Java 程序都有多个线程。每个线程都有自己的执行栈。有一个线程在 Java 程序中运行,那就是 main() 方法。现在我们可以说,当没有活动线程可以访问一个对象时,该对象就有资格进行垃圾回收。垃圾回收器将该对象视为可删除对象。如果程序有一个引用变量指向一个对象,那么该引用变量对活动线程是可访问的,这个对象称为可达对象。 这里出现一个问题:“Java 应用程序会耗尽内存吗?” 答案是肯定的。垃圾回收系统会尝试在对象不再使用时将其从内存中移除。尽管如此,如果我们维护了许多活动对象,垃圾回收并不能保证有足够的内存。只有可用内存会被有效管理。 Java 垃圾回收监控监控 Java 应用程序的垃圾回收对于性能调优和资源管理至关重要。作为实际示例,我们将演示如何使用 Java SE 下载中提供的演示应用程序来监控 GC 活动。我使用的应用程序是 Java2Demo.jar,位于下载 JDK 7 和 JavaFX Demos and Samples 后的 jdk1.7.0_55/demo/jfc/Java2D 目录中。但是,我们可以将这些监控技术应用于任何 Java 应用程序。 启动演示应用程序要使用特定的内存设置运行 Java2Demo 应用程序,请使用以下命令 jstatjstat 命令行工具是监控 JVM 内存和垃圾回收活动的宝贵资源。jstat 包含在标准 JDK 安装中,无需额外软件即可提供详细的见解。要使用 jstat,您首先需要找到要监控的 Java 应用程序的进程 ID。您可以通过在终端中执行 ps -eaf | grep java 命令轻松获取此信息。此步骤将帮助您识别要使用 jstat 监控的特定 Java 进程。 既然我们已经确定了 Java 应用程序的进程 ID 为 9582,我们可以继续使用 jstat 命令对其进行监控。以下是如何运行该命令 jstat 命令的最后一个参数指定输出的频率,因此将其设置为 1000 毫秒意味着它将每秒报告一次内存和垃圾回收数据。让我们探讨输出中的每一列代表什么
垃圾回收的类型有五种类型的垃圾回收如下
标记和清除算法JRockit JVM 使用标记和清除算法来执行垃圾回收。它包含两个阶段:标记阶段和清除阶段。 标记阶段:可从线程、本地句柄和其他 GC 根源访问的对象被标记为活动。每个对象树都有多个根对象。GC 根始终是可访问的。因此,任何以垃圾回收根为根的对象。它识别并标记所有正在使用的对象,其余的可以视为垃圾。 ![]() 清除阶段:在此阶段,遍历堆以查找活动对象之间的间隙。这些间隙记录在空闲列表中,可用于新对象的分配。 标记和清除有两种改进版本
并发标记清除它允许线程在垃圾回收的大部分时间内继续运行。有以下类型的标记
并行标记清除它利用系统中所有可用的 CPU 来尽快执行垃圾回收。它也称为并行垃圾收集器。并行垃圾回收执行时,线程不执行。 标记和清除的优点
标记和清除的缺点
下一个主题Java 教程 |
判断一个给定的字符串是否是偶数-奇数回文串是当前的任务。当偶数索引处的字符构成一个回文串,而奇数索引处的字符构成一个独立的回文串时,该字符串就被称为...
5 分钟阅读
Java 中的 main() 方法是程序执行的入口点。Java 应用程序通过 JVM 调用此预定义的、具有签名 public static void main(String[] args) 的方法来启动执行。程序员经常想知道 Java 程序是否可以有多个 main() 方法……
5 分钟阅读
在 Java 中,有多种方法可以创建和访问文本文件。在处理大量应用程序时,执行此操作非常必要。Java 有多种读取纯文本文件的方法,例如 FileReader、BufferedReader 和 Scanner。每种实用程序都提供独特的功能;例如,…
阅读 4 分钟
在 Java 编程中,方法签名是指方法的唯一标识符。它包括方法名称及其参数列表。签名有助于区分一个方法与另一个方法,并允许 Java 编译器将方法调用与其对应的定义进行匹配....
阅读 3 分钟
Java 是开发人员编写代码的首选。它是一种非常流行且成功的编程语言,用于构建应用程序。Java 开发人员的数量日益增加。它主要用于开发 Web 和移动应用程序。要成为...
5 分钟阅读
鸭子数是另一种特殊的正非零数,其中包含零。数字零不应出现在数字的开头。零可以出现在除开头以外的任何位置。让我们通过一些鸭子数的例子来理解……
阅读 3 分钟
合并两个已排序的链表是学习算法时必须解决的基本问题之一。这是一个将两个已排序列表合并的过程,合并后,结果列表仍然保持已排序状态。这个问题通常作为一项编码挑战出现...
5 分钟阅读
Java 提供了一个健壮的并发框架,使开发人员能够编写高效且安全的并发应用程序。在许多工具和概念中,它提供的原子类和 volatile 关键字对于确保线程安全和共享变量的可视性至关重要。在本节中,我们将……
5 分钟阅读
在本节中,我们将学习什么是矩形数,并创建 Java 程序来检查给定的数字是否为矩形数。矩形数程序经常出现在 Java 编码面试和学术界。矩形数一个矩形数是……
阅读 3 分钟
以下是演示此程序的程序。文件:ConvertStringToInteger.java public class ConvertStringToInteger { public static void main(String[] args) { // 第一种方式 String str1 = "5"; int result = Integer.parseInt(str1); // 使用 Integer.parsrInt() System.out.println(result); // 第二种方式 String str2 = "5"; Integer result2 =...
阅读1分钟
我们请求您订阅我们的新闻通讯以获取最新更新。
我们提供所有技术(如 Java 教程、Android、Java 框架)的教程和面试问题
G-13, 2nd Floor, Sec-3, Noida, UP, 201301, India