由于 Unity 中的 MapBox 导致内存泄漏
Memory leak because of MapBox in Unity
我在我的 Unity 项目中使用 MapBox SDK,我只是显示地图。我不使用其他服务,例如路线等。不知何故,我总是收到内存泄漏的错误消息。由于我的项目是 Ios 和 Android 应用程序,因此内存泄漏对我来说听起来不太好哈哈。
引用错误是这样的:
A Native Collection has not been disposed, resulting in a memory leak. Allocated from:
Unity.Collections.NativeArray`1:.ctor(Byte[], Allocator)
UnityEngine.Networking.UploadHandlerRaw:.ctor(Byte[])
Mapbox.Unity.Telemetry.<PostWWW>d__8:MoveNext() (at Assets\Mapbox\Unity\Telemetry\TelemetryEditor.cs:82)
Mapbox.Unity.Utilities.Routine:MoveNext() (at Assets\Mapbox\Unity\Utilities\Runnable.cs:130)
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
UnityEngine.MonoBehaviour:StartCoroutineManaged2(IEnumerator)
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
Mapbox.Unity.Utilities.Routine:.ctor(IEnumerator) (at Assets\Mapbox\Unity\Utilities\Runnable.cs:116)
Mapbox.Unity.Utilities.Runnable:Run(IEnumerator) (at Assets\Mapbox\Unity\Utilities\Runnable.cs:47)
Mapbox.Unity.Telemetry.TelemetryEditor:SendTurnstile() (at Assets\Mapbox\Unity\Telemetry\TelemetryEditor.cs:37)
Mapbox.Unity.MapboxAccess:ConfigureTelemetry() (at Assets\Mapbox\Unity\MapboxAccess.cs:196)
Mapbox.Unity.MapboxAccess:SetConfiguration(MapboxConfiguration, Boolean) (at Assets\Mapbox\Unity\MapboxAccess.cs:108)
Mapbox.Unity.MapboxAccess:LoadAccessToken() (at Assets\Mapbox\Unity\MapboxAccess.cs:161)
Mapbox.Unity.MapboxAccess:.ctor() (at Assets\Mapbox\Unity\MapboxAccess.cs:66)
Mapbox.Unity.MapboxAccess:get_Instance() (at Assets\Mapbox\Unity\MapboxAccess.cs:41)
Mapbox.Unity.Map.<SetupAccess>d__88:MoveNext() (at Assets\Mapbox\Unity\Map\AbstractMap.cs:609)
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
UnityEngine.MonoBehaviour:StartCoroutineManaged(String, Object)
UnityEngine.MonoBehaviour:StartCoroutine(String, Object)
UnityEngine.MonoBehaviour:StartCoroutine(String)
Mapbox.Unity.Map.AbstractMap:MapOnStartRoutine(Boolean) (at Assets\Mapbox\Unity\Map\AbstractMap.cs:534)
Mapbox.Unity.Map.AbstractMap:Start() (at Assets\Mapbox\Unity\Map\AbstractMap.cs:500)
指向SDK自带的MapBox脚本。我不知道是否应该将它们包括在这里,因为我没有以任何方式触及它们,而且它们非常通用并且指向许多其他脚本。
有人知道吗?或者我可以忽略这一点,我的应用程序会正常运行吗?到目前为止,据我所知,我的测试没有任何问题。
谢谢你和我一起思考!
文本表明 Mapbox.Unity.Map.AbstractMap 使用了一些非托管资源。这在处理图像或绘图的任何 class 中无处不在。我个人不知道这个class,但有了这个名字一点也不奇怪。
非托管资源未被 GC 及时自动清理。虽然通常有终结器最终清理该资源,但通常没有等待的选项。在这种情况下,似乎甚至有一个例外可以确保您不会忘记 - 非常方便!
您的工作是通过调用 Mapbox.Unity.Map.AbstractMap.Dispose()
.
确保在删除最后一个引用之前清理非托管资源
我的一般建议是,每当您处理任何可处理的东西时:“创建。使用。处理。全部在同一段代码中,最好使用 using statement。”通常建议重新设计您的代码,以便您可以尽可能地遵循该模式。
有一个非常罕见的例外。在极少数情况下,您必须实施 Dispose pattern.
Dispose 模式有两个用例:
- 您直接处理非托管资源。这基本上永远不会发生。 class 库已经涵盖了所有可能的情况。在这种情况下,您首先编写一个 Finalizer,然后为方便起见添加一个 Dispose 函数
- 您有一个实现 IDisposeable 的任何类型的字段。在这种情况下,您自己的 class 必须实现 IDisposeable。唯一目的是将 Dispose() 调用中继到您的类型包含的任何 Disposeable 字段。这是 99% 的用例。
由于代码大部分是相同的,通常终结器是 Dispose(bool) 函数的一部分。 fianlizer 和 Dispose 之间的唯一区别是:
- Finalizer 调用从不 中继到 Disposeable 字段。 Finalizer是每个Instance和GC之间的master。
- Dispose 调用总是 中继到 Disposeable 字段。该中继实际上是您首先实施 IDisposeable 的唯一原因。
以下错误只会发生在编辑器中,不会影响目标平台,因为 Assets/Mapbox/Unity/Telemetry/TelemetryEditor.cs
脚本中存在 UNITY_EDITOR 宏。
如果您仍然想修复此错误,则必须在使用完 UnityWebRequest 对象后调用 Dispose,无论请求成功还是失败,只需替换 PostWWW方法如下:
IEnumerator PostWWW(string url, string bodyJsonString)
{
byte[] bodyRaw = Encoding.UTF8.GetBytes(bodyJsonString);
#if UNITY_2017_1_OR_NEWER
using (UnityWebRequest postRequest = new UnityWebRequest(url, "POST"))
{
postRequest.SetRequestHeader("Content-Type", "application/json");
postRequest.downloadHandler = new DownloadHandlerBuffer();
postRequest.uploadHandler = new UploadHandlerRaw(bodyRaw);
yield return postRequest.SendWebRequest();
while (!postRequest.isDone) { yield return null; }
if (!postRequest.isNetworkError)
{
#else
var headers = new Dictionary<string, string>();
headers.Add("Content-Type", "application/json");
headers.Add("user-agent", GetUserAgent());
var www = new WWW(url, bodyRaw, headers);
yield return www;
while (!www.isDone) { yield return null; }
// www doesn't expose HTTP status code, relay on 'error' property
if (!string.IsNullOrEmpty(www.error))
{
#endif
PlayerPrefs.SetString(Constants.Path.TELEMETRY_TURNSTILE_LAST_TICKS_EDITOR_KEY, "0");
}
else
{
PlayerPrefs.SetString(Constants.Path.TELEMETRY_TURNSTILE_LAST_TICKS_EDITOR_KEY, DateTime.Now.Ticks.ToString());
}
}
}
我在我的 Unity 项目中使用 MapBox SDK,我只是显示地图。我不使用其他服务,例如路线等。不知何故,我总是收到内存泄漏的错误消息。由于我的项目是 Ios 和 Android 应用程序,因此内存泄漏对我来说听起来不太好哈哈。
引用错误是这样的:
A Native Collection has not been disposed, resulting in a memory leak. Allocated from:
Unity.Collections.NativeArray`1:.ctor(Byte[], Allocator)
UnityEngine.Networking.UploadHandlerRaw:.ctor(Byte[])
Mapbox.Unity.Telemetry.<PostWWW>d__8:MoveNext() (at Assets\Mapbox\Unity\Telemetry\TelemetryEditor.cs:82)
Mapbox.Unity.Utilities.Routine:MoveNext() (at Assets\Mapbox\Unity\Utilities\Runnable.cs:130)
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
UnityEngine.MonoBehaviour:StartCoroutineManaged2(IEnumerator)
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
Mapbox.Unity.Utilities.Routine:.ctor(IEnumerator) (at Assets\Mapbox\Unity\Utilities\Runnable.cs:116)
Mapbox.Unity.Utilities.Runnable:Run(IEnumerator) (at Assets\Mapbox\Unity\Utilities\Runnable.cs:47)
Mapbox.Unity.Telemetry.TelemetryEditor:SendTurnstile() (at Assets\Mapbox\Unity\Telemetry\TelemetryEditor.cs:37)
Mapbox.Unity.MapboxAccess:ConfigureTelemetry() (at Assets\Mapbox\Unity\MapboxAccess.cs:196)
Mapbox.Unity.MapboxAccess:SetConfiguration(MapboxConfiguration, Boolean) (at Assets\Mapbox\Unity\MapboxAccess.cs:108)
Mapbox.Unity.MapboxAccess:LoadAccessToken() (at Assets\Mapbox\Unity\MapboxAccess.cs:161)
Mapbox.Unity.MapboxAccess:.ctor() (at Assets\Mapbox\Unity\MapboxAccess.cs:66)
Mapbox.Unity.MapboxAccess:get_Instance() (at Assets\Mapbox\Unity\MapboxAccess.cs:41)
Mapbox.Unity.Map.<SetupAccess>d__88:MoveNext() (at Assets\Mapbox\Unity\Map\AbstractMap.cs:609)
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
UnityEngine.MonoBehaviour:StartCoroutineManaged(String, Object)
UnityEngine.MonoBehaviour:StartCoroutine(String, Object)
UnityEngine.MonoBehaviour:StartCoroutine(String)
Mapbox.Unity.Map.AbstractMap:MapOnStartRoutine(Boolean) (at Assets\Mapbox\Unity\Map\AbstractMap.cs:534)
Mapbox.Unity.Map.AbstractMap:Start() (at Assets\Mapbox\Unity\Map\AbstractMap.cs:500)
指向SDK自带的MapBox脚本。我不知道是否应该将它们包括在这里,因为我没有以任何方式触及它们,而且它们非常通用并且指向许多其他脚本。
有人知道吗?或者我可以忽略这一点,我的应用程序会正常运行吗?到目前为止,据我所知,我的测试没有任何问题。
谢谢你和我一起思考!
文本表明 Mapbox.Unity.Map.AbstractMap 使用了一些非托管资源。这在处理图像或绘图的任何 class 中无处不在。我个人不知道这个class,但有了这个名字一点也不奇怪。
非托管资源未被 GC 及时自动清理。虽然通常有终结器最终清理该资源,但通常没有等待的选项。在这种情况下,似乎甚至有一个例外可以确保您不会忘记 - 非常方便!
您的工作是通过调用 Mapbox.Unity.Map.AbstractMap.Dispose()
.
我的一般建议是,每当您处理任何可处理的东西时:“创建。使用。处理。全部在同一段代码中,最好使用 using statement。”通常建议重新设计您的代码,以便您可以尽可能地遵循该模式。
有一个非常罕见的例外。在极少数情况下,您必须实施 Dispose pattern.
Dispose 模式有两个用例:
- 您直接处理非托管资源。这基本上永远不会发生。 class 库已经涵盖了所有可能的情况。在这种情况下,您首先编写一个 Finalizer,然后为方便起见添加一个 Dispose 函数
- 您有一个实现 IDisposeable 的任何类型的字段。在这种情况下,您自己的 class 必须实现 IDisposeable。唯一目的是将 Dispose() 调用中继到您的类型包含的任何 Disposeable 字段。这是 99% 的用例。
由于代码大部分是相同的,通常终结器是 Dispose(bool) 函数的一部分。 fianlizer 和 Dispose 之间的唯一区别是:
- Finalizer 调用从不 中继到 Disposeable 字段。 Finalizer是每个Instance和GC之间的master。
- Dispose 调用总是 中继到 Disposeable 字段。该中继实际上是您首先实施 IDisposeable 的唯一原因。
以下错误只会发生在编辑器中,不会影响目标平台,因为 Assets/Mapbox/Unity/Telemetry/TelemetryEditor.cs
脚本中存在 UNITY_EDITOR 宏。
如果您仍然想修复此错误,则必须在使用完 UnityWebRequest 对象后调用 Dispose,无论请求成功还是失败,只需替换 PostWWW方法如下:
IEnumerator PostWWW(string url, string bodyJsonString)
{
byte[] bodyRaw = Encoding.UTF8.GetBytes(bodyJsonString);
#if UNITY_2017_1_OR_NEWER
using (UnityWebRequest postRequest = new UnityWebRequest(url, "POST"))
{
postRequest.SetRequestHeader("Content-Type", "application/json");
postRequest.downloadHandler = new DownloadHandlerBuffer();
postRequest.uploadHandler = new UploadHandlerRaw(bodyRaw);
yield return postRequest.SendWebRequest();
while (!postRequest.isDone) { yield return null; }
if (!postRequest.isNetworkError)
{
#else
var headers = new Dictionary<string, string>();
headers.Add("Content-Type", "application/json");
headers.Add("user-agent", GetUserAgent());
var www = new WWW(url, bodyRaw, headers);
yield return www;
while (!www.isDone) { yield return null; }
// www doesn't expose HTTP status code, relay on 'error' property
if (!string.IsNullOrEmpty(www.error))
{
#endif
PlayerPrefs.SetString(Constants.Path.TELEMETRY_TURNSTILE_LAST_TICKS_EDITOR_KEY, "0");
}
else
{
PlayerPrefs.SetString(Constants.Path.TELEMETRY_TURNSTILE_LAST_TICKS_EDITOR_KEY, DateTime.Now.Ticks.ToString());
}
}
}