一次性把GC讲清楚,别再被面试官问住了

本文深入讲解Java垃圾收集(GC)机制,包括对象回收标准、四种核心算法、不同内存区域的回收方式及垃圾收集器分类。适合Java开发者进阶学习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  Java大联盟

  致力于最高效的Java学习

关注

垃圾收集 GC(Garbage Collection)是 Java 非常重要的核心技术之一,Java 开发中程序员不需要关心对象的内存分配和资源释放,这些都由 GC 来完成,这使得  Java 开发者只需要将注意力集中在业务逻辑的处理上。

学习 GC 需要从以下 4 个方面入手:

1、如何判断某个对象是垃圾,需要被回收?

2、垃圾回收算法。

3、不同内存区域的回收方式。

4、垃圾收集器的分类。

如何判断对象是垃圾

Java 对象被判定为垃圾的标准:没有被其他对象引用,判断方法有两种:

1、引用计数算法

通过判断对象的引用数量来决定是否要被回收,每一个对象实例都有一个计数器,被引用则+1,完成引用则-1。

什么是完成引用?

当该对象的引用超过了生命周期,或者引用指向了其他对象,在某方法中定义一个对象的引用变量,方法结束之后变量被虚拟机栈自动释放,则改对象的引用也就结束了,所以任何一个引用计数为 0 的对象是可以被当作垃圾回收的。

2、可达性分析算法

通过判断对象的引用链是否可达来决定对象是否要被回收,这个算法的基本思想就是通过一系列的称为 GC Root 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Root 没有任何引用链相连的话,则证明此对象是不可达的,即认为它不可用,如下图所示。

什么对象可以作为 GC Root ?

1、虚拟机栈中的引用对象

2、方法区中的常量引用对象

3、方法区中的类静态属性引用对象

4、本地方法栈中的引用对象

5、活跃线程中的引用对象

垃圾回收算法

1、标记-清除算法(Mark and Sweep)

标记:从根集合进行扫描,对存活的对象进行标记。

清除:对堆内存进行遍历,回收不可达对象内存。

缺点:清除后会产生大量不连续的内存碎片,可能导致后续在创建较大对象是无法找到足够的连续内存而触发再一次的垃圾回收,如下图所示。

2、复制算法

将可用内存分为对象面和空闲面,在对象面上创建对象,当对象面没有空间的时候,将还存活的对象复制到空闲面,将对象面所有对象清除。

解决了碎片化问题,顺序分配内存,简单高效,适用于对象存活率较低的场景,因为复制的内容少,所以效率高,如下图所示。

3、标记-整理算法

标记:从根集合进行扫描,对存活的对象进行标记。

清除:移动所有存活的对象,按内存地址依次排列,然后将末端地址以后的内存全部回收。

在标记-清除的基础上完成了移动,解决了内存碎片的问题,但是成本更高,适用于对象存活率较高的场景,如下图所示。

4、分代收集算法

是一种组合的回收机制,也是 GC 的主流回收算法,将不同生命周期的对象分配到堆中的不同区域,采用不同的垃圾回收算法,提高 JVM 垃圾回收效率。

不同内存区域的回收方式

年轻代

使用 Minor GC 进行回收,采用复制算法,年轻代分为 Eden 区和 Survivor 区。

Eden区:对象刚被创建的时候,存放在 Eden 区,如果 Eden 区放不下,则放在 Survivor 区,甚至老年代中。

Survivor 区:Minor 回收时使用,将 Eden 中存活的对象存入 Survior 中(From),再一次 Minor 时,将 Survior From 中的对象存入 Survior To 中,清除 Survior From ,下一次 Minor 时重复次步骤,Survior From 变成 Survior To,Survior To 变成 Survior From,依次循环,同时每次 Minor,对象的年龄都 +1,年龄增加到一定程度的对象,移动到老年代中。

老年代

存放生命周期较长的对象,使用标记-清理算法或者标记-整理算法进行回收。

垃圾收集器的分类

年轻代常见的垃圾收集器

1、Serial 收集器(复制算法):单线程收集,进行垃圾收集时,必须暂停所有工作线程。

2、ParNew 收集器(复制算法):多线程收集,垃圾收集和工作线程可同时执行。

3、Parallel Scavenge 收集器(复制算法):多线程收集,更关注系统的吞吐量。

