Having a hard time De-allocating large arrays and objects we use for Saving Data ?

Hello Everyone I am working on a small mobile experience in which the core experience is recording and replaying an objects movements and interactions within a single scene. While everything is working well for the most part I am having a really hard time de-allocating and re-allocating memory for large arrays and data objects we use to hold the audio and recorded object data over time .

Basically once we save roughly 1 minute of recorded data ( audio , various transforms on various objects) we get about 10 - 12 Mb’s of data and store it to the memory of the mobile device . We take all of this data , grabbing the various " Trackers" we use that record /store the object transforms , and create a float array of audio data for our accompanying audio clip .

During the process of actually saving this data we convert the object Transform data trackers to JSON and store the data in player prefs and then we write the audio bytes to memory ( I will share all my code for this at the bottom) . It seems like somewhere I am causing a memory " Leak " where some of the data is still being retained and after roughly 6 save processes the games frame rate nose dives to the point of essentially pausing the game .

Does anyone who has saved large data structures or a large amount of memory have an idea of what I may be doing wrong or certain memory considerations I may be overlooking that Unity has ?

My save process is mainly broken down into 3 Separate functions , One for storing and saving audio data, one for storing the object data in player prefs, and one last area where we save various save details to an overall data manager .

Here is my code for converting the object to Json and saving to player prefs ( the area I least likely to expect the memory issues to come from )

[FONT=Courier New]//*** Writing a function to save and eventually load our data slots indepenedently of eachother so that we can load lal the data into memory as quickly as possible 
	public void SaveDataSlot( int saveDataSlot){
		
		//*** First we convert the playback data to Json and serialize the data
		string xData = JsonHelper.Serialize(PlayGlobal.level.PlaybackObject);
		
		//*** We then use the encryption algorithm to encrypt the data 
		string xEncryptedData = SecurityHelper.AES_Encrypt(xData, AES_KEY);
		
		//*** We use the Hash  to give us a code  to decrypt the existing data 
		string xHash = xEncryptedData.Substring(HASH_START, HASH_LENGTH);
		
		xHash = xHash.Length < HASH_LENGTH ? xHash.PadRight(HASH_LENGTH - xHash.Length, '*') : xHash;
		xHash = SecurityHelper.Md5Sum(xHash + HASH_ADD);
		
		//*** Delete Pre-existing save data
		if(PlayerPrefs.HasKey(saveSlotName+saveDataSlot.ToString())){
		   PlayerPrefs.DeleteKey(saveSlotName+saveDataSlot.ToString());
		   PlayerPrefs.Save();
		}


		// we save our playerPrefs


		//*** Set Data
		PlayerPrefs.SetString(saveSlotName+saveDataSlot.ToString(), xEncryptedData);
		//PlayerPrefs.SetString(PLAYERPREFS_HASH, xHash);
		
		// we save our playerPrefs
		PlayerPrefs.Save();


		//PlayerPrefs.SetString(saveSlotName+saveDataSlot, "slotstring");


		//*** unload unused assets......
		Resources.UnloadUnusedAssets();
	}[/FONT]

Here IS my code for saving the Audio Data

[FONT=Courier New]//*** set playback sample count based on our sample rate  of 11025 samples * clip length
		playbackLayer.audioSamples = Mathf.CeilToInt(numOfSamples * 	playbackLayer.time);
		
		//*** create a float array the length of  the number of audio float samples we get back
		saveAudioFloatData = new float[playbackLayer.audioSamples];

		//*** here we get the audio data form the current playback and begin playing the clip

		PlayGlobal.recorder.layers[L].clip.GetData(saveAudioFloatData,0);

		//DebugTools.Log("Saving Data!!!! audioData.length = " + audioFloatData.Length.ToString());


		//**** Begin setting up audio clip name and setting the filepath
		 audioClipName ="Slot"+pslot.ToString()+ "clip" + L.ToString(); 
		 filePath = "";
			
			
			if(Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer || Application.platform == RuntimePlatform.WindowsEditor || Application.platform == RuntimePlatform.WindowsPlayer)
			{
				//*** Here we create a file path based on combining our file name with our data path
				filePath = Path.Combine(Application.dataPath ,audioClipName);
				
				// if this file exists , we read the existing bytes 
				if(File.Exists(filePath)){
					//*** we remove pre-existing data
					 System.IO.File.Delete(filePath);
					
				}
				//else if unity is currently on an iphone or android device  , load bytes from the persistent data path 
			}else if(Application.platform == RuntimePlatform.IPhonePlayer || Application.platform == RuntimePlatform.Android){
				
				//*** Here we create a file path based on combining our file name with our data path
				filePath = Path.Combine(Application.persistentDataPath ,audioClipName);
				
				// if this file exists , we read the existing bytes 
				if(File.Exists(filePath)){
					//*** we remove pre-existing data
					 System.IO.File.Delete(filePath);
					
				}
			}

			//yield return new WaitForEndOfFrame();

		//*** Convert float data to Bytes 
		ConvertFloatsToBytes();

		DebugTools.Log(" converting bytes to float ");
		yield return new WaitForEndOfFrame();
			
		System.IO.File.WriteAllBytes(filePath,saveAudioByteData);
		DebugTools.Log(" Saved bytes ");
		yield return new WaitForEndOfFrame();

			//*** free memory allocated for audio float data array 
			saveAudioByteData = null;
			saveAudioByteData = new byte[0];[/FONT]

