blob: 6dfdd81cb2cce092a49d8a15d05bb7630032c5cb [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QV4GC_H
#define QV4GC_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <private/qv4global_p.h>
#include <private/qv4value_p.h>
#include <private/qv4scopedvalue_p.h>
#include <private/qv4object_p.h>
#include <private/qv4mmdefs_p.h>
#include <QVector>
#define QV4_MM_MAXBLOCK_SHIFT "QV4_MM_MAXBLOCK_SHIFT"
#define QV4_MM_MAX_CHUNK_SIZE "QV4_MM_MAX_CHUNK_SIZE"
#define QV4_MM_STATS "QV4_MM_STATS"
#define MM_DEBUG 0
QT_BEGIN_NAMESPACE
namespace QV4 {
struct ChunkAllocator;
struct MemorySegment;
struct BlockAllocator {
BlockAllocator(ChunkAllocator *chunkAllocator, ExecutionEngine *engine)
: chunkAllocator(chunkAllocator), engine(engine)
{
memset(freeBins, 0, sizeof(freeBins));
}
enum { NumBins = 8 };
static inline size_t binForSlots(size_t nSlots) {
return nSlots >= NumBins ? NumBins - 1 : nSlots;
}
HeapItem *allocate(size_t size, bool forceAllocation = false);
size_t totalSlots() const {
return Chunk::AvailableSlots*chunks.size();
}
size_t allocatedMem() const {
return chunks.size()*Chunk::DataSize;
}
size_t usedMem() const {
uint used = 0;
for (auto c : chunks)
used += c->nUsedSlots()*Chunk::SlotSize;
return used;
}
void sweep();
void freeAll();
void resetBlackBits();
void collectGrayItems(MarkStack *markStack);
// bump allocations
HeapItem *nextFree = nullptr;
size_t nFree = 0;
size_t usedSlotsAfterLastSweep = 0;
HeapItem *freeBins[NumBins];
ChunkAllocator *chunkAllocator;
ExecutionEngine *engine;
std::vector<Chunk *> chunks;
uint *allocationStats = nullptr;
};
struct HugeItemAllocator {
HugeItemAllocator(ChunkAllocator *chunkAllocator, ExecutionEngine *engine)
: chunkAllocator(chunkAllocator), engine(engine)
{}
HeapItem *allocate(size_t size);
void sweep(ClassDestroyStatsCallback classCountPtr);
void freeAll();
void resetBlackBits();
void collectGrayItems(MarkStack *markStack);
size_t usedMem() const {
size_t used = 0;
for (const auto &c : chunks)
used += c.size;
return used;
}
ChunkAllocator *chunkAllocator;
ExecutionEngine *engine;
struct HugeChunk {
MemorySegment *segment;
Chunk *chunk;
size_t size;
};
std::vector<HugeChunk> chunks;
};
class Q_QML_EXPORT MemoryManager
{
Q_DISABLE_COPY(MemoryManager);
public:
MemoryManager(ExecutionEngine *engine);
~MemoryManager();
// 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).
// Note: all occurrences of "16" in alloc/dealloc are also due to the alignment.
Q_DECL_CONSTEXPR static inline std::size_t align(std::size_t size)
{ return (size + Chunk::SlotSize - 1) & ~(Chunk::SlotSize - 1); }
template<typename ManagedType>
inline typename ManagedType::Data *allocManaged(std::size_t size, Heap::InternalClass *ic)
{
Q_STATIC_ASSERT(std::is_trivial< typename ManagedType::Data >::value);
size = align(size);
typename ManagedType::Data *d = static_cast<typename ManagedType::Data *>(allocData(size));
d->internalClass.set(engine, ic);
Q_ASSERT(d->internalClass && d->internalClass->vtable);
Q_ASSERT(ic->vtable == ManagedType::staticVTable());
return d;
}
template<typename ManagedType>
inline typename ManagedType::Data *allocManaged(std::size_t size, InternalClass *ic)
{
return allocManaged<ManagedType>(size, ic->d());
}
template<typename ManagedType>
inline typename ManagedType::Data *allocManaged(std::size_t size)
{
Scope scope(engine);
Scoped<InternalClass> ic(scope, ManagedType::defaultInternalClass(engine));
return allocManaged<ManagedType>(size, ic);
}
template <typename ObjectType>
typename ObjectType::Data *allocateObject(Heap::InternalClass *ic)
{
Heap::Object *o = allocObjectWithMemberData(ObjectType::staticVTable(), ic->size);
o->internalClass.set(engine, ic);
Q_ASSERT(o->internalClass.get() && o->vtable());
Q_ASSERT(o->vtable() == ObjectType::staticVTable());
return static_cast<typename ObjectType::Data *>(o);
}
template <typename ObjectType>
typename ObjectType::Data *allocateObject(InternalClass *ic)
{
return allocateObject<ObjectType>(ic->d());
}
template <typename ObjectType>
typename ObjectType::Data *allocateObject()
{
Scope scope(engine);
Scoped<InternalClass> ic(scope, ObjectType::defaultInternalClass(engine));
ic = ic->changeVTable(ObjectType::staticVTable());
ic = ic->changePrototype(ObjectType::defaultPrototype(engine)->d());
return allocateObject<ObjectType>(ic);
}
template <typename ManagedType, typename Arg1>
typename ManagedType::Data *allocWithStringData(std::size_t unmanagedSize, Arg1 arg1)
{
typename ManagedType::Data *o = reinterpret_cast<typename ManagedType::Data *>(allocString(unmanagedSize));
o->internalClass.set(engine, ManagedType::defaultInternalClass(engine));
Q_ASSERT(o->internalClass && o->internalClass->vtable);
o->init(arg1);
return o;
}
template <typename ObjectType, typename... Args>
typename ObjectType::Data *allocObject(Heap::InternalClass *ic, Args... args)
{
typename ObjectType::Data *d = allocateObject<ObjectType>(ic);
d->init(args...);
return d;
}
template <typename ObjectType, typename... Args>
typename ObjectType::Data *allocObject(InternalClass *ic, Args... args)
{
typename ObjectType::Data *d = allocateObject<ObjectType>(ic);
d->init(args...);
return d;
}
template <typename ObjectType, typename... Args>
typename ObjectType::Data *allocate(Args... args)
{
Scope scope(engine);
Scoped<ObjectType> t(scope, allocateObject<ObjectType>());
t->d_unchecked()->init(args...);
return t->d();
}
template <typename ManagedType, typename... Args>
typename ManagedType::Data *alloc(Args... args)
{
Scope scope(engine);
Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
t->d_unchecked()->init(args...);
return t->d();
}
void runGC();
void dumpStats() const;
size_t getUsedMem() const;
size_t getAllocatedMem() const;
size_t getLargeItemsMem() const;
// called when a JS object grows itself. Specifically: Heap::String::append
// and InternalClassDataPrivate<PropertyAttributes>.
void changeUnmanagedHeapSizeUsage(qptrdiff delta) { unmanagedHeapSize += delta; }
template<typename ManagedType>
typename ManagedType::Data *allocIC()
{
Heap::Base *b = *allocate(&icAllocator, align(sizeof(typename ManagedType::Data)));
return static_cast<typename ManagedType::Data *>(b);
}
void registerWeakMap(Heap::MapObject *map);
void registerWeakSet(Heap::SetObject *set);
protected:
/// expects size to be aligned
Heap::Base *allocString(std::size_t unmanagedSize);
Heap::Base *allocData(std::size_t size);
Heap::Object *allocObjectWithMemberData(const QV4::VTable *vtable, uint nMembers);
private:
enum {
MinUnmanagedHeapSizeGCLimit = 128 * 1024
};
void collectFromJSStack(MarkStack *markStack) const;
void mark();
void sweep(bool lastSweep = false, ClassDestroyStatsCallback classCountPtr = nullptr);
bool shouldRunGC() const;
void collectRoots(MarkStack *markStack);
HeapItem *allocate(BlockAllocator *allocator, std::size_t size)
{
bool didGCRun = false;
if (aggressiveGC) {
runGC();
didGCRun = true;
}
if (unmanagedHeapSize > unmanagedHeapSizeGCLimit) {
if (!didGCRun)
runGC();
if (3*unmanagedHeapSizeGCLimit <= 4 * unmanagedHeapSize) {
// more than 75% full, raise limit
unmanagedHeapSizeGCLimit = std::max(unmanagedHeapSizeGCLimit,
unmanagedHeapSize) * 2;
} else if (unmanagedHeapSize * 4 <= unmanagedHeapSizeGCLimit) {
// less than 25% full, lower limit
unmanagedHeapSizeGCLimit = qMax(std::size_t(MinUnmanagedHeapSizeGCLimit),
unmanagedHeapSizeGCLimit/2);
}
didGCRun = true;
}
if (size > Chunk::DataSize)
return hugeItemAllocator.allocate(size);
if (HeapItem *m = allocator->allocate(size))
return m;
if (!didGCRun && shouldRunGC())
runGC();
return allocator->allocate(size, true);
}
public:
QV4::ExecutionEngine *engine;
ChunkAllocator *chunkAllocator;
BlockAllocator blockAllocator;
BlockAllocator icAllocator;
HugeItemAllocator hugeItemAllocator;
PersistentValueStorage *m_persistentValues;
PersistentValueStorage *m_weakValues;
QVector<Value *> m_pendingFreedObjectWrapperValue;
Heap::MapObject *weakMaps = nullptr;
Heap::SetObject *weakSets = nullptr;
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.
std::size_t unmanagedHeapSizeGCLimit;
std::size_t usedSlotsAfterLastFullSweep = 0;
bool gcBlocked = false;
bool aggressiveGC = false;
bool gcStats = false;
bool gcCollectorStats = false;
int allocationCount = 0;
size_t lastAllocRequestedSlots = 0;
struct {
size_t maxReservedMem = 0;
size_t maxAllocatedMem = 0;
size_t maxUsedMem = 0;
uint allocations[BlockAllocator::NumBins];
} statistics;
};
}
QT_END_NAMESPACE
#endif // QV4GC_H