Serial 收集器和 ParNew 收集器更关注用户线程停顿时间,停顿时间越短,响应速度越快,用户体验越好,适用于直接与用户交互的程序。

Parallel Scavenge 收集器更关注系统的吞吐量,可提升 CPU 的效率,尽快完成运算任务,适合在后台运行,不需要太多交互的程序。

吞吐量 = 运行用户代码的时间/(运行用户代码的时间 + 垃圾收集的时间),即 CPU 运行用户代码时间与 CPU 总消耗时间的比值。

老年代常见的垃圾收集器

1、Serial Old 收集器(标记-整理算法):单线程收集,进行垃圾收集时,必须暂停所有工作线程。

2、ParNew Old 收集器(标记-整理算法):多线程收集,垃圾收集和工作线程可同时执行,吞吐量优先。

3、CMS 收集器(标记-清除算法):垃圾回收线程和用户线程几乎可以同时工作。

4、Garbage First 收集器(复制+标记-整理算法):并发和并行,使用多个 CPU 来缩短 Stop-the-World 的停顿时间,与用户线程并发执行,并且可采用不同的方式去处理新产生的对象。同时有利于空间整合,基于标记-整理算法,可以解决内存碎片的问题。

Stop-the-World:JVM 由于要执行 GC 而停止了应用程序的执行。

任何一种 GC 算法中都会发生,当 Stop-the-World 发生时,除了 GC 的线程以外, 所有线程都处于等待状态,直到 GC 任务完成,多数 GC 优化就是通过减少 Stop-the-World 发生的时间来提高程序性能。

以上就是 GC 的核心内容,你都学会了吗?

 

