Okay, i fixed this without delving deep into code. I just split #markAndTraceAndMaybeFreeStackPages: on three separate methods:
#invalidateStackPagesTraceState #markAndTraceRestStackPages #freeUnusedStackPages
so a code now looks like following:
"Now deal with stack pages" fullGCFlag ifFalse: [ self invalidateStackPagesTraceState ] ifTrue: [ self markAndTraceRestStackPages ]. "Process ephemerons" [ self processEphemeronsQueue ] whileTrue: [ "some stack pages may be reachable only through ephemerons" fullGCFlag ifTrue: [ self markAndTraceRestStackPages ]. ]. self unlinkEphemeronsStillInQueue. "Free unused stack pages. Only safe to free stack pages after all have been traced. "
fullGCFlag ifTrue: [ self freeUnusedStackPages ]. "Only safe to free any machine code methods after all stack pages have been traced." self markAndTraceOrFreeMachineCode: fullGCFlag.
Now the last question: should i take care of #markAndTraceOrFreeMachineCode: as well? Can machine code point to an object(s) which are not reachable from roots & ephemerons, but only from machine code?
I guess not, because if machine code could point to a subgraph which holding arbitrary set of objects, then it will be also dangerous to free stack pages before tracing machine code.