RealityKit / visionOS – Memory not released after dismissing ImmersiveSpace with USDZ models

Hi everyone,

I’m encountering a memory overflow issue in my visionOS app and I’d like to confirm if this is expected behavior or if I’m missing something in cleanup.

App Context

  • The app showcases apartments in real scale using AR.
  • Apartments are heavy USDZ models (hundreds of thousands of triangles, high-resolution textures).
  • Users can walk inside the apartments, and performance is good even close to hardware limits.

Flow

  • The app starts in a full immersive space (RealityView) for selecting the apartment.
  • When an apartment is selected, a new ImmersiveSpace opens and the apartment scene loads.
  • The scene includes multiple USDZ models, EnvironmentResources, and dynamic textures for skyboxes.
  • When the user dismisses the experience, we attempt cleanup:
    • Nulling out all entity references.
    • Removing ModelComponents.
    • Clearing cached textures and skyboxes.
    • Forcing dictionaries/collections to empty.

Despite this cleanup, memory usage remains very high.

Problem

After dismissing the ImmersiveSpace, memory does not return to baseline. Check the attached screenshot of the profiling made using Instruments:

  1. Initial state: ~30MB (main menu).
  2. After loading models sequentially: ~3.3GB.
  3. Skybox textures bring it near ~4GB.
  4. After dismissing the experience (at ~01:00 mark): memory only drops slightly (to ~2.66GB).
  5. When loading the second apartment, memory continues to increase until ~5GB, at which point the app crashes due to memory pressure.

The issue is consistently visible under VM: IOSurface in Instruments. No leaks are detected.

So it looks like RealityKit (or lower-level frameworks) keeps caching meshes and textures, and does not free them when RealityView is ended. But for my use case, these resources should be fully released once the ImmersiveSpace is dismissed, since new apartments will load entirely different models and textures.

Cleanup Code Example

Here’s a simplified version of the cleanup I’m doing:

func clearAllRoomEntities() {
    for (entityName, entity) in entityFromMarker {
        entity.removeFromParent()

        if let modelEntity = entity as? ModelEntity {
            modelEntity.components.removeAll()
            modelEntity.children.forEach { $0.removeFromParent() }
            modelEntity.clearTexturesAndMaterials()
        }

        entityFromMarker[entityName] = nil
        removeSkyboxPortals(from: entityName)
    }
    entityFromMarker.removeAll()
}
extension ModelEntity {
    func clearTexturesAndMaterials() {
        guard var modelComponent = self.model else { return }

        for index in modelComponent.materials.indices {
            removeTextures(from: &modelComponent.materials[index])
        }

        modelComponent.materials.removeAll()
        self.model = modelComponent
        self.model = nil
    }

    private func removeTextures(from material: inout any Material) {
        if var pbr = material as? PhysicallyBasedMaterial {
            pbr.baseColor.texture = nil
            pbr.emissiveColor.texture = nil
            pbr.metallic.texture = nil
            pbr.roughness.texture = nil
            pbr.normal.texture = nil
            pbr.ambientOcclusion.texture = nil
            pbr.clearcoat.texture = nil
            material = pbr
        } else if var simple = material as? SimpleMaterial {
            simple.color.texture = nil
            material = simple
        }
    }
}

Questions

  1. Is this expected RealityKit behavior (textures/meshes cached internally)?
  2. Is there a way to force RealityKit to release GPU resources tied to USDZ models when they’re no longer used?
  3. Should dismissing the ImmersiveSpace automatically free those IOSurfaces, or do I need to handle this differently?
  4. Any guidance, best practices, or confirmation would be hugely appreciated.

Thanks in advance!

Hello @giovanniR2U , thank you for your question!

Swift uses automatic reference counting (ARC), which means memory is automatically cleaned up for any object that is no longer referenced. Often, this is as simple as removing a root entity from the scene (thereby removing each of its descendants) or dismissing the ImmersiveScene entirely. Going through each entity in the scene and calling removeFromParent() for every entity and setting texture references to nil should not be necessary. The important thing is to verify there are no more references in memory, so make sure you don't have any variables lying around that might still be pointing to the objects you want to unload.

When you load your textures sequentially, are you keeping a reference to these textures in the system that loads them? This would prevent ARC from unloading those textures. You mention your app goes from 30 MB to 3.3 GB after loading just the textures, and then only goes to 2.6 GB after "dismissing the experience." This to me suggests that it is mostly textures that are still in memory, so I would recommend investigating the system loading them in the first place.

What you are seeing is not expected behavior, so in case you are seeing a memory leak with RealityKit itself, I recommend submitting feedback using Feedback Assistant and then sharing the ticket number here. It would be very helpful if you are able to share a project that reproduces this issue to help us track this on our end.