<think>嗯,用户现在想了解Unity中的AB打包策略,也就是AssetBundle的打包策略,以及在打包时需要注意的事项。用户之前已经询问过面试建议,现在深入到了AB打包的具体知识点,这可能是因为他在准备面试时遇到了相关问题,或者在实际项目中需要应用这方面的知识。 首先,我需要确定用户的基础水平。他已经知道AB打包是资源管理的一部分,但可能对具体策略和优化点不太清楚。作为回答者,我需要详细解释AB打包的基本概念,然后分步骤说明策略,再列举注意事项,最后可能结合实际例子帮助理解。 用户提到“合理的AB打包策略”,所以需要涵盖如何划分AB包,比如按模块、类型、场景等。同时,注意事项可能包括依赖管理、版本控制、内存管理、压缩方式等。还需要考虑不同平台的处理,以及热更新和冗余问题。 在组织内容时,应该结构清晰,分点说明,每个部分有子项,方便用户理解。可能需要先介绍AB打包的作用,再讲策略,再讲注意事项,最后总结或给出最佳实践。此外,用户可能对技术细节如依赖分析工具、内存泄漏预防等感兴趣,需要详细说明。 需要注意的是,用户可能在实际应用中遇到了问题,比如包体过大、加载慢或者依赖问题,因此回答中应包含解决这些常见问题的方法。同时,结合之前的面试建议,可能用户需要这些知识点来应对技术面试中的具体问题,因此回答要兼顾深度和实用性,确保内容全面且易于理解。</think>好的!AssetBundle(AB)打包是Unity资源管理的核心内容之一,合理的策略直接影响游戏性能、加载速度和维护成本。以下从**打包策略设计原则**、**注意事项**和**常见优化方案**三个方向详细说明: --- ### **一、AB打包的核心目标** 1. **减少包体大小**:避免重复资源,降低下载和存储压力。 2. **控制依赖关系**:防止冗余加载,减少内存占用。 3. **提升加载速度**:合理拆分AB包,按需加载。 4. **支持热更新**:动态替换资源,避免全量更新。 --- ### **二、AB打包策略设计原则** #### **1. 资源分类与拆分规则** - **按功能模块划分** - 例如:将角色、场景、UI、音效分别打包为 `characters.ab`、`scene_01.ab`、`ui_main.ab`。 - 优点:逻辑清晰,按需加载。 - **按资源类型划分** - 例如:将所有贴图打包为 `textures.ab`,所有动画片段打包为 `animations.ab`。 - 注意:需平衡粒度,避免单个AB包过大(如超过10MB)。 - **按使用频率划分** - 高频资源(如主界面UI)单独打包,低频资源(如剧情动画)合并打包。 - **公共资源独立打包** - 公共材质、Shader、字体等单独打包为 `common.ab`,避免被多个AB包重复引用。 #### **2. 依赖管理** - **显式声明依赖** - Unity会自动分析资源依赖,但需确保父AB包先加载。 - 例如:角色模型(`character.ab`)依赖公共材质(`common_materials.ab`),需先加载后者。 - **避免循环依赖** - 若AB包A依赖B,B又依赖A,会导致加载失败。 - **依赖冗余检测** - 使用工具(如Unity自带的 `AssetBundle Browser`)分析依赖树,确保无重复资源。 #### **3. 版本与热更新** - **增量更新** - 通过对比文件哈希值(MD5/SHA1),仅更新修改的AB包。 - **版本号管理** - 每个AB包附带版本号,例如 `ui_v1.2.ab`,服务端通过版本控制下发差异包。 --- ### **三、AB打包的注意事项** #### **1. 资源冗余问题** - **问题根源** - 同一资源被打包到多个AB包中(例如两个角色共享一个材质,但未独立打包)。 - **解决方案** - 使用 `AssetDatabase.GetDependencies` 分析资源引用,将共享资源提取到公共AB包。 #### **2. 内存管理** - **加载与卸载平衡** - 频繁加载/卸载AB包会触发GC,建议使用引用计数或LRU(最近最少使用)策略管理生命周期。 - **避免内存泄漏** - 卸载AB包时需先销毁其创建的实例(如GameObject、Texture),再调用 `AssetBundle.Unload(true)`。 #### **3. 压缩与性能** - **压缩方式选择** - **LZMA**:高压缩率,但解压慢,适合初始包下载。 - **LZ4**:低压缩率,但解压快,适合运行时动态加载。 - 可通过 `BuildAssetBundleOptions.ChunkBasedCompression` 启用LZ4。 - **非压缩包** - 适用于开发阶段快速测试,但最终发布需压缩。 #### **4. 平台兼容性** - **不同平台AB包不通用** - Android(.apk)和iOS(.ipa)需分别打包,通过 `BuildTarget` 指定平台。 - **注意纹理格式** - Android优先使用ETC2,iOS使用ASTC,避免因格式转换导致AB包失效。 #### **5. 调试与工具** - **使用AssetBundle Browser** - 可视化查看AB包依赖、资源分布和打包结果。 - **日志监控** - 在代码中记录AB包加载耗时、内存占用等关键指标。 --- ### **四、优化方案与最佳实践** 1. **按需加载与预加载结合** - 核心资源(如主场景)预加载,非核心资源(如副本贴图)运行时按需加载。 2. **AB包分块与加密** - 大资源(如过场动画)拆分为多个小块,避免单次下载超时。 - 对敏感资源(如剧情文本)加密,防止反编译。 3. **自动化打包流程** - 编写Editor脚本,自动化完成资源分类、依赖分析和打包操作。 - 示例代码框架: ```csharp [MenuItem("Tools/Build AssetBundles")] static void BuildABs() { BuildPipeline.BuildAssetBundles( "Assets/AssetBundles", BuildAssetBundleOptions.ChunkBasedCompression, BuildTarget.Android ); } ``` 4. **AB包更新策略** - 首次启动下载基础包,进入游戏后后台静默更新其他资源。 --- ### **五、常见问题与解决** - **问题1:AB包加载后资源丢失?** - 检查依赖是否完整加载,确保AB包路径和名称一致。 - **问题2:真机加载AB包耗时过长?** - 使用异步加载(`AssetBundle.LoadFromFileAsync`),搭配Loading界面。 - **问题3:热更新后资源未生效?** - 清除本地缓存,或强制重启应用重新加载AB包。 --- ### **六、学习资源推荐** 1. **官方文档** - [Unity AssetBundle 手册](https://docs.unity3d.com/Manual/AssetBundlesIntro.html) 2. **工具插件** - AssetBundle Browser(Unity官方工具) - Addressables(更高级的资源管理方案) 3. **实战案例** - 尝试为一个简单项目设计AB打包策略,并测试加载/卸载流程。 --- 掌握AB打包需要结合理论积累和项目实践,建议先从一个小型项目入手,逐步验证不同策略的效果。如果有具体场景的问题,可以继续讨论!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值