Hi all, sorry for my english.
After each command /test input, free memory on the client side is reduced. How to manually remove all components by CuiRawImageComponent?
A test case to test the memory leak.
Code:using System; using Oxide.Game.Rust.Cui; using UnityEngine;namespace Oxide.Plugins { [Info("Test", "tester", 0.1)] [Description("For test")] class Test : RustPlugin { [ChatCommand("test")] void test(BasePlayer player, string command, string[] args) { var elements = new CuiElementContainer(); var mainPanel = elements.Add(new CuiPanel { CursorEnabled = true, Image = { Color = "0.24 0.25 0.25 0.95" }, RectTransform = { AnchorMin = "0.1 0.1", AnchorMax = "0.4 0.4" } }); elements.Add(new CuiButton { Button = { Close = mainPanel, Color = "1.0 0.07 0.07 0.85" }, RectTransform = { AnchorMin = "0.95 0.95", AnchorMax = "0.999 1.0005" }, Text = { Text = "X", FontSize = 8, Align = TextAnchor.MiddleCenter } }, mainPanel); for (int i = 0; i < 5; i++) { elements.Add(new CuiElement { Name = CuiHelper.GetGuid(), Parent = mainPanel, Components = { new CuiRawImageComponent { Sprite = "assets/content/textures/generic/fulltransparent.tga", Url = "https://homepages.cae.wisc.edu/~ece533/images/airplane.png" //Color = "1.0 1.0 1.0 1.0" }, new CuiRectTransformComponent { AnchorMin = "0.0 0.0", AnchorMax = "1.0 0.9" } } }); } CuiHelper.AddUi(player, elements); } } }
Solved Memory keeps going up with CUI
Discussion in 'Rust Development' started by athlon.89, Apr 23, 2016.
-
Wulf Community Admin
You aren't destroying the existing CUI, you are just adding to it. You'd need to store it in a variable per player, and then destroy it when you call it again.
-
I tried to do it, but it does not work. Memory is not released.
Code:class Test : RustPlugin { private CuiElementContainer elements = new CuiElementContainer(); [ChatCommand("test")] void test(BasePlayer player, string command, string[] args) { SendReply(player, elements.Count.ToString()); if (elements.Count > 0) { foreach (var elem in elements) { CuiHelper.DestroyUi(player, elem.Name); } elements.Clear(); } var mainPanel = elements.Add(new CuiPanel { CursorEnabled = true, Image = { Color = "0.24 0.25 0.25 0.95" }, RectTransform = { AnchorMin = "0.1 0.1", AnchorMax = "0.4 0.4" } }); elements.Add(new CuiButton { Button = { Close = mainPanel, Color = "1.0 0.07 0.07 0.85" }, RectTransform = { AnchorMin = "0.95 0.95", AnchorMax = "0.999 1.0005" }, Text = { Text = "X", FontSize = 8, Align = TextAnchor.MiddleCenter } }, mainPanel); for (int i = 0; i < 5; i++) { elements.Add(new CuiElement { Name = CuiHelper.GetGuid(), Parent = mainPanel, Components = { new CuiRawImageComponent { Sprite = "assets/content/textures/generic/fulltransparent.tga", Url = "https://homepages.cae.wisc.edu/~ece533/images/airplane.png" //Color = "1.0 1.0 1.0 1.0" }, new CuiRectTransformComponent { AnchorMin = "0.0 0.0", AnchorMax = "1.0 0.9" } } }); } CuiHelper.AddUi(player, elements); } }
-
Because you are still adding elements to it and not removing it.
You have your
private CuiElementContainer elements = new CuiElementContainer();
See that as a list.
Every time someone does the command it will add new objects to that list so the list will grow bigger and bigger. -
Code:class Test : RustPlugin { private CuiElementContainer elements = new CuiElementContainer(); [ChatCommand("test")] void test(BasePlayer player, string command, string[] args) { SendReply(player, "Before clear: " + elements.Count.ToString()); if (elements.Count > 0) { foreach (var elem in elements) { CuiHelper.DestroyUi(player, elem.Name); } elements.Clear(); } SendReply(player, "After clear: " + elements.Count.ToString()); var mainPanel = elements.Add(new CuiPanel { CursorEnabled = true, Image = { Color = "0.24 0.25 0.25 0.95" }, RectTransform = { AnchorMin = "0.1 0.1", AnchorMax = "0.4 0.4" } }); elements.Add(new CuiButton { Button = { Close = mainPanel, Color = "1.0 0.07 0.07 0.85" }, RectTransform = { AnchorMin = "0.95 0.95", AnchorMax = "0.999 1.0005" }, Text = { Text = "X", FontSize = 8, Align = TextAnchor.MiddleCenter } }, mainPanel); for (int i = 0; i < 5; i++) { elements.Add(new CuiElement { Name = CuiHelper.GetGuid(), Parent = mainPanel, Components = { new CuiRawImageComponent { Sprite = "assets/content/textures/generic/fulltransparent.tga", Url = "https://homepages.cae.wisc.edu/~ece533/images/airplane.png" //Color = "1.0 1.0 1.0 1.0" }, new CuiRectTransformComponent { AnchorMin = "0.0 0.0", AnchorMax = "1.0 0.9" } } }); } CuiHelper.AddUi(player, elements); } }
-
-
Wulf Community Admin
I don't think .Clear() does the same as .Destroy().
-
Also isn't
Code:CuiHelper.AddUi(player, elements);
-
Thanks for your answers guys.
Code:elements = null; GC.Collect();
Code:elements.RemoveRange(0, elements.Count);
-
Wulf Community Admin
-
Code:class Test : RustPlugin { Dictionary<ulong, string> GuiInfo = new Dictionary<ulong, string>(); [ChatCommand("test")] void test(BasePlayer player, string command, string[] args) { for (int i = 0; i < 5; i++) { string guiInfo; if (GuiInfo.TryGetValue(player.userID, out guiInfo)) CuiHelper.DestroyUi(player, guiInfo); var elements = new CuiElementContainer(); GuiInfo[player.userID] = CuiHelper.GetGuid(); elements.Add(new CuiElement { Name = GuiInfo[player.userID], Components = { new CuiRawImageComponent { Sprite = "assets/content/textures/generic/fulltransparent.tga", Url = "https://homepages.cae.wisc.edu/~ece533/images/airplane.png" //Color = "1.0 1.0 1.0 1.0" }, new CuiRectTransformComponent { AnchorMin = "0.45 0.15", AnchorMax = "0.55 0.35" } } }); CuiHelper.AddUi(player, elements); } } void Unload() { foreach (var player in BasePlayer.activePlayerList) { // Destroy existing UI string guiInfo; if (GuiInfo.TryGetValue(player.userID, out guiInfo)) CuiHelper.DestroyUi(player, guiInfo); } } }
-
Wulf Community Admin
-
He appears to be on to something, because in my testing with the code he has supplied, and it being properly destroyed, it is still using the memory and not freeing it. Running the console commands gc.collect and gc.unload on the client seem to unload most of it, but it isn't done via DestroyUi. (Or so it seems)
-
Wulf Community Admin
-
Code:class Test : RustPlugin { Dictionary<ulong, string> GuiInfo = new Dictionary<ulong, string>(); [ChatCommand("test")] void test(BasePlayer player, string command, string[] args) { for (int i = 0; i < 5; i++) { string guiInfo; if (GuiInfo.TryGetValue(player.userID, out guiInfo)) CuiHelper.DestroyUi(player, guiInfo); var elements = new CuiElementContainer(); GuiInfo[player.userID] = CuiHelper.GetGuid(); elements.Add(new CuiElement { Name = GuiInfo[player.userID], Components = { new CuiRawImageComponent { Sprite = "assets/content/textures/generic/fulltransparent.tga", Url = "https://homepages.cae.wisc.edu/~ece533/images/airplane.png" //Color = "1.0 1.0 1.0 1.0" }, new CuiRectTransformComponent { AnchorMin = "0.45 0.15", AnchorMax = "0.55 0.35" } } }); CuiHelper.AddUi(player, elements); } player.Command("gc.unload"); player.Command("gc.collect"); } void Unload() { foreach (var player in BasePlayer.activePlayerList) { // Destroy existing UI string guiInfo; if (GuiInfo.TryGetValue(player.userID, out guiInfo)) CuiHelper.DestroyUi(player, guiInfo); } } }
-
In any application memory will increase until the garbage collector fires, unless you're using RC GCs or RC/STW GCs and not pure STW GCs.
It'd only be an issue if memory was never freed, in which case you either got a memory leak at the C++ level, a reference cycle for RC GCs, a thread leak or you simply didn't account for some reference dragging a lot of garbage along (anonymous classes in Java can cause this issue for instance).
The reason why Rust frees some memory before a GC fires is because that's the engine stuff written in C++, and that is either RC GC'd or manually freed. -
Wulf Community Admin
-
Wouldnt it be nice of someone made a garbage collector plugin? Does the server also need manual garbage collection?
-
Wulf Community Admin
-
Neither does the server need manual garbage collection (the suggestion of which makes it seem like you misunderstood the concept).
You should never call the collect method yourself unless you're blocking the GC due to profiling.
If you want to reduce throughput for lower latency you should always configure that directly in the GC, not through some script that runs on top of the GC.