Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
50
51struct GCData { virtual ~GCData(){};};
52
56struct GCStateMachine;
57
59 using ExtraData = std::variant<std::monostate, GCIteratorStorage>;
60 GCState (*execute)(GCStateMachine *, ExtraData &) = nullptr; // Function to execute for this state, returns true if ready to transition
61 bool breakAfter{false};
62};
63
67 std::chrono::microseconds timeLimit{};
69 std::array<GCStateInfo, GCState::Count> stateInfoMap;
70 MemoryManager *mm = nullptr;
71 ExtraData stateData; // extra date for specific states
72
74
75 inline void step() {
76 if (!inProgress()) {
77 reset();
78 }
79 transition();
80 }
81
82 inline bool inProgress() {
83 return state != GCState::Invalid;
84 }
85
86 inline void reset() {
88 }
89
90 Q_QML_EXPORT void transition();
91
94 }
95};
96
97
98struct ChunkAllocator;
99struct MemorySegment;
100
107
108 enum { NumBins = 8 };
109
110 static inline size_t binForSlots(size_t nSlots) {
111 return nSlots >= NumBins ? NumBins - 1 : nSlots;
112 }
113
114 HeapItem *allocate(size_t size, bool forceAllocation = false);
115
116 size_t totalSlots() const {
117 return Chunk::AvailableSlots*chunks.size();
118 }
119
120 size_t allocatedMem() const {
121 return chunks.size()*Chunk::DataSize;
122 }
123 size_t usedMem() const {
124 uint used = 0;
125 for (auto c : chunks)
126 used += c->nUsedSlots()*Chunk::SlotSize;
127 return used;
128 }
129
130 void sweep();
131 void freeAll();
132 void resetBlackBits();
133
134 // bump allocations
135 HeapItem *nextFree = nullptr;
136 size_t nFree = 0;
141 std::vector<Chunk *> chunks;
143};
144
149
150 HeapItem *allocate(size_t size);
151 void sweep(ClassDestroyStatsCallback classCountPtr);
152 void freeAll();
153 void resetBlackBits();
154
155 size_t usedMem() const {
156 size_t used = 0;
157 for (const auto &c : chunks)
158 used += c.size;
159 return used;
160 }
161
169
170 std::vector<HugeChunk> chunks;
171};
172
173
174class Q_QML_EXPORT MemoryManager
175{
176 Q_DISABLE_COPY(MemoryManager);
177
178public:
181
182 template <typename ToBeMarked>
183 friend struct GCCriticalSection;
184
185 // 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).
186 // Note: all occurrences of "16" in alloc/dealloc are also due to the alignment.
187 constexpr static inline std::size_t align(std::size_t size)
188 { return (size + Chunk::SlotSize - 1) & ~(Chunk::SlotSize - 1); }
189
190 /* NOTE: allocManaged comes in various overloads. If size is not passed explicitly
191 sizeof(ManagedType::Data) is used for size. However, there are quite a few cases
192 where we allocate more than sizeof(ManagedType::Data); that's generally the case
193 when the Object has a ValueArray member.
194 If no internal class pointer is provided, ManagedType::defaultInternalClass(engine)
195 will be used as the internal class.
196 */
197
198 template<typename ManagedType>
199 inline typename ManagedType::Data *allocManaged(std::size_t size, Heap::InternalClass *ic)
200 {
201 Q_STATIC_ASSERT(std::is_trivial_v<typename ManagedType::Data>);
202 size = align(size);
203 typename ManagedType::Data *d = static_cast<typename ManagedType::Data *>(allocData(size));
204 d->internalClass.set(engine, ic);
205 Q_ASSERT(d->internalClass && d->internalClass->vtable);
206 Q_ASSERT(ic->vtable == ManagedType::staticVTable());
207 return d;
208 }
209
210 template<typename ManagedType>
211 inline typename ManagedType::Data *allocManaged(Heap::InternalClass *ic)
212 {
213 return allocManaged<ManagedType>(sizeof(typename ManagedType::Data), ic);
214 }
215
216 template<typename ManagedType>
217 inline typename ManagedType::Data *allocManaged(std::size_t size, InternalClass *ic)
218 {
219 return allocManaged<ManagedType>(size, ic->d());
220 }
221
222 template<typename ManagedType>
223 inline typename ManagedType::Data *allocManaged(InternalClass *ic)
224 {
225 return allocManaged<ManagedType>(sizeof(typename ManagedType::Data), ic);
226 }
227
228 template<typename ManagedType>
229 inline typename ManagedType::Data *allocManaged(std::size_t size)
230 {
231 Scope scope(engine);
232 Scoped<InternalClass> ic(scope, ManagedType::defaultInternalClass(engine));
233 return allocManaged<ManagedType>(size, ic);
234 }
235
236 template<typename ManagedType>
237 inline typename ManagedType::Data *allocManaged()
238 {
239 auto constexpr size = sizeof(typename ManagedType::Data);
240 Scope scope(engine);
241 Scoped<InternalClass> ic(scope, ManagedType::defaultInternalClass(engine));
242 return allocManaged<ManagedType>(size, ic);
243 }
244
245 template <typename ObjectType>
246 typename ObjectType::Data *allocateObject(Heap::InternalClass *ic)
247 {
248 Heap::Object *o = allocObjectWithMemberData(ObjectType::staticVTable(), ic->size);
249 o->internalClass.set(engine, ic);
250 Q_ASSERT(o->internalClass.get() && o->vtable());
251 Q_ASSERT(o->vtable() == ObjectType::staticVTable());
252 return static_cast<typename ObjectType::Data *>(o);
253 }
254
255 template <typename ObjectType>
256 typename ObjectType::Data *allocateObject(InternalClass *ic)
257 {
258 return allocateObject<ObjectType>(ic->d());
259 }
260
261 template <typename ObjectType>
262 typename ObjectType::Data *allocateObject()
263 {
264 Scope scope(engine);
265 Scoped<InternalClass> ic(scope, ObjectType::defaultInternalClass(engine));
266 ic = ic->changeVTable(ObjectType::staticVTable());
267 ic = ic->changePrototype(ObjectType::defaultPrototype(engine)->d());
268 return allocateObject<ObjectType>(ic);
269 }
270
271 template <typename ManagedType, typename Arg1>
272 typename ManagedType::Data *allocWithStringData(std::size_t unmanagedSize, Arg1 &&arg1)
273 {
274 typename ManagedType::Data *o = reinterpret_cast<typename ManagedType::Data *>(allocString(unmanagedSize));
275 o->internalClass.set(engine, ManagedType::defaultInternalClass(engine));
276 Q_ASSERT(o->internalClass && o->internalClass->vtable);
277 o->init(std::forward<Arg1>(arg1));
278 return o;
279 }
280
281 template <typename ObjectType, typename... Args>
282 typename ObjectType::Data *allocObject(Heap::InternalClass *ic, Args&&... args)
283 {
284 typename ObjectType::Data *d = allocateObject<ObjectType>(ic);
285 d->init(std::forward<Args>(args)...);
286 return d;
287 }
288
289 template <typename ObjectType, typename... Args>
290 typename ObjectType::Data *allocObject(InternalClass *ic, Args&&... args)
291 {
292 typename ObjectType::Data *d = allocateObject<ObjectType>(ic);
293 d->init(std::forward<Args>(args)...);
294 return d;
295 }
296
297 template <typename ObjectType, typename... Args>
298 typename ObjectType::Data *allocate(Args&&... args)
299 {
300 Scope scope(engine);
301 Scoped<ObjectType> t(scope, allocateObject<ObjectType>());
302 t->d_unchecked()->init(std::forward<Args>(args)...);
303 return t->d();
304 }
305
306 template <typename ManagedType, typename... Args>
307 typename ManagedType::Data *alloc(Args&&... args)
308 {
309 Scope scope(engine);
310 Scoped<ManagedType> t(scope, allocManaged<ManagedType>());
311 t->d_unchecked()->init(std::forward<Args>(args)...);
312 return t->d();
313 }
314
315 void runGC();
316 bool tryForceGCCompletion();
317 void runFullGC();
318
319 void dumpStats() const;
320
321 size_t getUsedMem() const;
322 size_t getAllocatedMem() const;
323 size_t getLargeItemsMem() const;
324
325 // called when a JS object grows itself. Specifically: Heap::String::append
326 // and InternalClassDataPrivate<PropertyAttributes>.
327 void changeUnmanagedHeapSizeUsage(qptrdiff delta) { unmanagedHeapSize += delta; }
328
329 // called at the end of a gc cycle
330 void updateUnmanagedHeapSizeGCLimit();
331
332 template<typename ManagedType>
333 typename ManagedType::Data *allocIC()
334 {
335 Heap::Base *b = *allocate(&icAllocator, align(sizeof(typename ManagedType::Data)));
336 return static_cast<typename ManagedType::Data *>(b);
337 }
338
339 void registerWeakMap(Heap::MapObject *map);
340 void registerWeakSet(Heap::SetObject *set);
341
342 void onEventLoop();
343
344 //GC related methods
345 void setGCTimeLimit(int timeMs);
346 MarkStack* markStack() { return m_markStack.get(); }
347
348protected:
350 Heap::Base *allocString(std::size_t unmanagedSize);
351 Heap::Base *allocData(std::size_t size);
352 Heap::Object *allocObjectWithMemberData(const QV4::VTable *vtable, uint nMembers);
353
354private:
355 enum {
356 MinUnmanagedHeapSizeGCLimit = 128 * 1024
357 };
358
359public:
360 void collectFromJSStack(MarkStack *markStack) const;
361 void sweep(bool lastSweep = false, ClassDestroyStatsCallback classCountPtr = nullptr);
362 void cleanupDeletedQObjectWrappersInSweep();
364 {
365 const bool incrementalGCIsAlreadyRunning = m_markStack != nullptr;
366 const bool aboveUnmanagedHeapLimit = incrementalGCIsAlreadyRunning
367 ? unmanagedHeapSize > 3 * unmanagedHeapSizeGCLimit / 2
368 : unmanagedHeapSize > unmanagedHeapSizeGCLimit;
369 return aboveUnmanagedHeapLimit;
370 }
371private:
372 bool shouldRunGC() const;
373
374 HeapItem *allocate(BlockAllocator *allocator, std::size_t size)
375 {
376 const bool incrementalGCIsAlreadyRunning = m_markStack != nullptr;
377
378 bool didGCRun = false;
379 if (aggressiveGC) {
380 runFullGC();
381 didGCRun = true;
382 }
383
384 if (isAboveUnmanagedHeapLimit()) {
385 if (!didGCRun)
386 incrementalGCIsAlreadyRunning ? (void) tryForceGCCompletion() : runGC();
387 didGCRun = true;
388 }
389
390 if (size > Chunk::DataSize)
391 return hugeItemAllocator.allocate(size);
392
393 if (HeapItem *m = allocator->allocate(size))
394 return m;
395
396 if (!didGCRun && shouldRunGC())
397 runGC();
398
399 return allocator->allocate(size, true);
400 }
401
402public:
411 Heap::MapObject *weakMaps = nullptr;
412 Heap::SetObject *weakSets = nullptr;
413
414 std::unique_ptr<GCStateMachine> gcStateMachine{nullptr};
415 std::unique_ptr<MarkStack> m_markStack{nullptr};
416
417 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.
419 std::size_t usedSlotsAfterLastFullSweep = 0;
420
421 enum Blockness : quint8 {Unblocked, NormalBlocked, InCriticalSection };
422 Blockness gcBlocked = Unblocked;
423 bool aggressiveGC = false;
424 bool gcStats = false;
425 bool gcCollectorStats = false;
426
427 int allocationCount = 0;
428 size_t lastAllocRequestedSlots = 0;
429
430 struct {
431 size_t maxReservedMem = 0;
432 size_t maxAllocatedMem = 0;
433 size_t maxUsedMem = 0;
434 uint allocations[BlockAllocator::NumBins];
435 } statistics;
436};
437
445template <typename ToBeMarked = void>
447 Q_DISABLE_COPY_MOVE(GCCriticalSection)
448
449 Q_NODISCARD_CTOR GCCriticalSection(QV4::ExecutionEngine *engine, ToBeMarked *toBeMarked = nullptr)
450 : m_engine(engine)
451 , m_oldState(std::exchange(engine->memoryManager->gcBlocked, MemoryManager::InCriticalSection))
452 , m_toBeMarked(toBeMarked)
453 {
454 // disallow nested critical sections
456 }
458 {
459 m_engine->memoryManager->gcBlocked = m_oldState;
460 if (m_oldState != MemoryManager::Unblocked)
461 if constexpr (!std::is_same_v<ToBeMarked, void>)
462 if (m_toBeMarked)
463 m_toBeMarked->markObjects(m_engine->memoryManager->markStack());
464 /* because we blocked the gc, we might be using too much memoryon the unmanaged heap
465 and did not run the normal fixup logic. So recheck again, and trigger a gc run
466 if necessary*/
468 return;
469 if (!m_engine->isGCOngoing) {
470 m_engine->memoryManager->runGC();
471 } else {
472 [[maybe_unused]] bool gcFinished = m_engine->memoryManager->tryForceGCCompletion();
473 Q_ASSERT(gcFinished);
474 }
475 }
476
477private:
478 QV4::ExecutionEngine *m_engine;
479 MemoryManager::Blockness m_oldState;
480 ToBeMarked *m_toBeMarked;
481};
482
483}
484
486
487#endif // QV4GC_H
\inmodule QtCore
ManagedType::Data * allocManaged(InternalClass *ic)
Definition qv4mm_p.h:223
bool isAboveUnmanagedHeapLimit()
Definition qv4mm_p.h:363
QVector< Value * > m_pendingFreedObjectWrapperValue
Definition qv4mm_p.h:410
QV4::ExecutionEngine * engine
Definition qv4mm_p.h:403
ObjectType::Data * allocateObject(Heap::InternalClass *ic)
Definition qv4mm_p.h:246
ManagedType::Data * allocManaged()
Definition qv4mm_p.h:237
bool tryForceGCCompletion()
Definition qv4mm.cpp:1236
ManagedType::Data * allocManaged(std::size_t size, Heap::InternalClass *ic)
Definition qv4mm_p.h:199
ManagedType::Data * allocManaged(std::size_t size, InternalClass *ic)
Definition qv4mm_p.h:217
void changeUnmanagedHeapSizeUsage(qptrdiff delta)
Definition qv4mm_p.h:327
ObjectType::Data * allocate(Args &&... args)
Definition qv4mm_p.h:298
Blockness gcBlocked
Definition qv4mm_p.h:422
ChunkAllocator * chunkAllocator
Definition qv4mm_p.h:404
PersistentValueStorage * m_persistentValues
Definition qv4mm_p.h:408
ManagedType::Data * allocWithStringData(std::size_t unmanagedSize, Arg1 &&arg1)
Definition qv4mm_p.h:272
PersistentValueStorage * m_weakValues
Definition qv4mm_p.h:409
ManagedType::Data * allocManaged(std::size_t size)
Definition qv4mm_p.h:229
ManagedType::Data * allocManaged(Heap::InternalClass *ic)
Definition qv4mm_p.h:211
BlockAllocator blockAllocator
Definition qv4mm_p.h:405
HugeItemAllocator hugeItemAllocator
Definition qv4mm_p.h:407
ManagedType::Data * allocIC()
Definition qv4mm_p.h:333
ObjectType::Data * allocateObject()
Definition qv4mm_p.h:262
ObjectType::Data * allocObject(InternalClass *ic, Args &&... args)
Definition qv4mm_p.h:290
ManagedType::Data * alloc(Args &&... args)
Definition qv4mm_p.h:307
ObjectType::Data * allocObject(Heap::InternalClass *ic, Args &&... args)
Definition qv4mm_p.h:282
std::size_t unmanagedHeapSizeGCLimit
Definition qv4mm_p.h:418
static constexpr std::size_t align(std::size_t size)
Definition qv4mm_p.h:187
ObjectType::Data * allocateObject(InternalClass *ic)
Definition qv4mm_p.h:256
MarkStack * markStack()
Definition qv4mm_p.h:346
BlockAllocator icAllocator
Definition qv4mm_p.h:406
QMap< QString, QString > map
[6]
Combined button and popup list for selecting options.
void(* ClassDestroyStatsCallback)(const char *)
Definition qv4mmdefs_p.h:30
GCState
Definition qv4mm_p.h:31
@ MarkDrain
Definition qv4mm_p.h:39
@ MarkJSStack
Definition qv4mm_p.h:34
@ InitCallDestroyObjects
Definition qv4mm_p.h:41
@ HandleQObjectWrappers
Definition qv4mm_p.h:45
@ Count
Definition qv4mm_p.h:48
@ MarkPersistentValues
Definition qv4mm_p.h:36
@ CallDestroyObjects
Definition qv4mm_p.h:42
@ MarkWeakValues
Definition qv4mm_p.h:38
@ DoSweep
Definition qv4mm_p.h:46
@ InitMarkPersistentValues
Definition qv4mm_p.h:35
@ MarkGlobalObject
Definition qv4mm_p.h:33
@ MarkReady
Definition qv4mm_p.h:40
@ InitMarkWeakValues
Definition qv4mm_p.h:37
@ MarkStart
Definition qv4mm_p.h:32
@ Invalid
Definition qv4mm_p.h:47
@ FreeWeakMaps
Definition qv4mm_p.h:43
@ FreeWeakSets
Definition qv4mm_p.h:44
#define Q_STATIC_ASSERT(Condition)
Definition qassert.h:108
#define Q_NODISCARD_CTOR
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
GLboolean GLboolean GLboolean b
const GLfloat * m
GLenum GLuint GLintptr GLsizeiptr size
[1]
const GLubyte * c
GLuint GLuint GLuint GLuint arg1
GLdouble GLdouble t
Definition qopenglext.h:243
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_UNUSED(x)
ptrdiff_t qptrdiff
Definition qtypes.h:164
unsigned int uint
Definition qtypes.h:34
unsigned char quint8
Definition qtypes.h:46
QFuture< QSet< QChar > > set
[10]
QJSValueList args
QJSEngine engine
[0]
ChunkAllocator * chunkAllocator
Definition qv4mm_p.h:139
size_t usedMem() const
Definition qv4mm_p.h:123
HeapItem * freeBins[NumBins]
Definition qv4mm_p.h:138
uint * allocationStats
Definition qv4mm_p.h:142
size_t usedSlotsAfterLastSweep
Definition qv4mm_p.h:137
std::vector< Chunk * > chunks
Definition qv4mm_p.h:141
BlockAllocator(ChunkAllocator *chunkAllocator, ExecutionEngine *engine)
Definition qv4mm_p.h:102
void resetBlackBits()
Definition qv4mm.cpp:593
HeapItem * allocate(size_t size, bool forceAllocation=false)
Definition qv4mm.cpp:445
size_t totalSlots() const
Definition qv4mm_p.h:116
static size_t binForSlots(size_t nSlots)
Definition qv4mm_p.h:110
HeapItem * nextFree
Definition qv4mm_p.h:135
size_t allocatedMem() const
Definition qv4mm_p.h:120
ExecutionEngine * engine
Definition qv4mm_p.h:140
MemoryManager * memoryManager
virtual ~GCData()
Definition qv4mm_p.h:51
PersistentValueStorage::Iterator it
Definition qv4mm_p.h:54
std::variant< std::monostate, GCIteratorStorage > ExtraData
Definition qv4mm_p.h:59
GCState(* execute)(GCStateMachine *, ExtraData &)
Definition qv4mm_p.h:60
Q_QML_EXPORT void transition()
Definition qv4mm.cpp:1469
std::chrono::microseconds timeLimit
Definition qv4mm_p.h:67
MemoryManager * mm
Definition qv4mm_p.h:70
void handleTimeout(GCState state)
Definition qv4mm_p.h:92
ExtraData stateData
Definition qv4mm_p.h:71
std::array< GCStateInfo, GCState::Count > stateInfoMap
Definition qv4mm_p.h:69
GCStateInfo::ExtraData ExtraData
Definition qv4mm_p.h:65
QDeadlineTimer deadline
Definition qv4mm_p.h:68
ExecutionEngine * engine
Definition qv4mm_p.h:163
std::vector< HugeChunk > chunks
Definition qv4mm_p.h:170
HugeItemAllocator(ChunkAllocator *chunkAllocator, ExecutionEngine *engine)
Definition qv4mm_p.h:146
ChunkAllocator * chunkAllocator
Definition qv4mm_p.h:162
size_t usedMem() const
Definition qv4mm_p.h:155
HeapItem * allocate(size_t size)
Definition qv4mm.cpp:599
void sweep(ClassDestroyStatsCallback classCountPtr)
Definition qv4mm.cpp:646