首页 > 其他分享 >Unity项目Native Crash问题修复原理

Unity项目Native Crash问题修复原理

时间:2024-10-30 18:19:37浏览次数:9  
标签:调用 Crash text Unity 内存 nativeDone UnityPlayer Native

背景
相信大家公司有crash的收集途径的工具,肯定会看到大量的一种错误类型SIGSEVG(SEGV_MAPERR),这个crash其实并不属于恶性crash,对游戏体验不会造成严重的影响;另外,这个crash也只会在某些特定机型和系统上才会出现,非所有设备都会出现,其它细节也暂不明确,所以我们大部分情况是不会去修复他的,尽管解决这个问题带来的收益不明显,但是减少闪退次数还是值得去深究的,但是如果我们想修复呢??

SIGSEVG(SEGV_MAPERR)

SIGSEGV(Segmentation Fault)是一个信号,表示程序试图访问未被允许的内存区域。SEGV_MAPERRSIGSEGV 的一个具体错误代码,表示程序试图访问一个未映射的内存地址。这通常意味着程序试图访问一个无效的指针或已经释放的内存。

常见原因

  1. 空指针解引用: 程序试图访问一个未初始化或已被释放的指针。
  2. 数组越界: 访问数组时超出了其边界。
  3. 使用已释放的内存: 访问已经通过 freedelete 释放的内存。
  4. 栈溢出: 递归调用过深,导致栈空间耗尽。
  5. 内存分配错误: 使用 mallocnew 分配内存失败后未检查返回值。

调试步骤

  1. 检查代码: 仔细检查导致崩溃的代码,特别是指针和数组的使用。
  2. 使用调试工具: 使用调试器(如 GDB)来运行程序并查看崩溃时的调用栈。
    • 在 GDB 中,可以使用 run 命令运行程序,崩溃后使用 backtrace 查看调用栈。
  3. 内存检查工具: 使用工具如 Valgrind 来检测内存泄漏和非法内存访问。
  4. 日志记录: 在关键代码段添加日志,帮助追踪程序执行流程。

解决方案

  • 初始化指针: 确保所有指针在使用前都被正确初始化。
  • 边界检查: 在访问数组时,确保索引在有效范围内。
  • 智能指针: 在 C++ 中,使用智能指针(如 std::unique_ptrstd::shared_ptr)来管理内存,减少手动内存管理的错误。
  • 异常处理: 在可能导致崩溃的代码段中使用异常处理机制(如 C++ 的 try-catch)来捕获错误。

总结

SIGSEGV (SEGV_MAPERR) 是一个常见的内存访问错误,通常与指针和内存管理相关。通过仔细检查代码、使用调试工具和内存检查工具,可以有效地定位和解决此类问题。如果你有具体的代码示例或上下文,提供更多信息将有助于更准确地诊断问题。

下面我们就一个上报信息进行具体问题分析

java:
com.unity3d.player.UnityPlayer.nativeDone(Native Method)
com.unity3d.player.UnityPlayer.f(Unknown Source)
com.unity3d.player.UnityPlayer.j(Unknown Source)
com.unity3d.player.UnityPlayer 14. r u n ( U n k n o w n S o u r c e ) c o m . u n i t y 3 d . p l a y e r . U n i t y P l a y e r . e x e c u t e G L T h r e a d J o b s ( U n k n o w n S o u r c e ) c o m . u n i t y 3 d . p l a y e r . U n i t y P l a y e r 14.run(Unknown Source) com.unity3d.player.UnityPlayer.executeGLThreadJobs(Unknown Source) com.unity3d.player.UnityPlayer 14.run(UnknownSource)com.unity3d.player.UnityPlayer.executeGLThreadJobs(UnknownSource)com.unity3d.player.UnityPlayerb.run(Unknown Source)

通过com.unity3d.player.UnityPlayer.nativeDone(Native Method)信息我们是不是要追下这个方法到达位于哪里呢??????

经相关资源查询的得知
com.unity3d.player.UnityPlayer.nativeDone 是 Unity 引擎在 Android 平台上使用的一个方法,通常与 Unity 的渲染和生命周期管理相关。这个方法是 UnityPlayer 类的一部分,主要用于处理 Unity 引擎的状态。