Let me know if you have more questions! I'm happy to help get to the bottom of this issue. Thank you!

Hello again @giovanniR2U .

Additionally, I recommend upgrading to visionOS 26 if you have not already. There are a number of new features and bug fixes related to memory management that may be relevant. Let me know if you have any questions!

Accepted Answer

Thanks to @Vision Pro Engineer answer, I could find the solution!

I tried checking for others references to the entities added in the ImmersiveSpace. Indeed, I could find some references and auxiliary entities that were not being explicitly removed from parent on dismiss. Also, I made sure to remove the rootEntity from the parent as I was not doing it before (I thought that removing the entities themselves would be enough).

It worked! Now the memory almost goes back to the state before the ImmersiveSpace was opened.

However, I did had to:

  • force the .removeFromParent() for each non-nullable entities (MOST RELEVANT) ;
  • reset the arrays and dictionary referencing entities to [] or [:];
  • nulling every reference for Textures, EnvironmentResources and Colliders etc (LESS RELEVANT);

I think that this should not be the expected behavior. I guess that most developers would assume that this clean up is supposed to be automatically made when the ImmersiveSpace is dismissed and/or when the RealityView is dismissed.

But thanks a lot for the help!

Hello,

I'm reopening this issue because I noticed that with the new VisionOS 26, the memory cleaning got messed up again.

As I said in my previous answer, I'd managed to solve the issue in the VisionOS 2.6.

Everything was working fine, but after upgrading to VisionOS 26, the issue came back. The exact same code that was working before stopped working. The memory is no longer being cleaned after removing entities from parent and clearing all references to them.

Is it some kind of breaking change to the ARC system?

@Vision Pro Engineer I'd really appreciate your help here!

Thanks in advance

Hello @giovanniR2U, I'm sorry to hear your code has stopped working.

Could you please submit a bug report using Feedback Assistant, and include as much detail as possible? You can even share your project confidentially with us there, and that might help us get a better idea of what is going on. If you then share your ticket number here I'll be able to see your ticket myself on our end.

Rest assured, there have not been any changes to how the Swift programming language uses automatic reference counting.

One thing you can try is attempt to recreate the issue in a smaller, isolated project. Is that something that would be possible? If so, that would be very helpful to include in a bug report.

Thank you!

The OS is shutting down our app due to high memory usage. In our app, each “room” is an immersive space with a skybox. Within the room we navigate through a variety of spaces, each with its own texture to be used in the skybox material. Each time we swap out the skybox material the memory usage grows significantly.

When we exit each room (immersive space), we remove the skybox (removeFromParent), assign it to nil, and have even tried to set the Texture to nil. Exiting a room doesn’t appear to release any of the allocated memory. Each time we enter a new room the memory usage climbs until we reach about 5G, by which time the app is closed by the system. Should we remove the UnlitMaterial reference in each room object as well upon exiting immersive space?

We also observed that when we navigated through spaces that we’ve previously visited, the Persistent Bytes don’t grow even though we are releasing the Texture resource upon exiting a space, then re-creating the TextureResource when we enter it again. Could these Textures or Materials be cached by the system? Our images are high resolution 16K images.

VisionOS 26, Xcode 26.1.1

Meanwhile we are going through the code base line by line to make sure we haven’t maintained a strong reference to the skybox or texture resource elsewhere.

Attaching memory allocations at shutdown:

Thanks, bvsdev

Hi @bvsdev ,

Could you please file a bug report using Feedback Assistant? And please include a sysdiagnose if you can, then share the ticket number here.

Is there any code you can share related to how you are releasing your TextureResource? For each of your rooms, when a person leaves the room, are you keeping the room entities around but attempting to manually release textures by modifying their materials, or are you using removeFromParent(preservingWorldTransform:) to remove the entire room entity at once? I recommend the latter option if possible, ensuring you are also discarding any references to the room entity.

Thank you for your question!

Filed ticket number FB21327973 with sample test app demonstrating memory pressure with UnlitMaterial usage.

Also seeing an Error on some images, although they are still being rendered on the device:

IOSurface creation failed: e00002c2 parentID: 00000000 properties: { IOSurfaceAddress = 4380393472; IOSurfaceAllocSize = 20027995; IOSurfaceCacheMode = 0; IOSurfaceMapCacheAttribute = 1; IOSurfaceName = CMPhoto; IOSurfacePixelFormat = 1246774599; } IOSurface creation failed: e00002c2 parentID: 00000000 property: IOSurfaceCacheMode

RealityKit / visionOS – Memory not released after dismissing ImmersiveSpace with USDZ models
 
 
Q