Skip to content

Commit

Permalink
- OCBookmark+DataItem: avoid de-serialization (and memory consumption…
Browse files Browse the repository at this point in the history
…) 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
  • Loading branch information
felix-schwarz committed Sep 19, 2024
1 parent 3dde61e commit b955bce
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 91 deletions.
16 changes: 12 additions & 4 deletions ownCloudSDK/Bookmark/OCBookmark+DataItem.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#import "OCDataTypes.h"
#import "OCResource.h"
#import "OCMacros.h"
#import "OCCoreManager.h"

@implementation OCBookmark (DataItem)

Expand All @@ -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
172 changes: 89 additions & 83 deletions ownCloudSDK/Resource Management/OCBookmarkManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -78,130 +78,136 @@ - (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<OCBookmark *> *reconstructedBookmarks = nil;

@try
if ((bookmarkData = [[NSData alloc] initWithContentsOfURL:bookmarkStoreURL]) != nil)
{
NSArray<OCBookmark *> *existingBookmarks = nil;
NSMutableArray<OCBookmark *> *loadedBookmarks = nil;
NSMutableArray<OCBookmark *> *reconstructedBookmarks = nil;

loadedBookmarks = [NSKeyedUnarchiver unarchiveObjectWithData:bookmarkData];

@synchronized(self)
@try
{
existingBookmarks = [_bookmarks copy];
}
NSArray<OCBookmark *> *existingBookmarks = nil;
NSMutableArray<OCBookmark *> *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];
}
}
}
}

- (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];
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion ownCloudSDK/Resource Management/OCCoreManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion ownCloudSDK/Toolkit/OCIPNotificationCenter.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions ownCloudSDK/Vaults/Database/OCDatabase.m
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ - (void)addCacheItems:(NSArray <OCItem *> *)items syncAnchor:(OCSyncAnchor)syncA
completionHandler(self, error);
}
}]];
} segmentSize:((_memoryConfiguration == OCCoreMemoryConfigurationMinimum) ? 20 : 200)];
} segmentSize:((_memoryConfiguration == OCCoreMemoryConfigurationMinimum) ? 10 : 200)];
}

- (void)updateCacheItems:(NSArray <OCItem *> *)items syncAnchor:(OCSyncAnchor)syncAnchor completionHandler:(OCDatabaseCompletionHandler)completionHandler
Expand Down Expand Up @@ -452,7 +452,7 @@ - (void)updateCacheItems:(NSArray <OCItem *> *)items syncAnchor:(OCSyncAnchor)sy
completionHandler(self, error);
}
}]];
} segmentSize:((_memoryConfiguration == OCCoreMemoryConfigurationMinimum) ? 20 : 200)];
} segmentSize:((_memoryConfiguration == OCCoreMemoryConfigurationMinimum) ? 10 : 200)];
}

- (void)removeCacheItems:(NSArray <OCItem *> *)items syncAnchor:(OCSyncAnchor)syncAnchor completionHandler:(OCDatabaseCompletionHandler)completionHandler
Expand Down
1 change: 1 addition & 0 deletions ownCloudSDK/Vaults/Database/SQLite/OCSQLiteDB.m
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit b955bce

Please sign in to comment.