文件位置

UnityPlayer.nativeDone 方法位于 Unity 的 Android 库中,具体来说,它通常在以下路径中找到:

/Editor/Data/PlaybackEngines/AndroidPlayer/Variations/mono/Release/Classes/classes.jar

说明

  • classes.jar: 这个 JAR 文件包含了 Unity 在 Android 平台上使用的所有 Java 类,包括 UnityPlayer 类及其方法(如 nativeDone)。
  • Unity 安装路径: 你需要根据你的 Unity 安装位置来替换 <Your Unity Installation Path>。例如,如果你在 Windows 上安装了 Unity,路径可能类似于 C:\Program Files\Unity\Hub\Editor\<version>\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes\classes.jar

访问和使用

如果你需要查看 nativeDone 方法的实现或相关代码,通常需要使用反编译工具(如 JD-GUI 或 JADX)来查看 JAR 文件中的内容。请注意,直接修改 Unity 的内部类并不推荐,因为这可能会导致不稳定或不可预期的行为。

其他注意事项

  • Unity 版本: 不同版本的 Unity 可能会有不同的文件结构,因此确保你查看的是与你的 Unity 版本相对应的路径。
  • Android Build: 在构建 Android 应用时,Unity 会自动处理与 UnityPlayer 相关的所有内容,因此通常开发者不需要直接与这个方法交互。

既然我们知道了出现问题的方法位于classes.jar

你可以尝试使用java反汇编工具将其反编译成java代码,以此得到unity AndroidPlayer的代码。
反汇编完成后,我尝试在symbols中搜索nativeDone,结果真的有这样的一个C++函数,与之相邻的还有很多nativeXXX函数:
具体反编译截图就不截图了。。。。。。。。

可以直接通过双击nativeDone查看反编译的结果:
.text:60FCB68C ; nativeDone(_JNIEnv *, _jobject *)
.text:60FCB68C _Z10nativeDoneP7_JNIEnvP8_jobject
.text:60FCB68C STMFD SP!, {R4,LR}
.text:60FCB690 BL _ZN22NativeRuntimeException17GetExceptionStateEv ; NativeRuntimeException::GetExceptionState(void)
.text:60FCB694 MOV R4, R0
.text:60FCB698 BL _ZN22NativeRuntimeException3TryEv ; NativeRuntimeException::Try(void)
.text:60FCB69C MOV R0, R4
.text:60FCB6A0 BL _ZN22NativeRuntimeException12SignalRaisedEv ; NativeRuntimeException::SignalRaised(void)
.text:60FCB6A4 CMP R0, #0
.text:60FCB6A8 BNE loc_60FCB6C8
.text:60FCB6AC ADD R0, R4, #4 ; env
.text:60FCB6B0 BL setjmp
.text:60FCB6B4 CMP R0, #0
.text:60FCB6B8 BNE loc_60FCB6C8
.text:60FCB6BC BL _Z22UnityDeinitApplicationv ; UnityDeinitApplication(void)
.text:60FCB6C0 MOV R0, #0
.text:60FCB6C4 BL _Z42ANativeActivity_setFirstLevelHasBeenLoadedb

从反编译信息我们看到了一个非常关键的信息这个函数是:UnityDeinitApplication
这个是通知unity引擎反初始化行为,应该就是要清理unity引擎占用的内存了,那么是哪个几个场景unity引擎会进行反初始化呢????????

触发UnityDeinitApplication的几个场景

在 Unity 中,UnityDeinitApplication 是一个内部方法,通常与应用程序的生命周期管理相关。它主要用于清理和释放资源,通常在以下几种情景中被触发:

1. 应用程序退出

当用户关闭应用程序或通过系统操作(如按下“返回”按钮或关闭窗口)退出时,Unity 会调用 UnityDeinitApplication 来进行必要的清理工作。

2. 场景切换

在某些情况下,当切换到一个新的场景时,Unity 可能会调用 UnityDeinitApplication 来释放当前场景的资源,尤其是在使用了 Application.UnloadSceneAsyncSceneManager.UnloadSceneAsync 等方法时。

