3.JVM-对象引用

本文详细介绍了Java中的四种引用类型:强引用、软引用、弱引用和虚引用,以及它们在垃圾回收中的作用。同时,探讨了引用计数算法和可达性分析算法在对象生存状态判断中的应用。


对象是不是还在被引用,是java里判断对象是不是存活的依据。如果被引用,那么对象就是存活的,如果没被引用,对象就已经死亡。

1. 引用

早期(JDK1.2之前)对引用的定义是:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。

但是这种定义太过狭隘,所以在JDK1.2之后,java对引用的概念进行了扩充,将引用分为:

  • 强引用(Strong Reference)
  • 软引用(Soft Reference)
  • 弱引用(Weak Reference)
  • 虚引用(Phantom Reference)

1.1.强引用(Strong Reference)

强引用就是指在程序代码中普遍存在,类似 Object obj = new Object();

回收:只要强引用还在,垃圾收集器就不会回收掉被引用的对象

实现: Object obj = new Object();

1.2. 软引用(Soft Reference)

用来描述一些还有用但非必需的对象。

回收:当内存不足时就会回收,内存充足时保留。

实现:SoftReference类

1.3. 弱引用(Weak Reference)

用来描述非必需对象,强度比软引用更弱一些。它只能存活到下一次垃圾回收之前。

回收:当垃圾收集器工作时,就会回收。

实现:WeakReference类

1.4. 虚引用(Phantom Reference)

也称为幽灵引用或者幻影引用,是最弱的一种引用关系。

回收:对对象毫无影响,无法通过引用获得引用的对象实例,但是会在这个对象被回收时收到一个系统通知。

实现:PhantomReference类

2. 引用计数算法

给对象中添加一个引用计数器,每当有一个地方引用它时,计数器就加1,每当引用失效时,计数器就减1.当计数器为0时,说明对象已死,可以回收。

优点: 实现简单,判定效率高
缺点: 无法解决循环依赖,如A引用B,B引用A,他们的计数器都为1,但是其实已经没用了,引用计数器无法标记该种情况。

3. 可行性分析算法

通过一系列的"GC Roots" 的对象作为起点,从这些起点开始向下搜索,搜索所走过的路径为引用链(Reference Chain),如果一个对象不再这些引用链内,那么其不可达,可以回收。

