背景
相信大家公司有crash的收集途径的工具,肯定会看到大量的一种错误类型SIGSEVG(SEGV_MAPERR),这个crash其实并不属于恶性crash,对游戏体验不会造成严重的影响;另外,这个crash也只会在某些特定机型和系统上才会出现,非所有设备都会出现,其它细节也暂不明确,所以我们大部分情况是不会去修复他的,尽管解决这个问题带来的收益不明显,但是减少闪退次数还是值得去深究的,但是如果我们想修复呢??
SIGSEVG(SEGV_MAPERR)
SIGSEGV
(Segmentation Fault)是一个信号,表示程序试图访问未被允许的内存区域。SEGV_MAPERR
是 SIGSEGV
的一个具体错误代码,表示程序试图访问一个未映射的内存地址。这通常意味着程序试图访问一个无效的指针或已经释放的内存。
常见原因
- 空指针解引用: 程序试图访问一个未初始化或已被释放的指针。
- 数组越界: 访问数组时超出了其边界。
- 使用已释放的内存: 访问已经通过
free
或delete
释放的内存。 - 栈溢出: 递归调用过深,导致栈空间耗尽。
- 内存分配错误: 使用
malloc
或new
分配内存失败后未检查返回值。
调试步骤
- 检查代码: 仔细检查导致崩溃的代码,特别是指针和数组的使用。
- 使用调试工具: 使用调试器(如 GDB)来运行程序并查看崩溃时的调用栈。
- 在 GDB 中,可以使用
run
命令运行程序,崩溃后使用backtrace
查看调用栈。
- 在 GDB 中,可以使用
- 内存检查工具: 使用工具如 Valgrind 来检测内存泄漏和非法内存访问。
- 日志记录: 在关键代码段添加日志,帮助追踪程序执行流程。
解决方案
- 初始化指针: 确保所有指针在使用前都被正确初始化。
- 边界检查: 在访问数组时,确保索引在有效范围内。
- 智能指针: 在 C++ 中,使用智能指针(如
std::unique_ptr
和std::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.UnloadSceneAsync
或 SceneManager.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
类中被调用的。
调用场景
-
Unity 引擎初始化完成:
nativeDone
方法通常在 Unity 引擎完成初始化后被调用。这意味着 Unity 的核心组件已经准备好,可以开始处理游戏逻辑和渲染。 -
应用程序启动: 当 Unity 应用程序启动并完成必要的设置时,
nativeDone
会被调用,以通知 Unity 引擎已经准备好进行后续操作。 -
场景加载完成: 在某些情况下,当场景加载完成并且引擎准备好进行渲染时,
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