3. 应用程序崩溃

如果应用程序由于未处理的异常或其他原因崩溃,Unity 可能会在崩溃处理过程中调用 UnityDeinitApplication 来尝试清理资源。

4. 平台特定的行为

在某些平台(如移动设备或控制台),当应用程序被挂起或切换到后台时,Unity 可能会调用 UnityDeinitApplication 来释放不必要的资源,以优化性能和内存使用。

5. 重新加载应用程序

如果应用程序需要重新加载(例如,更新或重新启动),Unity 可能会调用 UnityDeinitApplication 来清理当前的状态和资源。

6. 编辑器模式下的停止播放

在 Unity 编辑器中,当你停止播放模式时,Unity 也会调用 UnityDeinitApplication 来清理当前的运行状态。

总结

UnityDeinitApplication 是 Unity 内部用于管理应用程序生命周期的重要方法,主要在应用程序退出、场景切换、崩溃处理、平台特定行为、重新加载和编辑器模式停止播放等情景中被触发。了解这些情景有助于开发者更好地管理资源和优化应用程序的性能。

然而比较遗憾的是我并复现触发nativeDone的流程,但是有一点可以确认的是,只要代码走到了nativeDone,那么说明程序立即就要退出被销毁,不会再有其它可能性。因此这个nativeDone的一种可能性是用户自己退出,或者切换到后台,被系统所杀,然而这并不重要,重要的是我们知道了这个crash是在游戏被销毁时发生的。一般来说,游戏退出,即使不发生异常,自己不回收系统资源,资源也会被操作系统回收,因此,可能的解决方案是,跳过这个函数的调用,不释放资源。然后直接让操作系统来回收资源。

鉴于上述分析我已经知道可行的修复方案

就是我们unity侧不去释放资源放权给操作系统,也就是避免触发类似访问无效内存等问题了、、、、
方案1:现在可以考虑的方法是去java代码中禁止这个函数的调用,因为so文件我们无法修改,而我们也不可能去告诉unity说我们需要一个退出时不释放资源直接exit(0)的方法

方案2:根据前面的堆栈,我们可以知道nativeDone应该是UnityPlayer的一个方法,因此打开UnityPlayer.java,我们可以看到nativeDone本身是一个private的native函数,因此可以肯定的是,这个函数只会在UnityPlayer这个类中被调用,因此找到调用nativeDone的地方,即可找到如何屏蔽它的方法
最终我们的解决方式就是修改游戏引擎侧来解决

private final native void nativeFocusChanged(boolean var1);
private final native void nativeRecreateGfxState(Surface var1);
private final native void nativeDone();

com.unity3d.player.UnityPlayer.nativeDone

在 Unity 中,com.unity3d.player.UnityPlayer.nativeDone 方法是一个内部方法,通常与 Unity 引擎的生命周期管理和与 Android 平台的交互相关。具体来说,这个方法通常是在 Unity 引擎的 UnityPlayerActivity 类中被调用的。

调用场景

  1. Unity 引擎初始化完成: nativeDone 方法通常在 Unity 引擎完成初始化后被调用。这意味着 Unity 的核心组件已经准备好,可以开始处理游戏逻辑和渲染。

  2. 应用程序启动: 当 Unity 应用程序启动并完成必要的设置时,nativeDone 会被调用,以通知 Unity 引擎已经准备好进行后续操作。

  3. 场景加载完成: 在某些情况下,当场景加载完成并且引擎准备好进行渲染时,nativeDone 也可能被调用。

具体实现

在 Unity 的 Android 实现中,UnityPlayerActivity 是一个 Java 类,负责处理与 Android 系统的交互。nativeDone 方法通常是通过 JNI(Java Native Interface)与 C++ 代码进行交互的,C++ 代码负责 Unity 引擎的核心逻辑。

总结

com.unity3d.player.UnityPlayer.nativeDone 方法是 Unity 引擎在初始化和准备阶段调用的一个内部方法,主要用于通知引擎已经完成初始化并准备好进行后续操作。虽然具体的调用链可能会因 Unity 版本而有所不同,但它通常与 Unity 引擎的启动和场景加载过程密切相关。