在java中,GC Roots对象包括下面几种:

  • 虚拟机栈
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI引用的对象
<think>嗯,用户询问的是Spark Executor中两种JVM参数配置的区别。这个问题涉及JVM调优的核心知识,用户可能是Spark开发者或运维人员,需要明确不同参数组合对性能的影响。 从引用材料看,用户提供了详细的JVM参数指南,说明ta对G1收集器已有基础认知。但需要特别注意Spark场景的特殊性:Executor作为分布式计算单元,其内存管理比单体应用更复杂。两种配置的核心差异在于目标导向不同: 第一种配置: - `-XX:+UseG1GC -XX:MaxGCPauseMillis=200` 这是典型的响应时间优化方案。G1收集器的特色在于可预测停顿,设置200ms停顿目标后,JVM会自动调整年轻代大小/回收频率等参数。但引用[1]提醒过,实际停顿时间可能因堆大小变化,在Spark这种数据密集型场景需谨慎。 第二种配置: - `-XX:+UseG1GC -Xms4096m -Xmx8192m` 侧重内存容量控制。固定最小堆4GB避免动态扩展开销(引用[1]提到的"忽略-Xms导致启动慢"问题),最大堆8GB给Executor留出弹性空间。这里隐含风险点:引用[4]强调过-Xmx过高可能引发Full GC,在Executor内存超YARN容器限制时会被kill。 特别要注意Spark的层次化配置: 1. `spark.executor.memory=8g` 控制总内存 2. `spark.executor.memoryOverhead` 额外堆外内存 3. `extraJavaOptions` 覆盖JVM行为 用户配置的-Xmx必须小于 `executor.memory - memoryOverhead`,否则启动失败(引用[3]的tomcat配置错误案例可类比)。 关于GC线程数等隐含参数,引用[1]提到应设置`ParallelGCThreads=CPU核数`,但Executor通常独占节点,需考虑 `spark.executor.cores` 参数联动。若未指定,G1可能误判并行线程数。 用户可能还关心:为什么不同时配置两组参数?实际上可以组合,但要注意`MaxGCPauseMillis`与堆大小的相互制约——大堆空间更容易满足停顿目标,但回收时可能突发长时间STW(引用[5]的混合模式说明暗示JVM自适应更可靠)。 最后想到个隐患:Spark Streaming场景用200ms停顿目标风险较大,需引用[1]推荐的`-XX:+PrintGCDetails`监控实际停顿。 </think> 理解您的问题,我们来详细分析 `spark.executor.extraJavaOptions` 中两种配置的区别: ### 核心区别分析 1. **配置一:`-XX:+UseG1GC -XX:MaxGCPauseMillis=200`** * **`-XX:+UseG1GC`**:启用 G1 垃圾收集器作为 Executor 进程的 GC 算法。这是两者的共同基础。 * **`-XX:MaxGCPauseMillis=200`**:这是 **G1 收集器的核心调优目标参数**。它指示 JVM **尽力保证每次垃圾收集的暂停时间(STW)不超过 200 毫秒**。 * **侧重点**:此配置的核心在于 **优化垃圾收集的停顿时间(Latency)和响应性**。它适用于对任务延迟敏感的场景(如流处理、交互式查询)。G1 收集器会根据这个目标**自动调整**其他内部参数(如年轻代大小、回收频率、混合收集周期等)来尝试达成目标。但它**不直接指定堆内存大小**,堆大小由 `spark.executor.memory` 和可能的 `-Xms`/`-Xmx` 共同决定(见下文注意事项)。 2. **配置二:`-XX:+UseG1GC -Xms4096m -Xmx8192m`** * **`-XX:+UseG1GC`**:同样启用 G1 垃圾收集器。 * **`-Xms4096m`**:设置 JVM 堆内存的**初始大小**为 4096 MB (4GB)。Executor 启动时就会分配这么多内存。 * **`-Xmx8192m`**:设置 JVM 堆内存的**最大大小**为 8192 MB (8GB)。Executor 堆内存最多可以增长到这个值。 * **侧重点**:此配置的核心在于 **明确指定 Executor JVM 堆内存的容量范围**。它设定了内存使用的下限和上限。`-Xms` 避免了堆初始过小导致早期频繁 GC 和动态扩展的开销(尤其是在启动加载数据时)。`-Xmx` 防止堆无限增长导致 OOM。但它**没有为 G1 设置明确的停顿时间目标**,G1 会使用其默认目标。 ### 关键差异总结 | 特性 | 配置一 (`-XX:+UseG1GC -XX:MaxGCPauseMillis=200`) | 配置二 (`-XX:+UseG1GC -Xms4096m -Xmx8192m`) | | :----------------- | :------------------------------------------------ | :--------------------------------------------- | | **核心目标** | **优化 GC 停顿时间 (Latency)** | **明确指定堆内存大小范围 (Capacity)** | | **G1 调优方向** | 追求更短的 GC 暂停,提升应用响应速度 | 提供稳定可控的内存池,避免 OOM 和初始扩展开销 | | **参数作用** | `MaxGCPauseMillis` 指导 G1 内部行为 | `-Xms`/`-Xmx` 直接定义堆空间硬边界 | | **堆大小控制** | **间接**,由 G1 根据目标自动调整 | **直接**,明确指定初始大小和上限 | | **典型应用场景** | 流处理、低延迟查询、交互式应用 | 批处理、内存需求明确且稳定的应用 | | **与 Spark 主参数关系** | 堆大小依赖 `spark.executor.memory` | 覆盖 `spark.executor.memory` 设定的堆大小 | ### 重要注意事项与最佳实践 1. **`spark.executor.memory` 的主导作用**: * Spark 的主配置参数 `spark.executor.memory` (例如 `--executor-memory 8g`) **已经默认设置了 Executor JVM 的堆大小上限 (`-Xmx`)**。其值等于 `spark.executor.memory`。 * **如果**你在 `spark.executor.extraJavaOptions` 中**显式指定了 `-Xmx`**,那么这个显式的 `-Xmx` 值**会覆盖** `spark.executor.memory` 设置的堆上限值[^3]。 * `spark.executor.memory` **不设置 `-Xms`**。默认初始堆 (`-Xms`) 通常较小(由 JVM 决定),这就是为什么有时需要显式设置 `-Xms` 来避免早期 GC。 2. **`-Xms` 和 `-Xmx` 的关系**: * 最佳实践是将 `-Xms` 和 `-Xmx` **设置为相同的值**(如 `-Xms8g -Xmx8g`)。这可以: * 避免 JVM 在运行时动态调整堆大小带来的开销。 * 确保应用启动时就能获得全部可用的堆内存,减少早期 GC 的发生。 * 提高内存分配的确定性。引用[1]也强调了这点:“-Xms4G -Xmx4G:初始和最大堆均为4GB,避免动态调整”。 3. **组合使用**: * 在实际生产环境中,为了同时获得**可控的内存大小**和**优化的停顿时间**,**最佳实践是组合使用这些参数**。例如: ```bash spark.executor.extraJavaOptions = -XX:+UseG1GC -Xms8g -Xmx8g -XX:MaxGCPauseMillis=200 ``` * `-Xms8g -Xmx8g` 确保 Executor 有固定且充足的 8GB 堆内存。 * `-XX:MaxGCPauseMillis=200` 指导 G1 在满足内存需求的前提下,尽力缩短 GC 停顿。 * 引用[4]指出:“JVM 调优是一个复杂的过程,需要根据应用程序的特点、运行环境等因素综合考虑并调整各种参数”。 4. **其他重要参数**: * 根据引用[1][3],配置 G1 时通常还需考虑: * `-XX:InitiatingHeapOccupancyPercent` (IHOP):触发并发标记周期的堆占用阈值,默认 45%,有时需要调高。 * `-XX:ConcGCThreads` / `-XX:ParallelGCThreads`:调整 GC 线程数以匹配 CPU 资源。 * `-XX:G1ReservePercent`:为晋升失败预留的空间百分比。 * `-XX:+PrintGCDetails -XX:+PrintGCDateStamps` 或 `-Xlog:gc*`:输出详细 GC 日志,用于监控和分析调优效果。引用[1][3]均推荐启用 GC 日志。 5. **`MaxGCPauseMillis` 是目标而非保证**: * `-XX:MaxGCPauseMillis=200` 只是一个**软目标**,JVM 会尽力达成,但**不保证**每次暂停都严格小于 200ms。实际暂停时间受数据量、对象结构、硬件等因素影响。设置过低(如 10ms)可能导致 G1 无法达成目标反而增加开销。 ### 结论 * `-XX:+UseG1GC -XX:MaxGCPauseMillis=200` 主要解决 **GC 停顿时间问题**,让 G1 优先考虑降低暂停对应用响应的影响。 * `-XX:+UseG1GC -Xms4096m -Xmx8192m` 主要解决 **堆内存容量管理问题**,明确指定了 Executor JVM 可用堆内存的起点和终点。 * 对于追求性能和稳定性的 Spark 应用,**推荐在 `spark.executor.extraJavaOptions` 中同时设置 `-Xms`/`-Xmx` (通常等值) 和 `-XX:MaxGCPauseMillis`**,并结合 `spark.executor.memory` 和其他 G1 调优参数(如 GC 日志、线程数等)。务必通过压力测试和监控 GC 日志来验证和调整参数[^1][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值