在循环函数中将解析的 JSON 数据上传到 Firebase 时,内存和 CPU 使用率极高

Posted

技术标签:

【中文标题】在循环函数中将解析的 JSON 数据上传到 Firebase 时,内存和 CPU 使用率极高【英文标题】:Extremely high Memory & CPU usage when uploading parsed JSON data to Firebase in loop function 【发布时间】:2018-03-06 12:43:04 【问题描述】:

这是我在这里的第一个问题,所以请放轻松!

我是一个新手编码器,我目前正在尝试遍历 JSON,解析数据并将信息备份到我的 Firebase 服务器 - 使用 Alamofire 请求 JSON 信息。

Swift 4、Alamofire 4.5.1、Firebase 4.2.0

该过程有效 - 但并非没有无限增加设备内存使用率和高达 200% 的 CPU 使用率。通过注释掉行,我将内存和 CPU 使用率单选到我的数据拉取函数中的 Firebase 上传 setValue 行——它迭代一个未知长度的 JSON 数据库(一次最多拉取 1000 行数据——因此增加偏移值)。我从中提取信息的数据库非常庞大,而且随着内存使用量的增加,该功能变得非常缓慢。

该函数检测是否找到空 JSON(结果结束),然后结束或解析 JSON,将信息上传到 Firebase,将偏移值增加 1000 行,然后使用新的偏移重复自身价值。

var offset: Int! = 0
var finished: Bool! = false

func pullCities() 
  print("step 1")
  let call = GET_CITIES + "&offset=\(self.offset!)&rows=1000"
  let cityURL = URL(string: call)!
  Alamofire.request(cityURL).authenticate(user: USERNAME, password: PASSWORD).responseJSON  response in
    let result = response.result
    print("step 2")
    if let dict = result.value as? [Dictionary<String, Any>] 
        print("step 3")
        if dict.count == 0 
            self.finished = true
            print("CITIES COMPLETE")
         else 
            print("step 4")
            for item in dict 
                if let id = item["city"] as? String 
                    let country = item["country"] as? String
                   let ref = DataService.ds.Database.child("countries").child(country!).child("cities").child(id)
                        ref.setValue(item)
                
            
            self.finished = false
            print("SUCCESS CITY \(self.offset!)")
            self.offset = self.offset! + 1000
        
    
        if self.finished == true 
            return
         else 
            self.pullCities()
        
     
  

在我看来,上传到 Firebase 的数据被保存在某个地方,并且上传完成后没有清空?虽然我在网上搜索时找不到关于这个问题的太多信息。

我尝试过的事情:

repeat,while 函数(不好,因为我只希望每个循环有 1 次主动重复 - 并且内存、CPU 使用率仍然很高)

性能监控(Xcode 调用树发现“CFString (immutable)”和“__NSArrayM”是内存使用量飙升的主要原因——两者都与上面的 setValue 行有关)

内存使用情况图表(非常清楚,此函数的内存在循环返回时不会被清空 - 内存完全没有减少)

autoreleasepool 块(根据建议,不成功)

已启用全模块优化(根据建议,未成功)

任何帮助将不胜感激!

更新

下图是单次循环运行后的分配图(1000 行数据)。它表明可能发生的情况是 Firebase 正在缓存结果字典中每个项目的数据,但似乎只在每次上传完成后才将内存作为一个整体取消分配? 理想情况下,它应该在每次成功上传后取消分配,而不是一次全部取消。如果有人能就此提出一些建议,我将不胜感激!

最终更新

如果有人遇到同样的问题,我没有找到解决方案。我的要求发生了变化,所以我将代码切换到完美运行的 nodejs。 HTTP 请求也很容易在 javascript 上编写代码!

【问题讨论】:

【参考方案1】:

我在处理外部网站上的数据时遇到了类似的问题,我可以解决的唯一方法是将循环包装在 autoreleasepool 块中,这会在每次迭代时强制清除内存。鉴于 ARC,您可能认为 Swift 不需要这样的结构,但请参阅这个 SO 讨论:

Is it necessary to use autoreleasepool in a Swift program?

希望对您有所帮助。

【讨论】:

感谢您的建议!我刚刚在发布函数内的许多不同位置应用了这些 autoreleasepool 块(包括在 dict 循环中的 for 项目内),但无济于事。仍然看到内存使用量增加和 CPU 使用率高 dict中的项目是唯一的吗?如果是这样,您是否尝试过将其设为变量并在找到每个条目时将其删除(这样字典会逐渐变小)? 刚试了一下,还是不行。我将它设置回一个 let 常量并检查了 Allocations - dict 已正确清空,但是,ref 和 setvalue 行仍然继续使用不可变的 CFStrings 和 NSArrayM 项在内存中搅动【参考方案2】:

有时编译器无法正确优化您的代码,除非您在项目构建设置中启用整个模块优化。这通常发生在使用泛型时。

尝试打开它,即使是调试环境和测试。

【讨论】:

谢谢,不过我已经为发布和调试启用了整个模块优化

以上是关于在循环函数中将解析的 JSON 数据上传到 Firebase 时,内存和 CPU 使用率极高的主要内容,如果未能解决你的问题,请参考以下文章

ajaxFileUpload上传文件成功后却无法解析服务器返回的json数据

jquery eval解析JSON中的注意点介绍

jquery eval解析JSON中的注意点介绍

在 Kotlin Android 中将值传递给函数时进行编译时间检查

一个解析json串并组装echarts的option的函数解析

jquery : eval() 解析json的注意