Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qv4mm_p.h
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QV4GC_H
5#define QV4GC_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <private/qv4global_p.h>
19#include <private/qv4value_p.h>
20#include <private/qv4scopedvalue_p.h>
21#include <private/qv4object_p.h>
22#include <private/qv4mmdefs_p.h>
23#include <QVector>
24
25#define MM_DEBUG 0
26
28
29namespace QV4 {
30
31struct GCData { virtual ~GCData(){};};
32
36
38 Q_GADGET_EXPORT(Q_QML_EXPORT)
39
40public:
41 enum GCState {
52 // The following needs to be after InitCallDestroyObjects,
53 // even if it normally would run before it, to ensure that in
54 // a normal incremental run the stack is redrained before this
55 // is run as we make use of that knowledge in a test.
64 };
65 Q_ENUM(GCState)
66
71
72 struct GCStateInfo {
74 GCState (*execute)(GCStateMachine *, ExtraData &) = nullptr; // Function to execute for this state, returns true if ready to transition
75 bool breakAfter{false};
76 };
77
84 MemoryManager *mm = nullptr;
85 ExtraData stateData; // extra date for specific states
86 bool collectTimings = false;
87 #ifdef QT_BUILD_INTERNAL
88 // This is used only to simplify testing.
91 #endif
92
94
95 inline void step() {
96 if (!inProgress()) {
97 reset();
98 }
99 transition();
100 }
101
102 inline bool inProgress() {
103 return state != GCState::Invalid;
104 }
105
106 inline void reset() {
107 state = GCState::MarkStart;
108 }
109
110 Q_QML_EXPORT void transition();
111
112 inline void handleTimeout(GCState state) {
113 Q_UNUSED(state);
114 }
115};
116
119
120struct ChunkAllocator;
121struct MemorySegment;
122
124 BlockAllocator(ChunkAllocator *chunkAllocator, ExecutionEngine *engine)
125 : chunkAllocator(chunkAllocator), engine(engine)
126 {
127 memset(freeBins, 0, sizeof(freeBins));
128 }
129
130 enum { NumBins = 8 };
131
132 static inline size_t binForSlots(size_t nSlots) {
133 return nSlots >= NumBins ? NumBins - 1 : nSlots;
134 }
135
136 HeapItem *allocate(size_t size, bool forceAllocation = false);
137
139 return Chunk::AvailableSlots*chunks.size();
140 }
141
143 return chunks.size()*Chunk::DataSize;
144 }
145 size_t usedMem() const {
146 uint used = 0;
147 for (auto c : chunks)
148 used += c->nUsedSlots()*Chunk::SlotSize;
149 return used;
150 }
151
152 void sweep();
153 void freeAll();
154 void resetBlackBits();
155
156 // bump allocations
157 HeapItem *nextFree = nullptr;
162 ExecutionEngine *engine;
164 uint *allocationStats = nullptr;
165};
166
168 HugeItemAllocator(ChunkAllocator *chunkAllocator, ExecutionEngine *engine)
169 : chunkAllocator(chunkAllocator), engine(engine)
170 {}
171
172 HeapItem *allocate(size_t size);
173 void sweep(ClassDestroyStatsCallback classCountPtr);
174 void freeAll();
175 void resetBlackBits();
176
177 size_t usedMem() const {
178 size_t used = 0;
179 for (const auto &c : chunks)
180 used += c.size;
181 return used;
182 }
183
185 ExecutionEngine *engine;
191
193};
194
195
196class Q_QML_EXPORT MemoryManager
197{
199
200public:
202 ~MemoryManager();
203
204 template <typename ToBeMarked>
205 friend struct GCCriticalSection;
206
207 // TODO: this is only for 64bit (and x86 with SSE/AVX), so exend it for other architectures to be slightly more efficient (meaning, align on 8-byte boundaries).
208 // Note: all occurrences of "16" in alloc/dealloc are also due to the alignment.
209 constexpr static inline std::size_t align(std::size_t size)
210 { return (size + Chunk::SlotSize - 1) & ~(Chunk::SlotSize - 1); }
211
212 /* NOTE: allocManaged comes in various overloads. If size is not passed explicitly
213 sizeof(ManagedType::Data) is used for size. However, there are quite a few cases
214 where we allocate more than sizeof(ManagedType::Data); that's generally the case
215 when the Object has a ValueArray member.
216 If no internal class pointer is provided, ManagedType::defaultInternalClass(engine)
217 will be used as the internal class.
218 */
219
220 template<typename ManagedType>
221 inline typename ManagedType::Data *allocManaged(std::size_t size, Heap::InternalClass *ic)
222 {
223 Q_STATIC_ASSERT(std::is_trivial_v<typename ManagedType::Data>);
224 size = align(size);
225 typename ManagedType::Data *d = static_cast<typename ManagedType::Data *>(allocData(size));
226 d->internalClass.set(engine, ic);
227 Q_ASSERT(d->internalClass && d->internalClass->vtable);
228 Q_ASSERT(ic->vtable == ManagedType::staticVTable());
229 return d;
230 }
231
232 template<typename ManagedType>
233 inline typename ManagedType::Data *allocManaged(Heap::InternalClass *ic)
234 {
235 return allocManaged<ManagedType>(sizeof(typename ManagedType::Data), ic);
236 }
237
238 template<typename ManagedType>
239 inline typename ManagedType::Data *allocManaged(std::size_t size, InternalClass *ic)
240 {
241 return allocManaged<ManagedType>(size, ic->d());
242 }
243
244 template<typename ManagedType>
245 inline typename ManagedType::Data *allocManaged(InternalClass *ic)
246 {
247 return allocManaged<ManagedType>(sizeof(typename ManagedType::Data), ic);
248 }
249
250 template<typename ManagedType>
251 inline typename ManagedType::Data *allocManaged(std::size_t size)
252 {
253 Scope scope(engine);
254 Scoped<InternalClass> ic(scope, ManagedType::defaultInternalClass(engine));
255 return allocManaged<ManagedType>(size, ic);
256 }
257
258 template<typename ManagedType>
259 inline typename ManagedType::Data *allocManaged()
260 {
261 auto constexpr size = sizeof(typename ManagedType::Data);
262 Scope scope(engine);
263 Scoped<InternalClass> ic(scope, ManagedType::defaultInternalClass(engine));
264 return allocManaged<ManagedType>(size, ic);
265 }
266
267 template <typename ObjectType>
268 typename ObjectType::Data *allocateObject(Heap::InternalClass *ic)
269 {
270 Heap::Object *o = allocObjectWithMemberData(ObjectType::staticVTable(), ic->size);
271 o->internalClass.set(engine, ic);
272 Q_ASSERT(o->internalClass.get() && o->vtable());
273 Q_ASSERT(o->vtable() == ObjectType::staticVTable());
274 return static_cast<typename ObjectType::Data *>(o);
275 }
276
277 template <typename ObjectType>
278 typename ObjectType::Data *allocateObject(InternalClass *ic)
279 {
280 return allocateObject<ObjectType>(ic->d());
281 }
282
283 template <typename ObjectType>
284 typename ObjectType::Data *allocateObject()
285 {
286 Scope scope(engine);
287 Scoped<InternalClass> ic(scope, ObjectType::defaultInternalClass(engine));
288 ic = ic->changeVTable(ObjectType::staticVTable());
289 ic = ic->changePrototype(ObjectType::defaultPrototype(engine)->d());
290 return allocateObject<ObjectType>(ic);
291 }
292
293 template <typename ManagedType, typename Arg1>
294 typename ManagedType::Data *allocWithStringData(std::size_t unmanagedSize, Arg1 &&arg1)
295 {
296 typename ManagedType::Data *o = reinterpret_cast<typename ManagedType::Data *>(allocString(unmanagedSize));
297 o->internalClass.set(engine, ManagedType::defaultInternalClass(engine));
298 Q_ASSERT(o->internalClass && o->internalClass->vtable);
299 o->init(std::forward<Arg1>(arg1));
300 return o;
301 }
302
303 template <typename ObjectType, typename... Args>
304 typename ObjectType::Data *allocObject(Heap::InternalClass *ic, Args&&... args)
305 {
306 typename ObjectType::Data *d = allocateObject<ObjectType>(ic);
307 d->init(std::forward<Args>(args)...);
308 return d;
309 }
310
311 template <typename ObjectType, typename... Args>
312 typename ObjectType::Data *allocObject(InternalClass *ic, Args&&... args)
313 {
314 typename ObjectType::Data *d = allocateObject<ObjectType>(ic);
315 d->init(std::forward<Args>(args)...);
316 return d;
317 }
318
319 template <typename ObjectType, typename... Args>
320 typename ObjectType::Data *allocate(Args&&... args)
321 {
322 Scope scope(engine);
323 Scoped<ObjectType> t(scope, allocateObject<ObjectType>());
324 t->d_unchecked()->init(std::forward<Args>(args)...);
325 return t->d();
326 }
327
328 template <typename ManagedType, typename... Args>
329 typename ManagedType::Data *alloc(Args&&... args)
330 {
331 Scope scope(engine);
332 Scoped<ManagedType> t(scope, allocManaged<ManagedType>());
333 t->d_unchecked()->init(std::forward<Args>(args)...);
334 return t->d();
335 }
336
337 void runGC();
339 void runFullGC();
340
341 void dumpStats() const;
342
343 size_t getUsedMem() const;
344 size_t getAllocatedMem() const;
345 size_t getLargeItemsMem() const;
346
347 // called when a JS object grows itself. Specifically: Heap::String::append
348 // and InternalClassDataPrivate<PropertyAttributes>.
349 void changeUnmanagedHeapSizeUsage(qptrdiff delta) { unmanagedHeapSize += delta; }
350
351 // called at the end of a gc cycle
353
354 template<typename ManagedType>
355 typename ManagedType::Data *allocIC()
356 {
357 Heap::Base *b = *allocate(&icAllocator, align(sizeof(typename ManagedType::Data)));
358 return static_cast<typename ManagedType::Data *>(b);
359 }
360
361 void registerWeakMap(Heap::MapObject *map);
362 void registerWeakSet(Heap::SetObject *set);
363
364 void onEventLoop();
365
366 //GC related methods
367 void setGCTimeLimit(int timeMs);
368 MarkStack* markStack() { return m_markStack.get(); }
369
370protected:
371 /// expects size to be aligned
373 Heap::Base *allocData(std::size_t size);
374 Heap::Object *allocObjectWithMemberData(const QV4::VTable *vtable, uint nMembers);
375
376private:
377 enum {
378 MinUnmanagedHeapSizeGCLimit = 128 * 1024
379 };
380
381public:
383 void sweep(bool lastSweep = false, ClassDestroyStatsCallback classCountPtr = nullptr);
386 {
387 const bool incrementalGCIsAlreadyRunning = m_markStack != nullptr;
388 const bool aboveUnmanagedHeapLimit = incrementalGCIsAlreadyRunning
389 ? unmanagedHeapSize > 3 * unmanagedHeapSizeGCLimit / 2
390 : unmanagedHeapSize > unmanagedHeapSizeGCLimit;
391 return aboveUnmanagedHeapLimit;
392 }
393private:
394 bool shouldRunGC() const;
395
396 HeapItem *allocate(BlockAllocator *allocator, std::size_t size)
397 {
398 const bool incrementalGCIsAlreadyRunning = m_markStack != nullptr;
399
400 bool didGCRun = false;
401 if (aggressiveGC) {
403 didGCRun = true;
404 }
405
407 if (!didGCRun)
408 incrementalGCIsAlreadyRunning ? (void) tryForceGCCompletion() : runGC();
409 didGCRun = true;
410 }
411
412 if (size > Chunk::DataSize)
413 return hugeItemAllocator.allocate(size);
414
415 if (HeapItem *m = allocator->allocate(size))
416 return m;
417
418 if (!didGCRun && shouldRunGC())
419 runGC();
420
421 return allocator->allocate(size, true);
422 }
423
424public:
430 PersistentValueStorage *m_persistentValues;
431 PersistentValueStorage *m_weakValues;
435
438
439 std::size_t unmanagedHeapSize = 0; // the amount of bytes of heap that is not managed by the memory manager, but which is held onto by managed items.
442
445 bool aggressiveGC = false;
447 bool gcStats = false;
448 bool gcCollectorStats = false;
449
452
453 struct {
458 } statistics;
459};
460
461/*!
462 \internal
463 GCCriticalSection prevets the gc from running, until it is destructed.
464 In its dtor, it runs a check whether we've reached the unmanaegd heap limit,
465 and triggers a gc run if necessary.
466 Lastly, it can optionally mark an object passed to it before runnig the gc.
467 */
468template <typename ToBeMarked = void>
471
481 {
484 if constexpr (!std::is_same_v<ToBeMarked, void>)
485 if (m_toBeMarked)
487 /* because we blocked the gc, we might be using too much memoryon the unmanaged heap
488 and did not run the normal fixup logic. So recheck again, and trigger a gc run
489 if necessary*/
491 return;
492 if (!m_engine->isGCOngoing) {
494 } else {
497 }
498 }
499
500private:
502 MemoryManager::Blockness m_oldState;
503 ToBeMarked *m_toBeMarked;
504};
505
506}
507
508QT_END_NAMESPACE
509
510#endif // QV4GC_H
ManagedType::Data * allocManaged(InternalClass *ic)
Definition qv4mm_p.h:245
bool isAboveUnmanagedHeapLimit()
Definition qv4mm_p.h:385
friend struct GCCriticalSection
Definition qv4mm_p.h:205
size_t maxReservedMem
Definition qv4mm_p.h:454
QVector< Value * > m_pendingFreedObjectWrapperValue
Definition qv4mm_p.h:432
Heap::SetObject * weakSets
Definition qv4mm_p.h:434
bool crossValidateIncrementalGC
Definition qv4mm_p.h:446
ObjectType::Data * allocateObject(Heap::InternalClass *ic)
Definition qv4mm_p.h:268
Heap::Object * allocObjectWithMemberData(const QV4::VTable *vtable, uint nMembers)
Definition qv4mm.cpp:1081
size_t getLargeItemsMem() const
Definition qv4mm.cpp:1442
ManagedType::Data * allocManaged()
Definition qv4mm_p.h:259
bool tryForceGCCompletion()
Definition qv4mm.cpp:1310
ManagedType::Data * allocManaged(std::size_t size, Heap::InternalClass *ic)
Definition qv4mm_p.h:221
ManagedType::Data * allocManaged(std::size_t size, InternalClass *ic)
Definition qv4mm_p.h:239
size_t lastAllocRequestedSlots
Definition qv4mm_p.h:451
void changeUnmanagedHeapSizeUsage(qptrdiff delta)
Definition qv4mm_p.h:349
std::unique_ptr< GCStateMachine > gcStateMachine
Definition qv4mm_p.h:436
ObjectType::Data * allocate(Args &&... args)
Definition qv4mm_p.h:320
Blockness gcBlocked
Definition qv4mm_p.h:444
ChunkAllocator * chunkAllocator
Definition qv4mm_p.h:426
PersistentValueStorage * m_persistentValues
Definition qv4mm_p.h:430
ManagedType::Data * allocWithStringData(std::size_t unmanagedSize, Arg1 &&arg1)
Definition qv4mm_p.h:294
void sweep(bool lastSweep=false, ClassDestroyStatsCallback classCountPtr=nullptr)
Definition qv4mm.cpp:1187
void cleanupDeletedQObjectWrappersInSweep()
Definition qv4mm.cpp:1228
PersistentValueStorage * m_weakValues
Definition qv4mm_p.h:431
std::size_t unmanagedHeapSize
Definition qv4mm_p.h:439
ManagedType::Data * allocManaged(std::size_t size)
Definition qv4mm_p.h:251
void registerWeakMap(Heap::MapObject *map)
Definition qv4mm.cpp:1470
ManagedType::Data * allocManaged(Heap::InternalClass *ic)
Definition qv4mm_p.h:233
void dumpStats() const
Definition qv4mm.cpp:1513
std::size_t usedSlotsAfterLastFullSweep
Definition qv4mm_p.h:441
BlockAllocator blockAllocator
Definition qv4mm_p.h:427
uint allocations[BlockAllocator::NumBins]
Definition qv4mm_p.h:457
HugeItemAllocator hugeItemAllocator
Definition qv4mm_p.h:429
size_t getUsedMem() const
Definition qv4mm.cpp:1432
std::unique_ptr< MarkStack > m_markStack
Definition qv4mm_p.h:437
Heap::Base * allocData(std::size_t size)
Definition qv4mm.cpp:1066
size_t getAllocatedMem() const
Definition qv4mm.cpp:1437
ManagedType::Data * allocIC()
Definition qv4mm_p.h:355
void setGCTimeLimit(int timeMs)
Definition qv4mm.cpp:1182
ObjectType::Data * allocateObject()
Definition qv4mm_p.h:284
void registerWeakSet(Heap::SetObject *set)
Definition qv4mm.cpp:1476
ObjectType::Data * allocObject(InternalClass *ic, Args &&... args)
Definition qv4mm_p.h:312
Heap::MapObject * weakMaps
Definition qv4mm_p.h:433
void updateUnmanagedHeapSizeGCLimit()
Definition qv4mm.cpp:1447
ManagedType::Data * alloc(Args &&... args)
Definition qv4mm_p.h:329
ObjectType::Data * allocObject(Heap::InternalClass *ic, Args &&... args)
Definition qv4mm_p.h:304
std::size_t unmanagedHeapSizeGCLimit
Definition qv4mm_p.h:440
static constexpr std::size_t align(std::size_t size)
Definition qv4mm_p.h:209
ObjectType::Data * allocateObject(InternalClass *ic)
Definition qv4mm_p.h:278
MarkStack * markStack()
Definition qv4mm_p.h:368
BlockAllocator icAllocator
Definition qv4mm_p.h:428
size_t maxAllocatedMem
Definition qv4mm_p.h:455
static void updateProtoUsage(Heap::Object *o, Heap::InternalClass *ic)
static Heap::InternalClass * cleanInternalClass(Heap::InternalClass *orig)
static PropertyAttributes attributesFromFlags(int flags)
static void addDummyEntry(InternalClass *newClass, PropertyHash::Entry e)
Definition qjsvalue.h:23
GCStateMachine::GCStateInfo GCStateInfo
Definition qv4mm_p.h:118
ChunkAllocator * chunkAllocator
Definition qv4mm_p.h:161
size_t usedMem() const
Definition qv4mm_p.h:145
HeapItem * freeBins[NumBins]
Definition qv4mm_p.h:160
uint * allocationStats
Definition qv4mm_p.h:164
size_t usedSlotsAfterLastSweep
Definition qv4mm_p.h:159
std::vector< Chunk * > chunks
Definition qv4mm_p.h:163
BlockAllocator(ChunkAllocator *chunkAllocator, ExecutionEngine *engine)
Definition qv4mm_p.h:124
void resetBlackBits()
Definition qv4mm.cpp:569
HeapItem * allocate(size_t size, bool forceAllocation=false)
Definition qv4mm.cpp:426
size_t totalSlots() const
Definition qv4mm_p.h:138
static size_t binForSlots(size_t nSlots)
Definition qv4mm_p.h:132
HeapItem * nextFree
Definition qv4mm_p.h:157
size_t allocatedMem() const
Definition qv4mm_p.h:142
ExecutionEngine * engine
Definition qv4mm_p.h:162
virtual ~GCData()
Definition qv4mm_p.h:31
PersistentValueStorage::Iterator it
Definition qv4mm_p.h:34
GCState(* execute)(GCStateMachine *, ExtraData &)
Definition qv4mm_p.h:74
Q_QML_EXPORT void transition()
Definition qv4mm.cpp:1611
std::chrono::microseconds timeLimit
Definition qv4mm_p.h:80
MemoryManager * mm
Definition qv4mm_p.h:84
void handleTimeout(GCState state)
Definition qv4mm_p.h:112
ExtraData stateData
Definition qv4mm_p.h:85
std::array< StepTiming, GCState::Count > executionTiming
Definition qv4mm_p.h:83
std::array< GCStateInfo, GCState::Count > stateInfoMap
Definition qv4mm_p.h:82
QDeadlineTimer deadline
Definition qv4mm_p.h:81
void init(InternalClass *other)
void init(ExecutionEngine *engine)
InternalClassTransition Transition
InternalClassTransition & lookupOrInsertTransition(const InternalClassTransition &t)
static void markObjects(Heap::Base *ic, MarkStack *stack)
static void removeMember(QV4::Object *object, PropertyKey identifier)
PropertyHash::Entry * findEntry(const PropertyKey id)
ExecutionEngine * engine
Definition qv4mm_p.h:185
std::vector< HugeChunk > chunks
Definition qv4mm_p.h:192
HugeItemAllocator(ChunkAllocator *chunkAllocator, ExecutionEngine *engine)
Definition qv4mm_p.h:168
ChunkAllocator * chunkAllocator
Definition qv4mm_p.h:184
size_t usedMem() const
Definition qv4mm_p.h:177
HeapItem * allocate(size_t size)
Definition qv4mm.cpp:575
void sweep(ClassDestroyStatsCallback classCountPtr)
Definition qv4mm.cpp:622
PropertyHash::Entry * entries
void addEntry(const Entry &entry, int classSize)
PropertyHash(const PropertyHash &other)
PropertyHashData * d
void detach(bool grow, int classSize)
bool isStringOrSymbol() const
bool isArrayIndex() const
uint asArrayIndex() const
static PropertyKey invalid()
Scope(ExecutionEngine *e)
SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate &other, uint pos, PropertyKey value)
SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate &other)