切记修复方案带猜测性,如发外网需要对每一步的猜测进行验证。

标签:调用,Crash,text,Unity,内存,nativeDone,UnityPlayer,Native
From: https://blog.csdn.net/qq_33060405/article/details/143277185

相关文章

  • Unity的SkinnedMeshRenderer性能优化
    Unity支持两种主要的Skinning技术在Unity中,Skinning主要指的是角色的蒙皮过程,这是3D动画中的一个关键步骤,用于将3D模型的网格(皮肤)附着到骨骼上,使得模型可以根据骨骼的动作进行逼真的变形。Unity支持两种主要的Skinning技术:CPUSkinning和GPUSkinning。1.CPUSkinning......
  • 【Unity】Addressables下的图集(SpriteAtlas)内存优化
    前言:资源管理系统:AddressablesUI:模拟NGUI图集Sprite,在UGUI下继承Image增加UIImage组件,实现将SpriteAtlas组件拖拽到属性面板上,切换选择里面的小图问题:在检查项目内存占用过高问题时,发现直接拖拽上去的资源不受Addressables系统的自动引用管理,导致部分资源虽然没有引用,但是未被释放......
  • CommunityToolkit.Mvvm中的Ioc
    什么是Ioc在软件工程中,控制反转(IoC)是一种设计原则,其中计算机程序的自定义编写部分从外部源(例如框架)接收控制流。术语“反转”是历史性的:与过程式编程相比,具有这种设计的软件架构“反转”了控制。在过程式编程中,程序的自定义代码调用可重用库来处理通用任务,但在控制反转的情况下,是......
  • 【Unity休闲风格UI资源】GUI - Casual Fantasy
    GUI-CasualFantasy是Unity的一款用户界面(GUI)插件,专为休闲幻想类游戏设计,提供了一套完整的UI资源和工具。该插件能帮助开发者快速搭建符合幻想风格的用户界面,适合各种类型的游戏,特别是带有轻松、卡通风格的RPG、冒险、策略等游戏项目。以下是它的主要功能和特点:1......
  • 个人学习React Native的实际意义探讨
        ReactNative(以下简称RN)是一个跨平台框架,它是由facebook公司基于React实现的移动端跨平台开发框架。目前比较流行的跨平台开发框架除了RN,还有一个就是Flutter。随着Flutter的兴起和后来居上,使得RN没有前几年那么吃香了。那么除了技术上的比较外,个人学习RN有什么必......
  • Unity 调用外部exe
    usingSystem;usingSystem.Diagnostics;usingSystem.IO;usingUnityEngine;publicclassRunExe:MonoBehaviour{publicstaticvoidStartExe(stringfilePath){//构建exe文件的完整路径stringexePath=Path.Combine(filePath);//......
  • Unity 三级目录
    usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;usingUnityEngine.UI;publicclassMenuManage:MonoBehaviour{//StartiscalledbeforethefirstframeupdatepublicTransformmenus;privateTransformslice1;......
  • Unity 滑动条 SlideView
    usingUnityEngine;usingSystem.Collections;usingUnityEngine.UI;publicclassSlideView:MonoBehaviour{publicSliderslide;publicScrollbarsb;//UsethisforinitializationvoidStart(){if(transform.name=="Sc......
  • Unity V3 初步使用 —— 为我的.NET项目从简单三层架构转到IOC做准备
    UnityV3初步使用——为我的.NET项目从简单三层架构转到IOC做准备 【前言】看过本博客的都知道(说得好像是热门博客似的,我真没法控制自己的臭美之情),本人喜欢在进入正题前总会喜欢搞点前奏,那么此文将不会再胡言乱语。马上开始。。。【Unity简介】在.NET中是一种非常流行......
  • GaussDB企业级AI-Native分布式数据库
    华为GaussDB是一个企业级AI-Native分布式数据库。GaussDB采用MPP(MassiveParallelProcessing)架构,支持行存储与列存储,提供PB(Petabyte,2的50次方字节)级别数据量的处理能力。华为Gauss数据库是全球首款AI-Native数据库,能够同时支持X86、ARM、GPU、NPU等异构计算。数......