From b955bce1a70a76b1de3827ec318d102befece6f6 Mon Sep 17 00:00:00 2001 From: Felix Schwarz Date: Wed, 18 Sep 2024 23:00:18 +0200 Subject: [PATCH] - OCBookmark+DataItem: avoid de-serialization (and memory consumption) of avatar resource in minimum memory configuration - OCBookmarkManager: quickly release memory from serialized and deserialized bookmarks via autoreleasepools to reduce peak memory consumption loading and saving bookmarks - OCCoreManager: reduce SQLite memory limit from 1 MB to 512 KB - OCIPNotificationCenter: handle notifications inside an autorelease pool to reduce peak memory usage - OCDatabase: reduce segment size stepping through the database to reduce peak memory usage - OCSQLiteDB: ensure sqlite3_soft_heap_limit64() becomes effective by enabling SQLite memory tracking --- ownCloudSDK/Bookmark/OCBookmark+DataItem.m | 16 +- .../Resource Management/OCBookmarkManager.m | 172 +++++++++--------- .../Resource Management/OCCoreManager.m | 2 +- ownCloudSDK/Toolkit/OCIPNotificationCenter.m | 4 +- ownCloudSDK/Vaults/Database/OCDatabase.m | 4 +- .../Vaults/Database/SQLite/OCSQLiteDB.m | 1 + 6 files changed, 108 insertions(+), 91 deletions(-) diff --git a/ownCloudSDK/Bookmark/OCBookmark+DataItem.m b/ownCloudSDK/Bookmark/OCBookmark+DataItem.m index 3e522240..6c0f88d0 100644 --- a/ownCloudSDK/Bookmark/OCBookmark+DataItem.m +++ b/ownCloudSDK/Bookmark/OCBookmark+DataItem.m @@ -20,6 +20,7 @@ #import "OCDataTypes.h" #import "OCResource.h" #import "OCMacros.h" +#import "OCCoreManager.h" @implementation OCBookmark (DataItem) @@ -35,10 +36,17 @@ - (OCDataItemReference)dataItemReference - (OCDataItemVersion)dataItemVersion { - OCResource *avatarResource = OCTypedCast(self.avatar, OCResource); - NSString *avatarVersion = ((avatarResource != nil) ? avatarResource.version : @""); - - return ([NSString stringWithFormat:@"%@%@%@%@%@%@%@", self.name, self.url, self.originURL, self.userName, self.userDisplayName, self.authenticationDataID, avatarVersion]); + if (OCCoreManager.sharedCoreManager.memoryConfiguration != OCCoreMemoryConfigurationMinimum) + { + OCResource *avatarResource = OCTypedCast(self.avatar, OCResource); + NSString *avatarVersion = ((avatarResource != nil) ? avatarResource.version : @""); + + return ([NSString stringWithFormat:@"%@%@%@%@%@%@%@", self.name, self.url, self.originURL, self.userName, self.userDisplayName, self.authenticationDataID, avatarVersion]); + } + else + { + return ([NSString stringWithFormat:@"%@%@%@%@%@%@", self.name, self.url, self.originURL, self.userName, self.userDisplayName, self.authenticationDataID]); + } } @end diff --git a/ownCloudSDK/Resource Management/OCBookmarkManager.m b/ownCloudSDK/Resource Management/OCBookmarkManager.m index fa88d792..427a332f 100644 --- a/ownCloudSDK/Resource Management/OCBookmarkManager.m +++ b/ownCloudSDK/Resource Management/OCBookmarkManager.m @@ -78,108 +78,112 @@ - (void)loadBookmarks NSData *bookmarkData; NSURL *bookmarkStoreURL; - if ((bookmarkStoreURL = self.bookmarkStoreURL) != nil) - { - if ((bookmarkData = [[NSData alloc] initWithContentsOfURL:bookmarkStoreURL]) != nil) + @autoreleasepool { + if ((bookmarkStoreURL = self.bookmarkStoreURL) != nil) { - NSMutableArray *reconstructedBookmarks = nil; - - @try + if ((bookmarkData = [[NSData alloc] initWithContentsOfURL:bookmarkStoreURL]) != nil) { - NSArray *existingBookmarks = nil; - NSMutableArray *loadedBookmarks = nil; + NSMutableArray *reconstructedBookmarks = nil; - loadedBookmarks = [NSKeyedUnarchiver unarchiveObjectWithData:bookmarkData]; - - @synchronized(self) + @try { - existingBookmarks = [_bookmarks copy]; - } + NSArray *existingBookmarks = nil; + NSMutableArray *loadedBookmarks = nil; - if (existingBookmarks != nil) - { - // Look for changed bookmarks and update only those that don't match the existing instances - reconstructedBookmarks = [NSMutableArray new]; + loadedBookmarks = [NSKeyedUnarchiver unarchiveObjectWithData:bookmarkData]; - for (OCBookmark *loadedBookmark in loadedBookmarks) + @synchronized(self) { - OCBookmark *existingBookmark = nil; + existingBookmarks = [_bookmarks copy]; + } - for (OCBookmark *bookmark in existingBookmarks) - { - if ([bookmark.uuid isEqual:loadedBookmark.uuid]) - { - existingBookmark = bookmark; - break; - } - } + if (existingBookmarks != nil) + { + // Look for changed bookmarks and update only those that don't match the existing instances + reconstructedBookmarks = [NSMutableArray new]; - if (existingBookmark == nil) + for (OCBookmark *loadedBookmark in loadedBookmarks) { - // New bookmark - [reconstructedBookmarks addObject:loadedBookmark]; - } - else - { - // Existing bookmark - check for changes - NSError *error = nil; - NSData *existingBookmarkData = nil, *loadedBookmarkData = nil; - BOOL isIdentical = NO; + OCBookmark *existingBookmark = nil; - if ((existingBookmarkData = [NSKeyedArchiver archivedDataWithRootObject:existingBookmark requiringSecureCoding:NO error:&error]) != nil) + for (OCBookmark *bookmark in existingBookmarks) { - if ((loadedBookmarkData = [NSKeyedArchiver archivedDataWithRootObject:loadedBookmark requiringSecureCoding:NO error:&error]) != nil) + if ([bookmark.uuid isEqual:loadedBookmark.uuid]) { - if ([existingBookmarkData isEqual:loadedBookmarkData]) - { - isIdentical = YES; - } + existingBookmark = bookmark; + break; } } - if (isIdentical) + if (existingBookmark == nil) { - // Bookmark unchanged - use existing copy - [reconstructedBookmarks addObject:existingBookmark]; + // New bookmark + [reconstructedBookmarks addObject:loadedBookmark]; } else { - // Bookmark changed - use loaded copy - [reconstructedBookmarks addObject:loadedBookmark]; + // Existing bookmark - check for changes + @autoreleasepool { + NSError *error = nil; + NSData *existingBookmarkData = nil, *loadedBookmarkData = nil; + BOOL isIdentical = NO; + + if ((existingBookmarkData = [NSKeyedArchiver archivedDataWithRootObject:existingBookmark requiringSecureCoding:NO error:&error]) != nil) + { + if ((loadedBookmarkData = [NSKeyedArchiver archivedDataWithRootObject:loadedBookmark requiringSecureCoding:NO error:&error]) != nil) + { + if ([existingBookmarkData isEqual:loadedBookmarkData]) + { + isIdentical = YES; + } + } + } + + if (isIdentical) + { + // Bookmark unchanged - use existing copy + [reconstructedBookmarks addObject:existingBookmark]; + } + else + { + // Bookmark changed - use loaded copy + [reconstructedBookmarks addObject:loadedBookmark]; + } + } } } } + else + { + // No bookmarks previously loaded - just use the loaded ones + reconstructedBookmarks = loadedBookmarks; + } } - else - { - // No bookmarks previously loaded - just use the loaded ones - reconstructedBookmarks = loadedBookmarks; + @catch(NSException *exception) { + OCLogError(@"Error loading bookmarks: %@", OCLogPrivate(exception)); } - } - @catch(NSException *exception) { - OCLogError(@"Error loading bookmarks: %@", OCLogPrivate(exception)); - } - @synchronized(self) - { - if (reconstructedBookmarks != nil) + @synchronized(self) { - _bookmarks = reconstructedBookmarks; + if (reconstructedBookmarks != nil) + { + _bookmarks = reconstructedBookmarks; + } + else + { + [_bookmarks removeAllObjects]; + } + + [_bookmarksDatasource setVersionedItems:_bookmarks]; } - else + } + else + { + @synchronized(self) { [_bookmarks removeAllObjects]; + [_bookmarksDatasource setVersionedItems:_bookmarks]; } - - [_bookmarksDatasource setVersionedItems:_bookmarks]; - } - } - else - { - @synchronized(self) - { - [_bookmarks removeAllObjects]; - [_bookmarksDatasource setVersionedItems:_bookmarks]; } } } @@ -187,21 +191,23 @@ - (void)loadBookmarks - (void)saveBookmarks { - @synchronized(self) - { - if (_bookmarks != nil) + @autoreleasepool { + @synchronized(self) { - NSData *bookmarkData = nil; - - @try + if (_bookmarks != nil) { - bookmarkData = [NSKeyedArchiver archivedDataWithRootObject:_bookmarks]; - } - @catch(NSException *exception) { - OCLogError(@"Error archiving bookmarks: %@", OCLogPrivate(exception)); - } + NSData *bookmarkData = nil; - [bookmarkData writeToURL:self.bookmarkStoreURL atomically:YES]; + @try + { + bookmarkData = [NSKeyedArchiver archivedDataWithRootObject:_bookmarks]; + } + @catch(NSException *exception) { + OCLogError(@"Error archiving bookmarks: %@", OCLogPrivate(exception)); + } + + [bookmarkData writeToURL:self.bookmarkStoreURL atomically:YES]; + } } } diff --git a/ownCloudSDK/Resource Management/OCCoreManager.m b/ownCloudSDK/Resource Management/OCCoreManager.m index 77c4e69d..3993fef6 100644 --- a/ownCloudSDK/Resource Management/OCCoreManager.m +++ b/ownCloudSDK/Resource Management/OCCoreManager.m @@ -425,7 +425,7 @@ - (void)setMemoryConfiguration:(OCCoreMemoryConfiguration)memoryConfiguration switch (memoryConfiguration) { case OCCoreMemoryConfigurationMinimum: - [OCSQLiteDB setMemoryLimit:(1 * 1024 * 1024)]; // Set 1 MB memory limit for SQLite; + [OCSQLiteDB setMemoryLimit:(1 * 1024 * 512)]; // Set 0.5 MB memory limit for SQLite; break; default: break; diff --git a/ownCloudSDK/Toolkit/OCIPNotificationCenter.m b/ownCloudSDK/Toolkit/OCIPNotificationCenter.m index ddfa8eb7..815f6888 100644 --- a/ownCloudSDK/Toolkit/OCIPNotificationCenter.m +++ b/ownCloudSDK/Toolkit/OCIPNotificationCenter.m @@ -76,7 +76,9 @@ static void OCIPNotificationCenterCallback(CFNotificationCenterRef center, void { OCIPNotificationCenter *notificationCenter = (__bridge OCIPNotificationCenter *)observer; - [notificationCenter deliverNotificationForName:(__bridge OCIPCNotificationName)name]; + @autoreleasepool { + [notificationCenter deliverNotificationForName:(__bridge OCIPCNotificationName)name]; + } } - (void)enable:(BOOL)enable observationForName:(NSString *)name diff --git a/ownCloudSDK/Vaults/Database/OCDatabase.m b/ownCloudSDK/Vaults/Database/OCDatabase.m index faf0ca02..4d0e8dd3 100644 --- a/ownCloudSDK/Vaults/Database/OCDatabase.m +++ b/ownCloudSDK/Vaults/Database/OCDatabase.m @@ -336,7 +336,7 @@ - (void)addCacheItems:(NSArray *)items syncAnchor:(OCSyncAnchor)syncA completionHandler(self, error); } }]]; - } segmentSize:((_memoryConfiguration == OCCoreMemoryConfigurationMinimum) ? 20 : 200)]; + } segmentSize:((_memoryConfiguration == OCCoreMemoryConfigurationMinimum) ? 10 : 200)]; } - (void)updateCacheItems:(NSArray *)items syncAnchor:(OCSyncAnchor)syncAnchor completionHandler:(OCDatabaseCompletionHandler)completionHandler @@ -452,7 +452,7 @@ - (void)updateCacheItems:(NSArray *)items syncAnchor:(OCSyncAnchor)sy completionHandler(self, error); } }]]; - } segmentSize:((_memoryConfiguration == OCCoreMemoryConfigurationMinimum) ? 20 : 200)]; + } segmentSize:((_memoryConfiguration == OCCoreMemoryConfigurationMinimum) ? 10 : 200)]; } - (void)removeCacheItems:(NSArray *)items syncAnchor:(OCSyncAnchor)syncAnchor completionHandler:(OCDatabaseCompletionHandler)completionHandler diff --git a/ownCloudSDK/Vaults/Database/SQLite/OCSQLiteDB.m b/ownCloudSDK/Vaults/Database/SQLite/OCSQLiteDB.m index 057b5eb6..d79ca037 100644 --- a/ownCloudSDK/Vaults/Database/SQLite/OCSQLiteDB.m +++ b/ownCloudSDK/Vaults/Database/SQLite/OCSQLiteDB.m @@ -1245,6 +1245,7 @@ - (void)flushCache + (int64_t)setMemoryLimit:(int64_t)memoryLimit { + sqlite3_config(SQLITE_CONFIG_MEMSTATUS, true); int64_t previousMemoryLimit = sqlite3_soft_heap_limit64(memoryLimit); OCLogDebug(@"Changed memory limit from %lld to %lld bytes", previousMemoryLimit, memoryLimit);