Unity ScrollView在Android与iOS平台的重复打开关闭性能劣化问题
Hey everyone, I wanted to share a solution to a Unity mobile lag issue that had me stuck for weeks. A month ago, I posted this problem on Stack Overflow but got no responses and the question was automatically removed. Now that I’ve fixed it, I’m reposting here to help anyone else dealing with the same headache.
The Problem
In my Unity game, there’s an inventory screen with a ScrollView that gets dynamically populated with item UI elements at runtime. Everything worked flawlessly in the Unity Editor, but on Android and iOS devices, after players opened and closed the inventory several times, the game slowed to a crawl—almost unplayable.
Root Cause
After digging into Unity’s Profiler connected to a test mobile device, I found the issue: memory leaks from improperly cleaned-up UI objects. Every time the inventory opened, I was instantiating new item prefabs for the ScrollView. But when closing the screen, I only disabled the parent inventory panel instead of destroying or disabling the individual instantiated items. Over repeated cycles, thousands of unused UI objects piled up in memory, triggering constant garbage collection spikes and overwhelming the device’s rendering pipeline.
The Solution
I implemented two key fixes to resolve the lag completely:
1. Clean Up Instantiated Items on Close
Instead of just hiding the inventory panel, explicitly destroy or disable the dynamic item elements when closing the screen. Here’s a simple example of destroying items:
public void CloseInventory() { // Reference to the ScrollView's content container holding item elements Transform scrollContent = inventoryScrollView.content.transform; // Loop through all child items and destroy them foreach (Transform childItem in scrollContent) { Destroy(childItem.gameObject); } // Finally disable the inventory panel inventoryPanel.SetActive(false); }
2. Use an Object Pool for Better Performance
For frequent inventory access, instantiating/destroying objects every time creates unnecessary GC spikes. An object pool reuses existing UI elements instead:
public GameObject inventoryItemPrefab; private List<GameObject> pooledItems = new List<GameObject>(); // Get a reusable item from the pool (or create a new one if needed) public GameObject GetPooledItem() { foreach (GameObject item in pooledItems) { if (!item.activeInHierarchy) { item.SetActive(true); return item; } } // No inactive items left—create a new one and add to the pool GameObject newItem = Instantiate(inventoryItemPrefab); pooledItems.Add(newItem); return newItem; } // Populate inventory using the pool public void PopulateInventory(List<Item> playerItems) { foreach (Item item in playerItems) { GameObject uiItem = GetPooledItem(); uiItem.transform.SetParent(inventoryScrollView.content.transform, false); // Update the item's UI (sprite, count, name, etc.) uiItem.GetComponent<InventoryItemUI>().SetItemData(item); } } // Disable all pooled items when closing inventory public void CloseInventory() { foreach (GameObject item in pooledItems) { if (item.activeInHierarchy) { item.SetActive(false); } } inventoryPanel.SetActive(false); }
Additional Tips
- Profile on mobile: Always test with Unity’s Profiler connected to an actual mobile device—desktop/editor behavior can hide mobile-specific performance issues.
- Check for orphaned references: Ensure you’re not keeping unintended references to UI objects (like in static lists) that prevent garbage collection.
内容的提问来源于stack exchange,提问作者Raffaele Tasso

