Object 0x7f5ed8607740 deallocated with retain count 2, reference may have escaped from deinit.
shenfu1991 opened this issue · 5 comments
Describe the bug
After running for about a month, I suddenly found that the program crashed. Checking the log showed: object 0x7f5ed8607740 deallocated with retain count 2, reference may have escaped from deinit.
To Reproduce
I don't know how to reproduce it, on the same server I run the entire project source code exactly the same and they all run fine. I will attach the source code of this class that was destroyed.
Expected behavior
Runs stably without crashing
Environment
- Vapor Framework version: 4.76.2
- Vapor Toolbox version: main (339fb04)
- OS version: ubuntu server 20.04
Additional context
Add any other context about the problem here.
full project log: https://osx1.xuanyuanhuangdi.org/lo.zip
XBotViewController.swift
//
// XBotViewController.swift
//
//
// Created by xuanyuan on 2021/3/5.
//
import Vapor
import Foundation
struct JuguCoreCase {
var chanStatus = 0
var chanDesc = ""
var pkPlus = 0
var pkPlusDesc = ""
var pkPlusEMA = 0
var pkt = 0
var pktDesc = ""
var pktEMA = 0
var ema = 0
var emaExtra = 0
var emaDesc = ""
var coreNum = 0
var maLow: Double = 0
var pk = 0
// var rsi: Double = 0
// var so: Double = 0
// var cci: Double = 0
// var psy: Double = 0
// var CHOP: Double = 0
var color = ""
var coreEma: Double = 0
var vol: Double = 0
var volatility: Double = 0
var sharpe: (Double,Double,Double,Int) = (0,0,0,0)
var TT = 0
var SB20 = 0
var WT: (Int,Double,Double,Int,Int,Int,Int) = (0,0,0.0,0,0,0,0)
var isRun = false
var run: Double = 0
var bc = 0
var mn: (Int,String) = (0,"")
}
class XBotViewController {
let formatter: DateFormatter = {
let format = DateFormatter()
format.dateFormat = "dd"
format.timeZone = TimeZone(identifier: TimeZone.current.identifier)
return format
}()
let dformatter: DateFormatter = {
let format = DateFormatter()
format.dateFormat = "yyyy-MM-dd HH:mm"
format.timeZone = TimeZone(identifier: TimeZone.current.identifier)
return format
}()
var userName = "shenfu"
var API_Key = "***"
var API_SecretKey = "***"
var fHost = "https://fapi.binance.com"
var kSingleOpen = 0.0
var kStock = 0.0
var kSymbol = "BTCUSDT"
var kIsSimulation = true
var total = 0.0
var balance: Double = 2000
var availableBalance: Double = 0
var maxBalance: Double = 0
var crossWalletBalance: Double = 0
var sum = 0.0
var closeTime = 0
var stockAvg = 0.0
var current = 0.0
var canGo = false
var last = 0.0
var zero = 0.0
var isPendingClose = false
var isPendingOpen = false
var fetchError = false
var logTextArr: [String] = []
var closeLogArr: [String] = []
let manager = NetwokingManager()
let httpTool = CoreNetwokingManager.sharedInstance
var closeInfo: [String: Int] = [:]
var earnInfo: [String: Double] = [:]
var openTime = 0
var autoStoploss = true //是否自动止损
var isDealloc = false //销毁
var isLong = false
var isShort = false
var featureStatus: FeatureStatus = .none
var subStatus: FeatureStatus = .none
var featureEarn: Double = 0
var maxEarn: Double = 0
var shortTime = 0
var shortSum: Double = 0
var featureX: Double = 0.5
var shouldLong = false
var firstOpenTimeStamp: TimeInterval = 0
var openTimeStampArr: [String] = []
var bootTimeStamp: TimeInterval = 0
var maxSum: Double = 0
var minSum: Double = 0
var interval = "5m"
var flagDesc = ""
var earnTime = 0
let systemVersion = 20.38
let versionMsg = """
选择比努力重要!
概率决定了一切!
开车需要目视远方
数学期望,赌场盈利原理。
用魔法打败魔法!
AI ---> money
"""
var allDatas: [MACDModel] = []
var shoudStopEarn = false
var maxFloatEarn: Double = 0 //最大浮盈
var maxFloatEarnDic: [String: Any] = [:] //最大浮盈记录
var tradeInfoArr: [tradeInfo] = []
let server = getCurrentServer()
var flagTimeStamp: Double = 0
var flagInfo: [String: Any] = [:]
var keepOrigialBalance = true
var origialBalance: Double = 2000
var isNew = false
var preCloseIsLong = false
var canTrade = false
var openInCoreNum = 0
var maxFloatLoss: Double = 0
var shouldStop = false
var shouldWaitUpdate = false
var feeSum: Double = 0
var shouldAutoFitFeatureX = true
var dualSidePositionTimes = 0
var fetchAssetSuccess = false
var juguCoreValue = JuguCoreCase()
var adaptX = 0
var adaptXTimes = 0
var lastestLog = ""
var openTimes = 0
var functionName = ""
// var traderInfos: [String: String] = [:]
var isAutoFollow = false
var fetchTraderOk = false
var isWaitingResume = false
var realTname = ""
var RealNameInfo: [String: String] = [:]
var penddingBindConfirmTimestamp: TimeInterval = 0
var penddingRemoveConfirmTimestamp: TimeInterval = 0
var preBTC: Double = 0
var preBTCTime = 0
var watchingTimeStamp: TimeInterval = 0
var removeSymbol = ""
var currentEarnRate: Double = 0
var maxEarnRate: Double = 0
var sendRetryTimes = 0
var csvString = ""
var shouldRecordCSV = false
var hasClearData = false
var preArr: [Double] = []
var isHalf = false
var earnPercent: Double = 0
var penddingDealloc = false
func boot() {
bootTimeStamp = Date().timeIntervalSince1970
manager.API_Key = API_Key
httpTool.API_Key = API_Key
fetchFeatureInstock(loop: true)
loadFeatureBalance()
loadPrice()
if isNew {
DispatchQueue.global().asyncAfter(deadline: .now()+8) {
self.watching()
}
}else{
watching()
}
if isAutoFollow {
goAutoFollow()
wtRealT()
}else{
selectFunc()
}
addLog("正在启动 \(server.rawValue)... interval: \(interval) boot...")
positionSide()
JuGuCore()
gohandleDatas()
sendCSV()
addPresous()
}
func selectMd(ss: [String],v: Int) {
var fc = TCMD1
if v == 2 {
fc = TCMD2
}else if v == 3 {
fc = TCMD3
}else if v == 4 {
fc = TCMD4
}else if v == 5 {
fc = TCMD5
}else if v == 6 {
fc = TCMD6
}else if v == 7 {
fc = TCMD7
}else if v == 8 {
fc = TCMD8
}else if v == 9 {
fc = TCMD9
}else if v == 10 {
fc = TCMD10
}else if v == 11 {
fc = TCMD11
}else if v == 12 {
fc = TCMD12
}
let funcArr = [
fc
]
var fcIdx = 0
var itIdx = 0
let itArr = ["3m","5m","15m","30m","1h","4h"]
for (_,value) in ss.enumerated() {
let fc = funcArr[fcIdx]
if server.rawValue == value {
fc()
self.interval = itArr[itIdx]
}
fcIdx += 1
if fcIdx >= funcArr.count {
fcIdx = 0
itIdx += 1
if itIdx >= itArr.count {
itIdx = 0
}
}
// debugPrint("\(value)=\(itArr[itIdx]),\(fc.self)")
}
}
func common(ss: [String]) {
let funcArr = [
TC2P
]
var fcIdx = 0
var itIdx = 0
let itArr = ["3m","5m","15m","30m","1h","4h"]
for (_,value) in ss.enumerated() {
let fc = funcArr[fcIdx]
if server.rawValue == value {
fc()
self.interval = itArr[itIdx]
}
fcIdx += 1
if fcIdx >= funcArr.count {
fcIdx = 0
itIdx += 1
if itIdx >= itArr.count {
itIdx = 0
}
}
// debugPrint("\(value)=\(itArr[itIdx]),\(fc.self)")
}
}
func getSS(name: String) ->[String] {
let ss = RunServer.allCases.map { rs in
rs.rawValue
}.filter { str in
str.contains(name)
}
return ss
}
func selectFunc() {
shouldRecordCSV = server.rawValue.contains("USX")
let s1 = getSS(name: "OSX")
selectMd(ss: s1, v: 1)
let s2 = getSS(name: "OSY")
selectMd(ss: s2, v: 2)
let s3 = getSS(name: "OSZ")
selectMd(ss: s3, v: 3)
let s4 = getSS(name: "TXX")
selectMd(ss: s4, v: 4)
let s5 = getSS(name: "TXY")
selectMd(ss: s5, v: 5)
let s6 = getSS(name: "TXZ")
selectMd(ss: s6, v: 6)
let s7 = getSS(name: "SGX")
selectMd(ss: s7, v: 7)
let s8 = getSS(name: "SGY")
selectMd(ss: s8, v: 8)
let s9 = getSS(name: "XGX")
selectMd(ss: s9, v: 9)
let s10 = getSS(name: "XGY")
selectMd(ss: s10, v: 10)
let s11 = getSS(name: "JPY")
selectMd(ss: s11, v: 11)
let s12 = getSS(name: "FRZ")
selectMd(ss: s12, v: 12)
let s13 = getSS(name: "USX")
common(ss:s13)
}
func updateDomainSysTem() {
shell("./MENG")
}
@discardableResult
func shell(_ args: String...) -> Int32 {
let task = Process()
task.launchPath = "/usr/bin/env"
task.arguments = args
task.launch()
// task.waitUntilExit()
return 0
}
func loadPrice() {
if isDealloc {
return
}
let p = getCurrentPrice(kSymbol)
if p > 0 {
self.current = p
}
DispatchQueue.global().asyncAfter(deadline: .now()+0.5) {
self.loadPrice()
}
}
func isMatchMinX() -> Bool {
return featureEarn > self.balance/2000.0*self.featureX*20
}
func getX() -> Double {
return 100000
}
func getZ() -> Double {
return getX()
}
func watching() {
if isDealloc {
return
}
if penddingDealloc {
isLong = false
isShort = false
}
if juguCoreValue.volatility == 0 && kIsSimulation && !isAutoFollow {
DispatchQueue.global().asyncAfter(deadline: .now()+2) {
self.watching()
}
return
}
let now = Date().timeIntervalSince1970
if watchingTimeStamp > 0 {
if now - watchingTimeStamp < 1.5 {
self.addLog("看门狗异常")
return
}
}
if kIsSimulation {
if balance < 500 {
balance += 2000
self.addLog("赌本不足,系统给您补充...")
}
}
watchingTimeStamp = now
if balance <= 0 || (featureEarn < 0 && balance + featureEarn <= 0) || (getEarn() + balance <= 0) {
addLog("阵亡!")
debugPrint("阵亡!")
isDealloc = true
}
if kStock > 0 {
featureEarn = (current - stockAvg) * kStock
currentEarnRate = (current-stockAvg)/stockAvg
}else if kStock < 0 {
featureEarn = (stockAvg - current) * fabs(kStock)
currentEarnRate = (stockAvg-current)/current
}else{
featureEarn = 0
currentEarnRate = 0
}
if featureEarn > maxEarn {
maxEarn = featureEarn
}
if currentEarnRate > maxEarnRate {
maxEarnRate = currentEarnRate
}
autoFitX()
if featureEarn > maxFloatEarn {
maxFloatEarn = featureEarn
maxFloatEarnDic["maxFloatEarn"] = maxFloatEarn
maxFloatEarnDic["maxFloatEarnTimeStamp"] = Date().timeIntervalSince1970
}
if featureEarn < maxFloatLoss && featureEarn < 0 {
maxFloatLoss = featureEarn
maxFloatEarnDic["maxFloatLoss"] = maxFloatLoss
}
if kIsSimulation {
if maxEarnRate >= 0.0125*3 && currentEarnRate <= maxEarnRate*0.66 && currentEarnRate > 0 {
earnPercent = (currentEarnRate/maxEarnRate)
shoudStopEarn = true
}else if maxEarnRate >= 0.0125*2 && currentEarnRate <= maxEarnRate*0.7 && currentEarnRate > 0 {
earnPercent = (currentEarnRate/maxEarnRate)
shoudStopEarn = true
}
}
var side = ""
if kStock == 0 {
if isLong {
side = "做多"
}else{
side = "做空"
}
}else{
if kStock > 0 {
side = "做多"
}else{
side = "做空"
}
}
if shouldAutoClose() { // 止损
addLog("貌似方向错了,系统为您自动平仓...,当前差价\(stockAvg - current)")
canGo = false
last = current
isPendingClose = true
addLog("\n【\(kSymbol)\(side)】第一次命中平仓价格,当前价格:\(current),尝试刷新价格...\n")
refreshClose()
}else if isPendingOpen { //刷新购买价格
canGo = false
refreshOpen()
}else if isPendingClose { //刷新卖价
canGo = false
refreshClose()
}else if shouldOpen() { //开仓
last = current
isPendingOpen = true
shouldLong = side == "做多"
addLog("\n【\(kSymbol)\(side)】第一次命中开仓价格,当前价格:\(current),尝试刷新价格...\n")
canGo = false
refreshOpen()
}else if shouldClose() { //平仓
if !isAutoFollow {
self.flagDesc += "\n\n 【close】\n: " + lastestLog + "\n"
}
canGo = false
last = current
isPendingClose = true
addLog("\n【\(kSymbol)\(side)】第一次命中平仓价格,当前价格:\(current),尝试刷新价格...\n")
refreshClose()
}else {
if shouldWaitUpdate {
DispatchQueue.global().asyncAfter(deadline: .now()+30) {
self.watching()
}
}else{
DispatchQueue.global().asyncAfter(deadline: .now()+2) {
self.watching()
}
}
}
}
func autoFitX() {
if !kIsSimulation {
let avs = vcArr.filter({ vc in
vc.kIsSimulation && !vc.isAutoFollow
})
let bingoArr = avs.filter { vc in
vc.currentEarnRate >= 0.0125
}
let isBull = Double(bingoArr.count)/Double(avs.count) >= 0.25 //正在赚钱的b超一半
if isBull || currentEarnRate >= 0.05 {
featureX = 0.75
}else{
featureX = 0.5
}
}
}
func openNow(vol: String? = nil) {
if openTime == 0 {
if let times = self.flagInfo["\(self.flagTimeStamp)"] as? Int {
self.flagInfo["\(self.flagTimeStamp)"] = times + 1
}else{
self.flagInfo["\(self.flagTimeStamp)"] = 1
}
}
if firstOpenTimeStamp == 0 {
firstOpenTimeStamp = Date().timeIntervalSince1970
}
isWaitingResume = false
let fee1 = fabs(kStock)*stockAvg*0.0004
sum -= fee1
feeSum += fee1
balance -= fee1
if !self.kIsSimulation && isAutoFollow {
self.sendRealT()
}
let t = self.getST(stamp: Date().timeIntervalSince1970*1000)
let times = openTimeStampArr.count + 1
openTimeStampArr.append("\n\(times)\n\(t)\nprice=\(current)\nvol=\(kSingleOpen)\navg=\(stockAvg)\n")
openTime += 1
if kIsSimulation {
openTimes += 1
}
addLog("\n\(kSymbol)开仓成功,当前价格:\(current)\n")
isPendingOpen = false
if kIsSimulation {
DispatchQueue.global().asyncAfter(deadline: .now()+5) {
self.watching()
}
}else{
DispatchQueue.global().asyncAfter(deadline: .now()+13) {
self.watching()
}
}
}
func closeNow() {
maxFloatEarn = 0
maxFloatLoss = 0
openTimes = 0
currentEarnRate = 0
maxEarnRate = 0
shoudStopEarn = false
isWaitingResume = false
penddingRemoveConfirmTimestamp = 0
maxEarn = 0
featureStatus = .none
subStatus = .none
openTime = 0
let d = formatter.string(from: Date())
closeTime += 1
let times = closeInfo[d]
if let t = times {
closeInfo[d] = t + 1
}else{
closeInfo[d] = 1
}
var msg = ""
if featureEarn > 0 {
earnTime += 1
}
sum += featureEarn
let earns = earnInfo[d]
if let e = earns {
earnInfo[d] = e + featureEarn
}else{
earnInfo[d] = featureEarn
}
let fee = fabs(kStock)*stockAvg*0.001
sum -= fee
feeSum += fee
let sts = featureEarn > 0 ? "٩(^ᴗ^)۶赚钱了" : "٩(^ᴗ^)۶再接再厉"
let side = kStock > 0 ? "做多" : "做空"
msg = """
\(sts)
用户:\(userName)
余额:\(balance)
盈利:\(featureEarn)
【\(kSymbol)合约\(side)】平仓成功
x: \((fabs(kStock)*stockAvg/balance).strBTF())
函数名:\(functionName)_\(interval)
当前价格:\(current)
持仓均价:\(stockAvg)
数量:\(kStock)
\(String(format: "累计收益:%.4f", sum))
"持仓金额:\(fabs(kStock)*stockAvg/featureX)
手续费:\(fee.str2F())
涨跌幅:\((((current-stockAvg)/stockAvg)*100).str2F())
"""
if !kIsSimulation {
sendMsg(text: msg)
}
let c = Date().timeIntervalSince1970
let maxFloat = maxFloatEarnDic["maxFloatEarn"] as? Double ?? 0
let maxFloatLoss1 = maxFloatEarnDic["maxFloatLoss"] as? Double ?? 0
let maxFloatEarnTimeStamp = maxFloatEarnDic["maxFloatEarnTimeStamp"] as? Double ?? 0
let tmp = tradeInfo(firstOpenTimeStamp: firstOpenTimeStamp, closeTime: c, featureEarn: featureEarn, maxFloatEarn: maxFloat, maxFloatEarnTimeStamp: maxFloatEarnTimeStamp, maxFloatLoss: maxFloatLoss1,isLong: kStock > 0,openInCoreNum: openInCoreNum)
tradeInfoArr.append(tmp)
maxFloatEarnDic = [:]
openTimeStampArr = []
firstOpenTimeStamp = 0
if kStock < 0 {
shortTime += 1
shortSum += featureEarn
}
if kIsSimulation {
balance += featureEarn
kStock = 0
stockAvg = 0
balance -= fee
}else{
kStock = 0
stockAvg = 0
}
if sum > maxSum {
maxSum = sum
}
if sum < minSum {
minSum = sum
}
let closeMsg = flagDesc + "\n~~~~~~~~~~~~~\n" + msg + "\n\n\n\n\n"
addCloseLog(closeMsg)
addLog(msg)
isPendingClose = false
featureEarn = 0
flagDesc = ""
if kIsSimulation {
DispatchQueue.global().asyncAfter(deadline: .now()+5) {
self.watching()
}
}else{
DispatchQueue.global().asyncAfter(deadline: .now()+23) {
self.watching()
}
if !kIsSimulation && isAutoFollow {
removeRealT()
}
}
}
deinit {
debugPrint("\(self) 实盘销毁:\(userName) -deinit")
}
}
None of the code in that file seems to be even remotely related to Vapor. That includes the crash: one of your classes, not necessarily this one, has a deinit which illegally captured self – for example in a DispatchQueue.async closure that the code is seemingly infested with.
This is the result of a misuse of Dispatch causing an object to be resurrected during its deinit; it has nothing to do with Vapor.
@shenfu1991 It's very strange that your code crashes only once a month. Due to your illiterate use of the GCD, it should crash every few minutes.
@mkll This is indeed very strange. It doesn't crash every month, and I'm not sure when it crashes. I have encountered this several times. I will fix the error in GCD and observe again. Thank you to the individuals mentioned above for pointing it out.
Actually, this class should never be destroyed because I haven't discarded it. It is an infinite loop task.