mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-18 21:40:32 +00:00
fixes the recently discovered GC memory leaks
This revision is intended as comparison point between the old and the new GC The used GC can be switched in mmdisp and various statistics will be gathered during execution (these will be removed/disabled in later revisions)
This commit is contained in:
@@ -285,6 +285,12 @@ proc getParamTypeDesc(m: BModule, t: PType, check: var TIntSet): PRope =
|
||||
return getTypeDescAux(m, b, check)
|
||||
result = getTypeDescAux(m, t, check)
|
||||
|
||||
proc paramStorageLoc(param: PSym): TStorageLoc =
|
||||
if param.typ.skipTypes({tyVar}).kind notin {tyArray, tyOpenArray}:
|
||||
result = OnStack
|
||||
else:
|
||||
result = OnUnknown
|
||||
|
||||
proc genProcParams(m: BModule, t: PType, rettype, params: var PRope,
|
||||
check: var TIntSet, declareEnvironment=true) =
|
||||
params = nil
|
||||
@@ -297,7 +303,8 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var PRope,
|
||||
var param = t.n.sons[i].sym
|
||||
if isCompileTimeOnly(param.typ): continue
|
||||
if params != nil: app(params, ~", ")
|
||||
fillLoc(param.loc, locParam, param.typ, mangleName(param), OnStack)
|
||||
fillLoc(param.loc, locParam, param.typ, mangleName(param),
|
||||
param.paramStorageLoc)
|
||||
app(params, getParamTypeDesc(m, param.typ, check))
|
||||
if ccgIntroducedPtr(param):
|
||||
app(params, ~"*")
|
||||
|
||||
@@ -25,7 +25,7 @@ type
|
||||
next*: PIdent # for hash-table chaining
|
||||
h*: THash # hash value of s
|
||||
|
||||
var buckets: array[0..4096 * 2 - 1, PIdent]
|
||||
var buckets*: array[0..4096 * 2 - 1, PIdent]
|
||||
|
||||
proc cmpIgnoreStyle(a, b: cstring, blen: int): int =
|
||||
var i = 0
|
||||
@@ -102,5 +102,5 @@ proc getIdent*(identifier: string, h: THash): PIdent =
|
||||
proc IdentEq*(id: PIdent, name: string): bool =
|
||||
result = id.id == getIdent(name).id
|
||||
|
||||
let idAnon* = getIdent":anonymous"
|
||||
var idAnon* = getIdent":anonymous"
|
||||
|
||||
|
||||
@@ -394,7 +394,57 @@ proc dbgseqimp(x: PGenericSeq) {.cdecl.} =
|
||||
|
||||
seqdbg = dbgseqimp
|
||||
|
||||
proc resetMemory =
|
||||
resetCompilationLists()
|
||||
ccgutils.resetCaches()
|
||||
ResetAllModules()
|
||||
resetRopeCache()
|
||||
resetSysTypes()
|
||||
gGenericsCache = nil
|
||||
gOwners = @[]
|
||||
rangeDestructorProc = nil
|
||||
for i in low(buckets)..high(buckets):
|
||||
buckets[i] = nil
|
||||
idAnon = nil
|
||||
|
||||
# XXX: clean these global vars
|
||||
# ccgstmts.gBreakpoints
|
||||
# ccgthreadvars.nimtv
|
||||
# ccgthreadvars.nimtVDeps
|
||||
# ccgthreadvars.nimtvDeclared
|
||||
# cgendata
|
||||
# cgmeth?
|
||||
# condsyms?
|
||||
# depends?
|
||||
# lexer.gLinesCompiled
|
||||
# msgs - error counts
|
||||
# magicsys, when system.nim changes
|
||||
# rodread.rodcompilerProcs
|
||||
# rodread.gTypeTable
|
||||
# rodread.gMods
|
||||
|
||||
# !! ropes.cache
|
||||
# !! semdata.gGenericsCache
|
||||
# semthreads.computed?
|
||||
#
|
||||
# suggest.usageSym
|
||||
#
|
||||
# XXX: can we run out of IDs?
|
||||
# XXX: detect config reloading (implement as error/require restart)
|
||||
# XXX: options are appended (they will accumulate over time)
|
||||
# vis = visimpl
|
||||
gcDebugging = true
|
||||
echo "COLLECT 1"
|
||||
GC_fullCollect()
|
||||
echo "COLLECT 2"
|
||||
GC_fullCollect()
|
||||
echo "COLLECT 3"
|
||||
GC_fullCollect()
|
||||
echo GC_getStatistics()
|
||||
|
||||
proc MainCommand =
|
||||
gGlobalOptions.incl(optCaasEnabled)
|
||||
|
||||
# In "nimrod serve" scenario, each command must reset the registered passes
|
||||
clearPasses()
|
||||
gLastCmdTime = epochTime()
|
||||
@@ -499,47 +549,7 @@ proc MainCommand =
|
||||
# XXX: temporary command for easier testing
|
||||
commandEval(mainCommandArg())
|
||||
of "reset":
|
||||
ResetModule(gProjectMainIdx)
|
||||
gcDebugging = true
|
||||
GC_fullCollect()
|
||||
|
||||
resetCompilationLists()
|
||||
ccgutils.resetCaches()
|
||||
ResetAllModules()
|
||||
resetRopeCache()
|
||||
resetSysTypes()
|
||||
gGenericsCache = nil
|
||||
gOwners = @[]
|
||||
rangeDestructorProc = nil
|
||||
|
||||
# XXX: clean these global vars
|
||||
# ccgstmts.gBreakpoints
|
||||
# ccgthreadvars.nimtv
|
||||
# ccgthreadvars.nimtVDeps
|
||||
# ccgthreadvars.nimtvDeclared
|
||||
# cgendata
|
||||
# cgmeth?
|
||||
# condsyms?
|
||||
# depends?
|
||||
# lexer.gLinesCompiled
|
||||
# msgs - error counts
|
||||
# magicsys, when system.nim changes
|
||||
# rodread.rodcompilerProcs
|
||||
# rodread.gTypeTable
|
||||
# rodread.gMods
|
||||
|
||||
# !! ropes.cache
|
||||
# !! semdata.gGenericsCache
|
||||
# semthreads.computed?
|
||||
#
|
||||
# suggest.usageSym
|
||||
#
|
||||
# XXX: can we run out of IDs?
|
||||
# XXX: detect config reloading (implement as error/require restart)
|
||||
# XXX: options are appended (they will accumulate over time)
|
||||
# vis = visimpl
|
||||
GC_fullCollect()
|
||||
echo GC_getStatistics()
|
||||
resetMemory()
|
||||
of "idetools":
|
||||
gCmd = cmdIdeTools
|
||||
if gEvalExpr != "":
|
||||
@@ -565,3 +575,5 @@ proc MainCommand =
|
||||
echo " int tries: ", gCacheIntTries
|
||||
echo " efficiency: ", formatFloat(1-(gCacheMisses.float/gCacheTries.float), ffDecimal, 3)
|
||||
|
||||
# resetMemory()
|
||||
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
# Efficient set of pointers for the GC (and repr)
|
||||
|
||||
type
|
||||
TRefCount = int
|
||||
|
||||
TCell {.pure.} = object
|
||||
refcount: int # the refcount and some flags
|
||||
refcount: TRefCount # the refcount and some flags
|
||||
typ: PNimType
|
||||
when trackAllocationSource:
|
||||
filename: cstring
|
||||
|
||||
1039
lib/system/gc.nim
1039
lib/system/gc.nim
File diff suppressed because it is too large
Load Diff
@@ -13,11 +13,19 @@ when defined(NimString):
|
||||
else:
|
||||
{.pragma: codegenType.}
|
||||
|
||||
type # This should be he same as ast.TTypeKind
|
||||
# many enum fields are not used at runtime
|
||||
type
|
||||
# This should be he same as ast.TTypeKind
|
||||
# many enum fields are not used at runtime
|
||||
TNimKind = enum
|
||||
tyNone, tyBool, tyChar,
|
||||
tyEmpty, tyArrayConstr, tyNil, tyExpr, tyStmt, tyTypeDesc,
|
||||
tyNone,
|
||||
tyBool,
|
||||
tyChar,
|
||||
tyEmpty,
|
||||
tyArrayConstr,
|
||||
tyNil,
|
||||
tyExpr,
|
||||
tyStmt,
|
||||
tyTypeDesc,
|
||||
tyGenericInvokation, # ``T[a, b]`` for types to invoke
|
||||
tyGenericBody, # ``T[a, b, body]`` last parameter is the body
|
||||
tyGenericInst, # ``T[a, b, realInstance]`` instantiated generic type
|
||||
@@ -30,15 +38,30 @@ type # This should be he same as ast.TTypeKind
|
||||
tyTuple, # WARNING: The compiler uses tyTuple for pure objects!
|
||||
tySet,
|
||||
tyRange,
|
||||
tyPtr, tyRef,
|
||||
tyPtr,
|
||||
tyRef,
|
||||
tyVar,
|
||||
tySequence,
|
||||
tyProc,
|
||||
tyPointer, tyOpenArray,
|
||||
tyString, tyCString, tyForward,
|
||||
tyInt, tyInt8, tyInt16, tyInt32, tyInt64,
|
||||
tyFloat, tyFloat32, tyFloat64, tyFloat128,
|
||||
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64,
|
||||
tyPointer,
|
||||
tyOpenArray,
|
||||
tyString,
|
||||
tyCString,
|
||||
tyForward,
|
||||
tyInt,
|
||||
tyInt8,
|
||||
tyInt16,
|
||||
tyInt32,
|
||||
tyInt64,
|
||||
tyFloat,
|
||||
tyFloat32,
|
||||
tyFloat64,
|
||||
tyFloat128,
|
||||
tyUInt,
|
||||
tyUInt8,
|
||||
tyUInt16,
|
||||
tyUInt32,
|
||||
tyUInt64,
|
||||
tyBigNum,
|
||||
|
||||
TNimNodeKind = enum nkNone, nkSlot, nkList, nkCase
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
{.push checks:off.}
|
||||
|
||||
const
|
||||
debugGC = false # we wish to debug the GC...
|
||||
debugGC = true # we wish to debug the GC...
|
||||
logGC = false
|
||||
traceGC = false # extensive debugging
|
||||
traceGC = true # extensive debugging
|
||||
alwaysCycleGC = false
|
||||
alwaysGC = false # collect after every memory allocation (for debugging)
|
||||
leakDetector = false
|
||||
@@ -307,7 +307,10 @@ else:
|
||||
include "system/cellsets"
|
||||
when not leakDetector:
|
||||
sysAssert(sizeof(TCell) == sizeof(TFreeCell), "sizeof TFreeCell")
|
||||
include "system/gc"
|
||||
when true:
|
||||
include "system/gc"
|
||||
else:
|
||||
include "system/oldgc"
|
||||
|
||||
{.pop.}
|
||||
|
||||
|
||||
1044
lib/system/oldgc.nim
Normal file
1044
lib/system/oldgc.nim
Normal file
File diff suppressed because it is too large
Load Diff
@@ -204,9 +204,16 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
|
||||
# we need to decref here, otherwise the GC leaks!
|
||||
when not defined(boehmGC) and not defined(nogc):
|
||||
for i in newLen..result.len-1:
|
||||
let len0 = gch.tempStack.len
|
||||
forAllChildrenAux(cast[pointer](cast[TAddress](result) +%
|
||||
GenericSeqSize +% (i*%elemSize)),
|
||||
extGetCellType(result).base, waZctDecRef)
|
||||
extGetCellType(result).base, waPush)
|
||||
let len1 = gch.tempStack.len
|
||||
for i in len0 .. <len1:
|
||||
doDecRef(gch.tempStack.d[i], LocalHeap, MaybeCyclic)
|
||||
gch.tempStack.len = len0
|
||||
|
||||
# XXX add a proper addCycleRoot barrier here!
|
||||
# and set the memory to nil:
|
||||
zeroMem(cast[pointer](cast[TAddress](result) +% GenericSeqSize +%
|
||||
(newLen*%elemSize)), (result.len-%newLen) *% elemSize)
|
||||
|
||||
@@ -44,10 +44,11 @@ elif defined(macosx):
|
||||
|
||||
proc getTicks(): TTicks {.inline.} =
|
||||
result = TTicks(mach_absolute_time())
|
||||
|
||||
|
||||
var timeBaseInfo: TMachTimebaseInfoData
|
||||
mach_timebase_info(timeBaseInfo)
|
||||
|
||||
proc `-`(a, b: TTicks): TNanos =
|
||||
var timeBaseInfo: TMachTimebaseInfoData
|
||||
mach_timebase_info(timeBaseInfo)
|
||||
result = (a.int64 - b.int64) * timeBaseInfo.numer div timeBaseInfo.denom
|
||||
|
||||
elif defined(posixRealtime):
|
||||
|
||||
39
tests/gc/refarrayleak.nim
Normal file
39
tests/gc/refarrayleak.nim
Normal file
@@ -0,0 +1,39 @@
|
||||
discard """
|
||||
outputsub: "no leak: "
|
||||
"""
|
||||
|
||||
type
|
||||
TNode = object
|
||||
data: array[0..300, char]
|
||||
|
||||
PNode = ref TNode
|
||||
|
||||
TNodeArray = array[0..10, PNode]
|
||||
|
||||
TArrayHolder = object
|
||||
sons: TNodeArray
|
||||
|
||||
proc nullify(a: var TNodeArray) =
|
||||
for i in 0..high(a):
|
||||
a[i] = nil
|
||||
|
||||
proc newArrayHolder: ref TArrayHolder =
|
||||
new result
|
||||
|
||||
for i in 0..high(result.sons):
|
||||
new result.sons[i]
|
||||
|
||||
nullify result.sons
|
||||
|
||||
proc loop =
|
||||
for i in 0..10000:
|
||||
discard newArrayHolder()
|
||||
|
||||
if getOccupiedMem() > 300_000:
|
||||
echo "still a leak! ", getOccupiedMem()
|
||||
quit 1
|
||||
else:
|
||||
echo "no leak: ", getOccupiedMem()
|
||||
|
||||
loop()
|
||||
|
||||
@@ -29,5 +29,3 @@ proc loop =
|
||||
|
||||
loop()
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user