and here is my code specifically for converting Byte Data To Float Data

[FONT=Courier New]public void  ConvertFloatsToBytes()
	{

		//*** create a byte array 4 times the size of the float array to hold the value for each float in bytes
		saveAudioByteData = new byte[saveAudioFloatData.Length * 4];

		//*** This function converts our current float array elements  to the same exact place in byte data 
		Buffer.BlockCopy(saveAudioFloatData,0,saveAudioByteData,0,saveAudioByteData.Length);

		//*** Release audio float  data array from  memory 
		saveAudioFloatData = null;
		saveAudioFloatData = new float[0];
	}[/FONT]

The last section is devoted to us populating our tracked transform data which is basically this particular snippet repeated and modified based on the tracked object in question .

[FONT=Courier New]	//*** We populate all of the tracker data for our given layer 
		for( i = 0 ; i < PlayGlobal.recorder.puppetTrackers[puppetIndex].trackers_TransformData.Count ; i++)
		{

			//*** Here we add our transform data to our current record data stack 
			PlayRecordData_Transform convertedTransformData  = new PlayRecordData_Transform();

			//*** Here we place the converted transform record data
					convertedTransformData	= PlayGlobal.recorder.puppetTrackers[puppetIndex].trackers_TransformData[i] as PlayRecordData_Transform;

			//*** probably unnecessary but just making sure the converted transform data exists 
				if (convertedTransformData !=null)
				{
				//*** and then we add the new transform tracker data to our tracker data list
				playbackLayer.puppetRecordTrackers[puppetNumInLayer].trackers_TransformData.Add(convertedTransformData);

				convertedTransformData = null;
				}

		}//*** End Transform Data Population[/FONT]

I thank you for any and all responses, I am not certain if the issue is merely me overlooking something in my code or if maybe some portion of my process is gradually wearing down my available memory over time and causing a massive performance drop .

The issue is a little more confusing because on a particular Android Device we are able to save roughly 11 times with a very limited performance drop while on all the iOS and most mobile Android devices the " 6 saves before meltdown " is very consistent .

I suspect that considering we are writing byte arrays with hundreds of thousands of elements and my individual save trackers can have upwards of 8,000 data members in their lists somewhere I am not freeing up th extraordinary amount of memory our save process uses .

not 100% sure, but setting the arrays to null only marks them for GC cleanup later and doesn’t instantly release… is it possible to just set it to a new array (let GC do it’s business) and/or perhaps force a GC clean up somewhere in there?

I was only skeptical of forcing garbage collection because literally every resource I said to avoid calling the garbage collector explicitly .

granted I don’t know many games or projects like ours that produce and store so much data in memory at one time so maybe we are an extraordinary case where that won’t be the worst idea .

I’m guessing though, that these large sets of data are used primarily in the editor… right? and so would it really be a big deal for the cleanup once in while?

You should take the time to profile your game when it gets super slow. That will tell you why it’s slow. That’s what profilers are for!

Also, I wouldn’t advise dumping tons of JSON into the player prefs. It might be fine, but that system is meant more for things like “YInvert=True” and not as a general savegame facility. You should write that data out to an actual file, much like you do the audio data.