2012-12-04 12:40:18 +00:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
2016-01-19 09:38:36 +00:00
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
2012-12-04 12:40:18 +00:00
|
|
|
**
|
2013-06-24 11:50:51 +00:00
|
|
|
** This file is part of the QtQml module of the Qt Toolkit.
|
2012-12-04 12:40:18 +00:00
|
|
|
**
|
2016-01-19 09:38:36 +00:00
|
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
2012-12-04 12:40:18 +00:00
|
|
|
** 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
|
2015-01-28 11:55:39 +00:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
2016-01-19 09:38:36 +00:00
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
2012-12-04 12:40:18 +00:00
|
|
|
**
|
|
|
|
** GNU Lesser General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
2016-01-19 09:38:36 +00:00
|
|
|
** 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.
|
2012-12-04 12:40:18 +00:00
|
|
|
**
|
2016-01-19 09:38:36 +00:00
|
|
|
** 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.
|
2012-12-04 12:40:18 +00:00
|
|
|
**
|
2013-03-29 03:00:03 +00:00
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
2012-12-04 12:40:18 +00:00
|
|
|
****************************************************************************/
|
|
|
|
|
2013-04-15 09:50:16 +00:00
|
|
|
#include "qv4engine_p.h"
|
|
|
|
#include "qv4object_p.h"
|
|
|
|
#include "qv4objectproto_p.h"
|
|
|
|
#include "qv4mm_p.h"
|
2013-06-07 09:21:18 +00:00
|
|
|
#include "qv4qobjectwrapper_p.h"
|
2016-12-22 14:20:05 +00:00
|
|
|
#include <QtCore/qalgorithms.h>
|
|
|
|
#include <QtCore/private/qnumeric_p.h>
|
2013-05-24 11:19:15 +00:00
|
|
|
#include <qqmlengine.h>
|
2016-12-22 14:20:05 +00:00
|
|
|
#include "PageReservation.h"
|
2012-12-18 14:03:26 +00:00
|
|
|
#include "PageAllocation.h"
|
2016-12-22 14:20:05 +00:00
|
|
|
#include "PageAllocationAligned.h"
|
2012-12-18 14:03:26 +00:00
|
|
|
#include "StdLibExtras.h"
|
2012-12-04 12:40:18 +00:00
|
|
|
|
2016-08-02 11:25:35 +00:00
|
|
|
#include <QElapsedTimer>
|
2012-12-13 22:46:51 +00:00
|
|
|
#include <QMap>
|
2016-07-25 07:25:11 +00:00
|
|
|
#include <QScopedValueRollback>
|
2012-12-04 12:40:18 +00:00
|
|
|
|
|
|
|
#include <iostream>
|
2012-12-10 08:56:30 +00:00
|
|
|
#include <cstdlib>
|
2013-09-12 09:06:59 +00:00
|
|
|
#include <algorithm>
|
2013-02-08 08:30:40 +00:00
|
|
|
#include "qv4alloca_p.h"
|
2014-06-02 16:33:19 +00:00
|
|
|
#include "qv4profiling_p.h"
|
2012-12-04 12:40:18 +00:00
|
|
|
|
2017-02-13 12:38:48 +00:00
|
|
|
//#define MM_STATS
|
|
|
|
|
|
|
|
#if !defined(MM_STATS) && !defined(QT_NO_DEBUG)
|
|
|
|
#define MM_STATS
|
|
|
|
#endif
|
2016-12-22 14:20:05 +00:00
|
|
|
|
|
|
|
#if MM_DEBUG
|
|
|
|
#define DEBUG qDebug() << "MM:"
|
|
|
|
#else
|
|
|
|
#define DEBUG if (1) ; else qDebug() << "MM:"
|
|
|
|
#endif
|
|
|
|
|
2013-03-12 18:49:13 +00:00
|
|
|
#ifdef V4_USE_VALGRIND
|
|
|
|
#include <valgrind/valgrind.h>
|
|
|
|
#include <valgrind/memcheck.h>
|
|
|
|
#endif
|
|
|
|
|
2016-06-20 17:31:29 +00:00
|
|
|
#ifdef V4_USE_HEAPTRACK
|
|
|
|
#include <heaptrack_api.h>
|
|
|
|
#endif
|
|
|
|
|
2013-06-27 19:51:22 +00:00
|
|
|
#if OS(QNX)
|
|
|
|
#include <sys/storage.h> // __tls()
|
|
|
|
#endif
|
|
|
|
|
2014-01-21 09:55:18 +00:00
|
|
|
#if USE(PTHREADS) && HAVE(PTHREAD_NP_H)
|
|
|
|
#include <pthread_np.h>
|
|
|
|
#endif
|
|
|
|
|
2016-05-20 08:36:02 +00:00
|
|
|
#define MIN_UNMANAGED_HEAPSIZE_GC_LIMIT std::size_t(128 * 1024)
|
2015-09-08 13:24:00 +00:00
|
|
|
|
2015-01-09 17:52:56 +00:00
|
|
|
using namespace WTF;
|
|
|
|
|
2013-06-24 13:28:00 +00:00
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
2016-12-22 14:20:05 +00:00
|
|
|
namespace QV4 {
|
|
|
|
|
|
|
|
enum {
|
|
|
|
MinSlotsGCLimit = QV4::Chunk::AvailableSlots*16,
|
|
|
|
GCOverallocation = 200 /* Max overallocation by the GC in % */
|
|
|
|
};
|
|
|
|
|
|
|
|
struct MemorySegment {
|
|
|
|
enum {
|
|
|
|
NumChunks = 8*sizeof(quint64),
|
|
|
|
SegmentSize = NumChunks*Chunk::ChunkSize,
|
|
|
|
};
|
|
|
|
|
|
|
|
MemorySegment(size_t size)
|
|
|
|
{
|
|
|
|
size += Chunk::ChunkSize; // make sure we can get enough 64k aligment memory
|
|
|
|
if (size < SegmentSize)
|
|
|
|
size = SegmentSize;
|
|
|
|
|
|
|
|
pageReservation = PageReservation::reserve(size, OSAllocator::JSGCHeapPages);
|
|
|
|
base = reinterpret_cast<Chunk *>((reinterpret_cast<quintptr>(pageReservation.base()) + Chunk::ChunkSize - 1) & ~(Chunk::ChunkSize - 1));
|
|
|
|
nChunks = NumChunks;
|
2017-03-21 13:49:10 +00:00
|
|
|
availableBytes = size - (reinterpret_cast<quintptr>(base) - reinterpret_cast<quintptr>(pageReservation.base()));
|
|
|
|
if (availableBytes < SegmentSize)
|
2016-12-22 14:20:05 +00:00
|
|
|
--nChunks;
|
|
|
|
}
|
|
|
|
MemorySegment(MemorySegment &&other) {
|
|
|
|
qSwap(pageReservation, other.pageReservation);
|
|
|
|
qSwap(base, other.base);
|
|
|
|
qSwap(allocatedMap, other.allocatedMap);
|
2017-03-21 13:49:10 +00:00
|
|
|
qSwap(availableBytes, other.availableBytes);
|
|
|
|
qSwap(nChunks, other.nChunks);
|
2016-12-22 14:20:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
~MemorySegment() {
|
|
|
|
if (base)
|
|
|
|
pageReservation.deallocate();
|
|
|
|
}
|
|
|
|
|
|
|
|
void setBit(size_t index) {
|
|
|
|
Q_ASSERT(index < nChunks);
|
|
|
|
quint64 bit = static_cast<quint64>(1) << index;
|
|
|
|
// qDebug() << " setBit" << hex << index << (index & (Bits - 1)) << bit;
|
|
|
|
allocatedMap |= bit;
|
|
|
|
}
|
|
|
|
void clearBit(size_t index) {
|
|
|
|
Q_ASSERT(index < nChunks);
|
|
|
|
quint64 bit = static_cast<quint64>(1) << index;
|
|
|
|
// qDebug() << " setBit" << hex << index << (index & (Bits - 1)) << bit;
|
|
|
|
allocatedMap &= ~bit;
|
|
|
|
}
|
|
|
|
bool testBit(size_t index) const {
|
|
|
|
Q_ASSERT(index < nChunks);
|
|
|
|
quint64 bit = static_cast<quint64>(1) << index;
|
|
|
|
return (allocatedMap & bit);
|
|
|
|
}
|
|
|
|
|
|
|
|
Chunk *allocate(size_t size);
|
|
|
|
void free(Chunk *chunk, size_t size) {
|
|
|
|
DEBUG << "freeing chunk" << chunk;
|
|
|
|
size_t index = static_cast<size_t>(chunk - base);
|
2017-03-21 13:49:10 +00:00
|
|
|
size_t end = qMin(static_cast<size_t>(NumChunks), index + (size - 1)/Chunk::ChunkSize + 1);
|
2016-12-22 14:20:05 +00:00
|
|
|
while (index < end) {
|
|
|
|
Q_ASSERT(testBit(index));
|
|
|
|
clearBit(index);
|
|
|
|
++index;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t pageSize = WTF::pageSize();
|
|
|
|
size = (size + pageSize - 1) & ~(pageSize - 1);
|
2017-04-21 09:57:22 +00:00
|
|
|
#if !defined(Q_OS_LINUX) && !defined(Q_OS_WIN)
|
|
|
|
// Linux and Windows zero out pages that have been decommitted and get committed again.
|
|
|
|
// unfortunately that's not true on other OSes (e.g. BSD based ones), so zero out the
|
|
|
|
// memory before decommit, so that we can be sure that all chunks we allocate will be
|
|
|
|
// zero initialized.
|
|
|
|
memset(chunk, 0, size);
|
|
|
|
#endif
|
2016-12-22 14:20:05 +00:00
|
|
|
pageReservation.decommit(chunk, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool contains(Chunk *c) const {
|
|
|
|
return c >= base && c < base + nChunks;
|
|
|
|
}
|
|
|
|
|
|
|
|
PageReservation pageReservation;
|
|
|
|
Chunk *base = 0;
|
|
|
|
quint64 allocatedMap = 0;
|
2017-03-21 13:49:10 +00:00
|
|
|
size_t availableBytes = 0;
|
2016-12-22 14:20:05 +00:00
|
|
|
uint nChunks = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
Chunk *MemorySegment::allocate(size_t size)
|
|
|
|
{
|
2017-03-21 13:49:10 +00:00
|
|
|
if (!allocatedMap && size >= SegmentSize) {
|
|
|
|
// chunk allocated for one huge allocation
|
|
|
|
Q_ASSERT(availableBytes >= size);
|
|
|
|
pageReservation.commit(base, size);
|
|
|
|
allocatedMap = ~static_cast<quintptr>(0);
|
|
|
|
return base;
|
|
|
|
}
|
2016-12-22 14:20:05 +00:00
|
|
|
size_t requiredChunks = (size + sizeof(Chunk) - 1)/sizeof(Chunk);
|
|
|
|
uint sequence = 0;
|
|
|
|
Chunk *candidate = 0;
|
|
|
|
for (uint i = 0; i < nChunks; ++i) {
|
|
|
|
if (!testBit(i)) {
|
|
|
|
if (!candidate)
|
|
|
|
candidate = base + i;
|
|
|
|
++sequence;
|
|
|
|
} else {
|
|
|
|
candidate = 0;
|
|
|
|
sequence = 0;
|
|
|
|
}
|
|
|
|
if (sequence == requiredChunks) {
|
|
|
|
pageReservation.commit(candidate, size);
|
|
|
|
for (uint i = 0; i < requiredChunks; ++i)
|
|
|
|
setBit(candidate - base + i);
|
2017-01-03 10:49:15 +00:00
|
|
|
DEBUG << "allocated chunk " << candidate << hex << size;
|
2016-12-22 14:20:05 +00:00
|
|
|
return candidate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ChunkAllocator {
|
|
|
|
ChunkAllocator() {}
|
|
|
|
|
|
|
|
size_t requiredChunkSize(size_t size) {
|
|
|
|
size += Chunk::HeaderSize; // space required for the Chunk header
|
|
|
|
size_t pageSize = WTF::pageSize();
|
|
|
|
size = (size + pageSize - 1) & ~(pageSize - 1); // align to page sizes
|
|
|
|
if (size < Chunk::ChunkSize)
|
|
|
|
size = Chunk::ChunkSize;
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
Chunk *allocate(size_t size = 0);
|
|
|
|
void free(Chunk *chunk, size_t size = 0);
|
|
|
|
|
|
|
|
std::vector<MemorySegment> memorySegments;
|
|
|
|
};
|
|
|
|
|
|
|
|
Chunk *ChunkAllocator::allocate(size_t size)
|
|
|
|
{
|
|
|
|
size = requiredChunkSize(size);
|
|
|
|
for (auto &m : memorySegments) {
|
|
|
|
if (~m.allocatedMap) {
|
|
|
|
Chunk *c = m.allocate(size);
|
|
|
|
if (c)
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// allocate a new segment
|
|
|
|
memorySegments.push_back(MemorySegment(size));
|
|
|
|
Chunk *c = memorySegments.back().allocate(size);
|
|
|
|
Q_ASSERT(c);
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChunkAllocator::free(Chunk *chunk, size_t size)
|
|
|
|
{
|
|
|
|
size = requiredChunkSize(size);
|
|
|
|
for (auto &m : memorySegments) {
|
|
|
|
if (m.contains(chunk)) {
|
|
|
|
m.free(chunk, size);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Q_ASSERT(false);
|
|
|
|
}
|
|
|
|
|
2017-04-27 17:37:35 +00:00
|
|
|
#ifdef DUMP_SWEEP
|
|
|
|
QString binary(quintptr n) {
|
|
|
|
QString s = QString::number(n, 2);
|
|
|
|
while (s.length() < 64)
|
|
|
|
s.prepend(QChar::fromLatin1('0'));
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
#define SDUMP qDebug
|
|
|
|
#else
|
|
|
|
QString binary(quintptr) { return QString(); }
|
|
|
|
#define SDUMP if (1) ; else qDebug
|
|
|
|
#endif
|
2012-12-04 12:40:18 +00:00
|
|
|
|
2017-03-09 09:36:16 +00:00
|
|
|
void Heap::Base::markChildren(MarkStack *markStack)
|
2017-04-04 08:05:58 +00:00
|
|
|
{
|
|
|
|
if (vtable()->markObjects)
|
2017-03-09 09:36:16 +00:00
|
|
|
vtable()->markObjects(this, markStack);
|
2017-04-04 08:05:58 +00:00
|
|
|
if (quint64 m = vtable()->markTable) {
|
|
|
|
// qDebug() << "using mark table:" << hex << m << "for" << h;
|
|
|
|
void **mem = reinterpret_cast<void **>(this);
|
|
|
|
while (m) {
|
|
|
|
MarkFlags mark = static_cast<MarkFlags>(m & 3);
|
|
|
|
switch (mark) {
|
|
|
|
case Mark_NoMark:
|
|
|
|
break;
|
|
|
|
case Mark_Value:
|
|
|
|
// qDebug() << "marking value at " << mem;
|
2017-03-09 09:36:16 +00:00
|
|
|
reinterpret_cast<Value *>(mem)->mark(markStack);
|
2017-04-04 08:05:58 +00:00
|
|
|
break;
|
|
|
|
case Mark_Pointer: {
|
|
|
|
// qDebug() << "marking pointer at " << mem;
|
|
|
|
Heap::Base *p = *reinterpret_cast<Heap::Base **>(mem);
|
|
|
|
if (p)
|
2017-03-09 09:36:16 +00:00
|
|
|
p->mark(markStack);
|
2017-04-04 08:05:58 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Mark_ValueArray: {
|
|
|
|
Q_ASSERT(m == Mark_ValueArray);
|
|
|
|
// qDebug() << "marking Value Array at offset" << hex << (mem - reinterpret_cast<void **>(h));
|
|
|
|
ValueArray<0> *a = reinterpret_cast<ValueArray<0> *>(mem);
|
|
|
|
Value *v = a->values;
|
|
|
|
const Value *end = v + a->alloc;
|
|
|
|
if (a->alloc > 32*1024) {
|
|
|
|
// drain from time to time to avoid overflows in the js stack
|
2017-03-09 09:36:16 +00:00
|
|
|
Heap::Base **currentBase = markStack->top;
|
2017-04-04 08:05:58 +00:00
|
|
|
while (v < end) {
|
2017-03-09 09:36:16 +00:00
|
|
|
v->mark(markStack);
|
2017-04-04 08:05:58 +00:00
|
|
|
++v;
|
2017-03-09 09:36:16 +00:00
|
|
|
if (markStack->top >= currentBase + 32*1024) {
|
|
|
|
Heap::Base **oldBase = markStack->base;
|
|
|
|
markStack->base = currentBase;
|
|
|
|
markStack->drain();
|
|
|
|
markStack->base = oldBase;
|
|
|
|
}
|
2017-04-04 08:05:58 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while (v < end) {
|
2017-03-09 09:36:16 +00:00
|
|
|
v->mark(markStack);
|
2017-04-04 08:05:58 +00:00
|
|
|
++v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m >>= 2;
|
|
|
|
++mem;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-14 13:03:56 +00:00
|
|
|
// Stores a classname -> freed count mapping.
|
|
|
|
typedef QHash<const char*, int> MMStatsHash;
|
|
|
|
Q_GLOBAL_STATIC(MMStatsHash, freedObjectStatsGlobal)
|
2017-04-04 08:05:58 +00:00
|
|
|
|
2017-02-14 13:03:56 +00:00
|
|
|
// This indirection avoids sticking QHash code in each of the call sites, which
|
|
|
|
// shaves off some instructions in the case that it's unused.
|
|
|
|
static void increaseFreedCountForClass(const char *className)
|
|
|
|
{
|
|
|
|
(*freedObjectStatsGlobal())[className]++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Chunk::sweep(ClassDestroyStatsCallback classCountPtr)
|
2017-01-03 10:49:15 +00:00
|
|
|
{
|
2017-04-27 17:37:35 +00:00
|
|
|
SDUMP() << "sweeping chunk" << this;
|
2017-01-03 10:49:15 +00:00
|
|
|
HeapItem *o = realBase();
|
2017-04-27 17:37:35 +00:00
|
|
|
bool lastSlotFree = false;
|
2017-01-03 10:49:15 +00:00
|
|
|
for (uint i = 0; i < Chunk::EntriesInBitmap; ++i) {
|
2017-02-08 15:21:02 +00:00
|
|
|
#if WRITEBARRIER(none)
|
2017-01-03 10:49:15 +00:00
|
|
|
Q_ASSERT((grayBitmap[i] | blackBitmap[i]) == blackBitmap[i]); // check that we don't have gray only objects
|
2017-02-08 15:21:02 +00:00
|
|
|
#endif
|
2017-01-03 10:49:15 +00:00
|
|
|
quintptr toFree = objectBitmap[i] ^ blackBitmap[i];
|
|
|
|
Q_ASSERT((toFree & objectBitmap[i]) == toFree); // check all black objects are marked as being used
|
|
|
|
quintptr e = extendsBitmap[i];
|
2017-04-27 17:37:35 +00:00
|
|
|
SDUMP() << " index=" << i;
|
|
|
|
SDUMP() << " toFree =" << binary(toFree);
|
|
|
|
SDUMP() << " black =" << binary(blackBitmap[i]);
|
|
|
|
SDUMP() << " object =" << binary(objectBitmap[i]);
|
|
|
|
SDUMP() << " extends =" << binary(e);
|
|
|
|
if (lastSlotFree)
|
|
|
|
e &= (e + 1); // clear all lowest extent bits
|
2017-01-03 10:49:15 +00:00
|
|
|
while (toFree) {
|
|
|
|
uint index = qCountTrailingZeroBits(toFree);
|
|
|
|
quintptr bit = (static_cast<quintptr>(1) << index);
|
|
|
|
|
|
|
|
toFree ^= bit; // mask out freed slot
|
|
|
|
// DEBUG << " index" << hex << index << toFree;
|
|
|
|
|
|
|
|
// remove all extends slots that have been freed
|
|
|
|
// this is a bit of bit trickery.
|
|
|
|
quintptr mask = (bit << 1) - 1; // create a mask of 1's to the right of and up to the current bit
|
|
|
|
quintptr objmask = e | mask; // or'ing mask with e gives all ones until the end of the current object
|
|
|
|
quintptr result = objmask + 1;
|
|
|
|
Q_ASSERT(qCountTrailingZeroBits(result) - index != 0); // ensure we freed something
|
|
|
|
result |= mask; // ensure we don't clear stuff to the right of the current object
|
|
|
|
e &= result;
|
|
|
|
|
|
|
|
HeapItem *itemToFree = o + index;
|
|
|
|
Heap::Base *b = *itemToFree;
|
2017-02-14 13:03:56 +00:00
|
|
|
const VTable *v = b->vtable();
|
|
|
|
if (Q_UNLIKELY(classCountPtr))
|
|
|
|
classCountPtr(v->className);
|
|
|
|
if (v->destroy) {
|
|
|
|
v->destroy(b);
|
2017-01-03 10:49:15 +00:00
|
|
|
b->_checkIsDestroyed();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
objectBitmap[i] = blackBitmap[i];
|
2017-02-15 14:40:27 +00:00
|
|
|
grayBitmap[i] = 0;
|
2017-01-03 10:49:15 +00:00
|
|
|
extendsBitmap[i] = e;
|
2017-04-27 17:37:35 +00:00
|
|
|
lastSlotFree = !((objectBitmap[i]|extendsBitmap[i]) >> (sizeof(quintptr)*8 - 1));
|
|
|
|
SDUMP() << " new extends =" << binary(e);
|
|
|
|
SDUMP() << " lastSlotFree" << lastSlotFree;
|
|
|
|
Q_ASSERT((objectBitmap[i] & extendsBitmap[i]) == 0);
|
2017-01-03 10:49:15 +00:00
|
|
|
o += Chunk::Bits;
|
|
|
|
}
|
|
|
|
// DEBUG << "swept chunk" << this << "freed" << slotsFreed << "slots.";
|
|
|
|
}
|
|
|
|
|
|
|
|
void Chunk::freeAll()
|
|
|
|
{
|
|
|
|
// DEBUG << "sweeping chunk" << this << (*freeList);
|
|
|
|
HeapItem *o = realBase();
|
|
|
|
for (uint i = 0; i < Chunk::EntriesInBitmap; ++i) {
|
|
|
|
quintptr toFree = objectBitmap[i];
|
|
|
|
quintptr e = extendsBitmap[i];
|
|
|
|
// DEBUG << hex << " index=" << i << toFree;
|
|
|
|
while (toFree) {
|
|
|
|
uint index = qCountTrailingZeroBits(toFree);
|
|
|
|
quintptr bit = (static_cast<quintptr>(1) << index);
|
|
|
|
|
|
|
|
toFree ^= bit; // mask out freed slot
|
|
|
|
// DEBUG << " index" << hex << index << toFree;
|
|
|
|
|
|
|
|
// remove all extends slots that have been freed
|
|
|
|
// this is a bit of bit trickery.
|
|
|
|
quintptr mask = (bit << 1) - 1; // create a mask of 1's to the right of and up to the current bit
|
|
|
|
quintptr objmask = e | mask; // or'ing mask with e gives all ones until the end of the current object
|
|
|
|
quintptr result = objmask + 1;
|
|
|
|
Q_ASSERT(qCountTrailingZeroBits(result) - index != 0); // ensure we freed something
|
|
|
|
result |= mask; // ensure we don't clear stuff to the right of the current object
|
|
|
|
e &= result;
|
|
|
|
|
|
|
|
HeapItem *itemToFree = o + index;
|
|
|
|
Heap::Base *b = *itemToFree;
|
|
|
|
if (b->vtable()->destroy) {
|
|
|
|
b->vtable()->destroy(b);
|
|
|
|
b->_checkIsDestroyed();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
objectBitmap[i] = 0;
|
2017-02-15 14:40:27 +00:00
|
|
|
grayBitmap[i] = 0;
|
2017-01-03 10:49:15 +00:00
|
|
|
extendsBitmap[i] = e;
|
|
|
|
o += Chunk::Bits;
|
|
|
|
}
|
|
|
|
// DEBUG << "swept chunk" << this << "freed" << slotsFreed << "slots.";
|
|
|
|
}
|
|
|
|
|
2017-02-15 14:40:27 +00:00
|
|
|
void Chunk::resetBlackBits()
|
|
|
|
{
|
|
|
|
memset(blackBitmap, 0, sizeof(blackBitmap));
|
|
|
|
}
|
|
|
|
|
2017-02-13 12:38:48 +00:00
|
|
|
#ifdef MM_STATS
|
2017-02-15 14:40:27 +00:00
|
|
|
static uint nGrayItems = 0;
|
|
|
|
#endif
|
|
|
|
|
2017-03-09 09:36:16 +00:00
|
|
|
void Chunk::collectGrayItems(MarkStack *markStack)
|
2017-02-15 14:40:27 +00:00
|
|
|
{
|
|
|
|
// DEBUG << "sweeping chunk" << this << (*freeList);
|
|
|
|
HeapItem *o = realBase();
|
|
|
|
for (uint i = 0; i < Chunk::EntriesInBitmap; ++i) {
|
|
|
|
#if WRITEBARRIER(none)
|
|
|
|
Q_ASSERT((grayBitmap[i] | blackBitmap[i]) == blackBitmap[i]); // check that we don't have gray only objects
|
|
|
|
#endif
|
|
|
|
quintptr toMark = blackBitmap[i] & grayBitmap[i]; // correct for a Steele type barrier
|
|
|
|
Q_ASSERT((toMark & objectBitmap[i]) == toMark); // check all black objects are marked as being used
|
|
|
|
// DEBUG << hex << " index=" << i << toFree;
|
|
|
|
while (toMark) {
|
|
|
|
uint index = qCountTrailingZeroBits(toMark);
|
|
|
|
quintptr bit = (static_cast<quintptr>(1) << index);
|
|
|
|
|
|
|
|
toMark ^= bit; // mask out marked slot
|
|
|
|
// DEBUG << " index" << hex << index << toFree;
|
|
|
|
|
|
|
|
HeapItem *itemToFree = o + index;
|
|
|
|
Heap::Base *b = *itemToFree;
|
|
|
|
Q_ASSERT(b->inUse());
|
2017-03-09 09:36:16 +00:00
|
|
|
markStack->push(b);
|
2017-02-13 12:38:48 +00:00
|
|
|
#ifdef MM_STATS
|
2017-02-15 14:40:27 +00:00
|
|
|
++nGrayItems;
|
|
|
|
// qDebug() << "adding gray item" << b << "to mark stack";
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
grayBitmap[i] = 0;
|
|
|
|
o += Chunk::Bits;
|
|
|
|
}
|
|
|
|
// DEBUG << "swept chunk" << this << "freed" << slotsFreed << "slots.";
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-01-03 10:49:15 +00:00
|
|
|
void Chunk::sortIntoBins(HeapItem **bins, uint nBins)
|
|
|
|
{
|
2017-02-10 10:51:43 +00:00
|
|
|
// qDebug() << "sortIntoBins:";
|
2017-01-03 10:49:15 +00:00
|
|
|
HeapItem *base = realBase();
|
|
|
|
#if QT_POINTER_SIZE == 8
|
|
|
|
const int start = 0;
|
|
|
|
#else
|
|
|
|
const int start = 1;
|
2017-02-10 10:51:43 +00:00
|
|
|
#endif
|
2017-02-13 12:38:48 +00:00
|
|
|
#ifdef MM_STATS
|
2017-02-10 10:51:43 +00:00
|
|
|
uint freeSlots = 0;
|
|
|
|
uint allocatedSlots = 0;
|
2017-01-03 10:49:15 +00:00
|
|
|
#endif
|
|
|
|
for (int i = start; i < EntriesInBitmap; ++i) {
|
|
|
|
quintptr usedSlots = (objectBitmap[i]|extendsBitmap[i]);
|
|
|
|
#if QT_POINTER_SIZE == 8
|
|
|
|
if (!i)
|
|
|
|
usedSlots |= (static_cast<quintptr>(1) << (HeaderSize/SlotSize)) - 1;
|
|
|
|
#endif
|
2017-02-13 12:38:48 +00:00
|
|
|
#ifdef MM_STATS
|
2017-02-10 10:51:43 +00:00
|
|
|
allocatedSlots += qPopulationCount(usedSlots);
|
|
|
|
// qDebug() << hex << " i=" << i << "used=" << usedSlots;
|
|
|
|
#endif
|
|
|
|
while (1) {
|
|
|
|
uint index = qCountTrailingZeroBits(usedSlots + 1);
|
|
|
|
if (index == Bits)
|
|
|
|
break;
|
|
|
|
uint freeStart = i*Bits + index;
|
|
|
|
usedSlots &= ~((static_cast<quintptr>(1) << index) - 1);
|
|
|
|
while (!usedSlots) {
|
|
|
|
++i;
|
|
|
|
if (i == EntriesInBitmap) {
|
|
|
|
usedSlots = (quintptr)-1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
usedSlots = (objectBitmap[i]|extendsBitmap[i]);
|
2017-02-13 12:38:48 +00:00
|
|
|
#ifdef MM_STATS
|
2017-02-10 10:51:43 +00:00
|
|
|
allocatedSlots += qPopulationCount(usedSlots);
|
|
|
|
// qDebug() << hex << " i=" << i << "used=" << usedSlots;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
HeapItem *freeItem = base + freeStart;
|
|
|
|
|
|
|
|
index = qCountTrailingZeroBits(usedSlots);
|
|
|
|
usedSlots |= (quintptr(1) << index) - 1;
|
|
|
|
uint freeEnd = i*Bits + index;
|
|
|
|
uint nSlots = freeEnd - freeStart;
|
2017-02-13 12:38:48 +00:00
|
|
|
#ifdef MM_STATS
|
2017-02-10 10:51:43 +00:00
|
|
|
// qDebug() << hex << " got free slots from" << freeStart << "to" << freeEnd << "n=" << nSlots << "usedSlots=" << usedSlots;
|
|
|
|
freeSlots += nSlots;
|
|
|
|
#endif
|
|
|
|
Q_ASSERT(freeEnd > freeStart && freeEnd <= NumSlots);
|
|
|
|
freeItem->freeData.availableSlots = nSlots;
|
|
|
|
uint bin = qMin(nBins - 1, nSlots);
|
|
|
|
freeItem->freeData.next = bins[bin];
|
|
|
|
bins[bin] = freeItem;
|
2017-01-03 10:49:15 +00:00
|
|
|
}
|
|
|
|
}
|
2017-02-13 12:38:48 +00:00
|
|
|
#ifdef MM_STATS
|
2017-02-15 11:48:07 +00:00
|
|
|
Q_ASSERT(freeSlots + allocatedSlots == (EntriesInBitmap - start) * 8 * sizeof(quintptr));
|
2017-02-10 10:51:43 +00:00
|
|
|
#endif
|
2017-01-03 10:49:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-12-22 20:24:27 +00:00
|
|
|
template<typename T>
|
|
|
|
StackAllocator<T>::StackAllocator(ChunkAllocator *chunkAlloc)
|
|
|
|
: chunkAllocator(chunkAlloc)
|
|
|
|
{
|
|
|
|
chunks.push_back(chunkAllocator->allocate());
|
|
|
|
firstInChunk = chunks.back()->first();
|
|
|
|
nextFree = firstInChunk;
|
|
|
|
lastInChunk = firstInChunk + (Chunk::AvailableSlots - 1)/requiredSlots*requiredSlots;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
void StackAllocator<T>::freeAll()
|
|
|
|
{
|
|
|
|
for (auto c : chunks)
|
|
|
|
chunkAllocator->free(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
void StackAllocator<T>::nextChunk() {
|
|
|
|
Q_ASSERT(nextFree == lastInChunk);
|
|
|
|
++currentChunk;
|
|
|
|
if (currentChunk >= chunks.size()) {
|
|
|
|
Chunk *newChunk = chunkAllocator->allocate();
|
|
|
|
chunks.push_back(newChunk);
|
|
|
|
}
|
|
|
|
firstInChunk = chunks.at(currentChunk)->first();
|
|
|
|
nextFree = firstInChunk;
|
|
|
|
lastInChunk = firstInChunk + (Chunk::AvailableSlots - 1)/requiredSlots*requiredSlots;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
void QV4::StackAllocator<T>::prevChunk() {
|
|
|
|
Q_ASSERT(nextFree == firstInChunk);
|
|
|
|
Q_ASSERT(chunks.at(currentChunk) == nextFree->chunk());
|
|
|
|
Q_ASSERT(currentChunk > 0);
|
|
|
|
--currentChunk;
|
|
|
|
firstInChunk = chunks.at(currentChunk)->first();
|
|
|
|
lastInChunk = firstInChunk + (Chunk::AvailableSlots - 1)/requiredSlots*requiredSlots;
|
|
|
|
nextFree = lastInChunk;
|
|
|
|
}
|
|
|
|
|
|
|
|
template struct StackAllocator<Heap::CallContext>;
|
|
|
|
|
2017-01-03 10:49:15 +00:00
|
|
|
|
|
|
|
HeapItem *BlockAllocator::allocate(size_t size, bool forceAllocation) {
|
|
|
|
Q_ASSERT((size % Chunk::SlotSize) == 0);
|
|
|
|
size_t slotsRequired = size >> Chunk::SlotSizeShift;
|
|
|
|
#if MM_DEBUG
|
|
|
|
++allocations[bin];
|
|
|
|
#endif
|
|
|
|
|
|
|
|
HeapItem **last;
|
|
|
|
|
|
|
|
HeapItem *m;
|
|
|
|
|
|
|
|
if (slotsRequired < NumBins - 1) {
|
|
|
|
m = freeBins[slotsRequired];
|
|
|
|
if (m) {
|
|
|
|
freeBins[slotsRequired] = m->freeData.next;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
2017-02-10 10:51:43 +00:00
|
|
|
|
2017-01-03 10:49:15 +00:00
|
|
|
if (nFree >= slotsRequired) {
|
|
|
|
// use bump allocation
|
|
|
|
Q_ASSERT(nextFree);
|
|
|
|
m = nextFree;
|
|
|
|
nextFree += slotsRequired;
|
|
|
|
nFree -= slotsRequired;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
// DEBUG << "No matching bin found for item" << size << bin;
|
|
|
|
// search last bin for a large enough item
|
|
|
|
last = &freeBins[NumBins - 1];
|
|
|
|
while ((m = *last)) {
|
|
|
|
if (m->freeData.availableSlots >= slotsRequired) {
|
|
|
|
*last = m->freeData.next; // take it out of the list
|
|
|
|
|
|
|
|
size_t remainingSlots = m->freeData.availableSlots - slotsRequired;
|
|
|
|
// DEBUG << "found large free slots of size" << m->freeData.availableSlots << m << "remaining" << remainingSlots;
|
2017-02-10 10:51:43 +00:00
|
|
|
if (remainingSlots == 0)
|
2017-01-03 10:49:15 +00:00
|
|
|
goto done;
|
2017-02-10 10:51:43 +00:00
|
|
|
|
2017-01-03 10:49:15 +00:00
|
|
|
HeapItem *remainder = m + slotsRequired;
|
2017-02-10 11:17:35 +00:00
|
|
|
if (remainingSlots > nFree) {
|
2017-01-03 10:49:15 +00:00
|
|
|
if (nFree) {
|
|
|
|
size_t bin = binForSlots(nFree);
|
|
|
|
nextFree->freeData.next = freeBins[bin];
|
|
|
|
nextFree->freeData.availableSlots = nFree;
|
|
|
|
freeBins[bin] = nextFree;
|
|
|
|
}
|
|
|
|
nextFree = remainder;
|
|
|
|
nFree = remainingSlots;
|
|
|
|
} else {
|
|
|
|
remainder->freeData.availableSlots = remainingSlots;
|
|
|
|
size_t binForRemainder = binForSlots(remainingSlots);
|
|
|
|
remainder->freeData.next = freeBins[binForRemainder];
|
|
|
|
freeBins[binForRemainder] = remainder;
|
|
|
|
}
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
last = &m->freeData.next;
|
|
|
|
}
|
|
|
|
|
2017-02-10 11:17:35 +00:00
|
|
|
if (slotsRequired < NumBins - 1) {
|
|
|
|
// check if we can split up another slot
|
|
|
|
for (size_t i = slotsRequired + 1; i < NumBins - 1; ++i) {
|
|
|
|
m = freeBins[i];
|
|
|
|
if (m) {
|
|
|
|
freeBins[i] = m->freeData.next; // take it out of the list
|
|
|
|
// qDebug() << "got item" << slotsRequired << "from slot" << i;
|
|
|
|
size_t remainingSlots = i - slotsRequired;
|
|
|
|
Q_ASSERT(remainingSlots < NumBins - 1);
|
|
|
|
HeapItem *remainder = m + slotsRequired;
|
|
|
|
remainder->freeData.availableSlots = remainingSlots;
|
|
|
|
remainder->freeData.next = freeBins[remainingSlots];
|
|
|
|
freeBins[remainingSlots] = remainder;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-03 10:49:15 +00:00
|
|
|
if (!m) {
|
|
|
|
if (!forceAllocation)
|
|
|
|
return 0;
|
|
|
|
Chunk *newChunk = chunkAllocator->allocate();
|
|
|
|
chunks.push_back(newChunk);
|
|
|
|
nextFree = newChunk->first();
|
|
|
|
nFree = Chunk::AvailableSlots;
|
|
|
|
m = nextFree;
|
|
|
|
nextFree += slotsRequired;
|
|
|
|
nFree -= slotsRequired;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
m->setAllocatedSlots(slotsRequired);
|
|
|
|
// DEBUG << " " << hex << m->chunk() << m->chunk()->objectBitmap[0] << m->chunk()->extendsBitmap[0] << (m - m->chunk()->realBase());
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2017-02-14 13:03:56 +00:00
|
|
|
void BlockAllocator::sweep(ClassDestroyStatsCallback classCountPtr)
|
2017-01-03 10:49:15 +00:00
|
|
|
{
|
|
|
|
nextFree = 0;
|
|
|
|
nFree = 0;
|
|
|
|
memset(freeBins, 0, sizeof(freeBins));
|
|
|
|
|
|
|
|
// qDebug() << "BlockAlloc: sweep";
|
|
|
|
usedSlotsAfterLastSweep = 0;
|
|
|
|
for (auto c : chunks) {
|
2017-02-14 13:03:56 +00:00
|
|
|
c->sweep(classCountPtr);
|
2017-01-03 10:49:15 +00:00
|
|
|
c->sortIntoBins(freeBins, NumBins);
|
|
|
|
// qDebug() << "used slots in chunk" << c << ":" << c->nUsedSlots();
|
|
|
|
usedSlotsAfterLastSweep += c->nUsedSlots();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void BlockAllocator::freeAll()
|
|
|
|
{
|
|
|
|
for (auto c : chunks) {
|
|
|
|
c->freeAll();
|
|
|
|
chunkAllocator->free(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-15 14:40:27 +00:00
|
|
|
void BlockAllocator::resetBlackBits()
|
|
|
|
{
|
|
|
|
for (auto c : chunks)
|
|
|
|
c->resetBlackBits();
|
|
|
|
}
|
|
|
|
|
2017-03-09 09:36:16 +00:00
|
|
|
void BlockAllocator::collectGrayItems(MarkStack *markStack)
|
2017-02-15 14:40:27 +00:00
|
|
|
{
|
|
|
|
for (auto c : chunks)
|
2017-03-09 09:36:16 +00:00
|
|
|
c->collectGrayItems(markStack);
|
2017-02-15 14:40:27 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-01-03 10:49:15 +00:00
|
|
|
#if MM_DEBUG
|
|
|
|
void BlockAllocator::stats() {
|
|
|
|
DEBUG << "MM stats:";
|
|
|
|
QString s;
|
|
|
|
for (int i = 0; i < 10; ++i) {
|
|
|
|
uint c = 0;
|
|
|
|
HeapItem *item = freeBins[i];
|
|
|
|
while (item) {
|
|
|
|
++c;
|
|
|
|
item = item->freeData.next;
|
|
|
|
}
|
|
|
|
s += QString::number(c) + QLatin1String(", ");
|
|
|
|
}
|
|
|
|
HeapItem *item = freeBins[NumBins - 1];
|
|
|
|
uint c = 0;
|
|
|
|
while (item) {
|
|
|
|
++c;
|
|
|
|
item = item->freeData.next;
|
|
|
|
}
|
|
|
|
s += QLatin1String("..., ") + QString::number(c);
|
|
|
|
DEBUG << "bins:" << s;
|
|
|
|
QString a;
|
|
|
|
for (int i = 0; i < 10; ++i)
|
|
|
|
a += QString::number(allocations[i]) + QLatin1String(", ");
|
|
|
|
a += QLatin1String("..., ") + QString::number(allocations[NumBins - 1]);
|
|
|
|
DEBUG << "allocs:" << a;
|
|
|
|
memset(allocations, 0, sizeof(allocations));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2017-01-20 10:36:16 +00:00
|
|
|
HeapItem *HugeItemAllocator::allocate(size_t size) {
|
|
|
|
Chunk *c = chunkAllocator->allocate(size);
|
|
|
|
chunks.push_back(HugeChunk{c, size});
|
|
|
|
Chunk::setBit(c->objectBitmap, c->first() - c->realBase());
|
|
|
|
return c->first();
|
|
|
|
}
|
|
|
|
|
2017-02-14 13:03:56 +00:00
|
|
|
static void freeHugeChunk(ChunkAllocator *chunkAllocator, const HugeItemAllocator::HugeChunk &c, ClassDestroyStatsCallback classCountPtr)
|
2017-01-20 10:36:16 +00:00
|
|
|
{
|
|
|
|
HeapItem *itemToFree = c.chunk->first();
|
|
|
|
Heap::Base *b = *itemToFree;
|
2017-02-14 13:03:56 +00:00
|
|
|
const VTable *v = b->vtable();
|
|
|
|
if (Q_UNLIKELY(classCountPtr))
|
|
|
|
classCountPtr(v->className);
|
|
|
|
|
|
|
|
if (v->destroy) {
|
|
|
|
v->destroy(b);
|
2017-01-20 10:36:16 +00:00
|
|
|
b->_checkIsDestroyed();
|
|
|
|
}
|
|
|
|
chunkAllocator->free(c.chunk, c.size);
|
|
|
|
}
|
|
|
|
|
2017-02-14 13:03:56 +00:00
|
|
|
void HugeItemAllocator::sweep(ClassDestroyStatsCallback classCountPtr)
|
|
|
|
{
|
|
|
|
auto isBlack = [this, classCountPtr] (const HugeChunk &c) {
|
2017-01-20 10:36:16 +00:00
|
|
|
bool b = c.chunk->first()->isBlack();
|
|
|
|
if (!b)
|
2017-02-14 13:03:56 +00:00
|
|
|
freeHugeChunk(chunkAllocator, c, classCountPtr);
|
2017-01-20 10:36:16 +00:00
|
|
|
return !b;
|
|
|
|
};
|
|
|
|
|
|
|
|
auto newEnd = std::remove_if(chunks.begin(), chunks.end(), isBlack);
|
|
|
|
chunks.erase(newEnd, chunks.end());
|
|
|
|
}
|
|
|
|
|
2017-02-15 14:40:27 +00:00
|
|
|
void HugeItemAllocator::resetBlackBits()
|
|
|
|
{
|
|
|
|
for (auto c : chunks)
|
|
|
|
Chunk::clearBit(c.chunk->blackBitmap, c.chunk->first() - c.chunk->realBase());
|
|
|
|
}
|
|
|
|
|
2017-03-09 09:36:16 +00:00
|
|
|
void HugeItemAllocator::collectGrayItems(MarkStack *markStack)
|
2017-02-15 14:40:27 +00:00
|
|
|
{
|
|
|
|
for (auto c : chunks)
|
|
|
|
// Correct for a Steele type barrier
|
|
|
|
if (Chunk::testBit(c.chunk->blackBitmap, c.chunk->first() - c.chunk->realBase()) &&
|
|
|
|
Chunk::testBit(c.chunk->grayBitmap, c.chunk->first() - c.chunk->realBase())) {
|
|
|
|
HeapItem *i = c.chunk->first();
|
|
|
|
Heap::Base *b = *i;
|
2017-03-09 09:36:16 +00:00
|
|
|
b->mark(markStack);
|
2017-02-15 14:40:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-20 10:36:16 +00:00
|
|
|
void HugeItemAllocator::freeAll()
|
|
|
|
{
|
|
|
|
for (auto &c : chunks) {
|
2017-02-14 13:03:56 +00:00
|
|
|
freeHugeChunk(chunkAllocator, c, nullptr);
|
2017-01-20 10:36:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-12 20:55:51 +00:00
|
|
|
MemoryManager::MemoryManager(ExecutionEngine *engine)
|
2015-08-28 11:48:52 +00:00
|
|
|
: engine(engine)
|
2016-12-22 14:20:05 +00:00
|
|
|
, chunkAllocator(new ChunkAllocator)
|
2016-12-22 20:24:27 +00:00
|
|
|
, stackAllocator(chunkAllocator)
|
2017-01-03 10:49:15 +00:00
|
|
|
, blockAllocator(chunkAllocator)
|
2017-01-20 10:36:16 +00:00
|
|
|
, hugeItemAllocator(chunkAllocator)
|
2015-01-12 20:55:51 +00:00
|
|
|
, m_persistentValues(new PersistentValueStorage(engine))
|
|
|
|
, m_weakValues(new PersistentValueStorage(engine))
|
2017-01-03 11:06:25 +00:00
|
|
|
, unmanagedHeapSizeGCLimit(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT)
|
|
|
|
, aggressiveGC(!qEnvironmentVariableIsEmpty("QV4_MM_AGGRESSIVE_GC"))
|
|
|
|
, gcStats(!qEnvironmentVariableIsEmpty(QV4_MM_STATS))
|
2012-12-04 12:40:18 +00:00
|
|
|
{
|
2013-03-12 18:49:13 +00:00
|
|
|
#ifdef V4_USE_VALGRIND
|
2014-12-12 15:38:09 +00:00
|
|
|
VALGRIND_CREATE_MEMPOOL(this, 0, true);
|
2013-03-12 18:49:13 +00:00
|
|
|
#endif
|
2012-12-04 12:40:18 +00:00
|
|
|
}
|
|
|
|
|
2017-02-13 12:38:48 +00:00
|
|
|
#ifdef MM_STATS
|
|
|
|
static int allocationCount = 0;
|
2017-03-23 15:15:46 +00:00
|
|
|
static size_t lastAllocRequestedSlots = 0;
|
2017-02-10 10:51:43 +00:00
|
|
|
#endif
|
|
|
|
|
2017-01-03 10:49:15 +00:00
|
|
|
Heap::Base *MemoryManager::allocString(std::size_t unmanagedSize)
|
2012-12-04 12:40:18 +00:00
|
|
|
{
|
2017-02-10 10:51:43 +00:00
|
|
|
const size_t stringSize = align(sizeof(Heap::String));
|
2017-02-13 12:38:48 +00:00
|
|
|
#ifdef MM_STATS
|
2017-02-10 10:51:43 +00:00
|
|
|
lastAllocRequestedSlots = stringSize >> Chunk::SlotSizeShift;
|
2017-02-13 12:38:48 +00:00
|
|
|
++allocationCount;
|
2017-02-10 10:51:43 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
bool didGCRun = false;
|
|
|
|
if (aggressiveGC) {
|
2012-12-04 12:40:18 +00:00
|
|
|
runGC();
|
2017-02-10 10:51:43 +00:00
|
|
|
didGCRun = true;
|
|
|
|
}
|
2012-12-04 12:40:18 +00:00
|
|
|
|
2017-01-03 11:06:25 +00:00
|
|
|
unmanagedHeapSize += unmanagedSize;
|
|
|
|
if (unmanagedHeapSize > unmanagedHeapSizeGCLimit) {
|
2017-02-15 14:40:27 +00:00
|
|
|
if (!didGCRun)
|
|
|
|
runGC();
|
2015-07-03 11:20:18 +00:00
|
|
|
|
2017-01-03 11:06:25 +00:00
|
|
|
if (3*unmanagedHeapSizeGCLimit <= 4*unmanagedHeapSize)
|
2015-09-08 13:24:00 +00:00
|
|
|
// more than 75% full, raise limit
|
2017-01-03 11:06:25 +00:00
|
|
|
unmanagedHeapSizeGCLimit = std::max(unmanagedHeapSizeGCLimit, unmanagedHeapSize) * 2;
|
|
|
|
else if (unmanagedHeapSize * 4 <= unmanagedHeapSizeGCLimit)
|
2015-09-08 13:24:00 +00:00
|
|
|
// less than 25% full, lower limit
|
2017-01-03 11:06:25 +00:00
|
|
|
unmanagedHeapSizeGCLimit = qMax(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT, unmanagedHeapSizeGCLimit/2);
|
2015-07-03 11:20:18 +00:00
|
|
|
didGCRun = true;
|
|
|
|
}
|
|
|
|
|
2017-01-04 09:49:23 +00:00
|
|
|
HeapItem *m = blockAllocator.allocate(stringSize);
|
2017-01-03 10:49:15 +00:00
|
|
|
if (!m) {
|
|
|
|
if (!didGCRun && shouldRunGC())
|
|
|
|
runGC();
|
2017-01-04 09:49:23 +00:00
|
|
|
m = blockAllocator.allocate(stringSize, true);
|
2013-11-14 11:05:42 +00:00
|
|
|
}
|
2012-12-04 12:40:18 +00:00
|
|
|
|
2017-02-15 14:40:27 +00:00
|
|
|
// qDebug() << "allocated string" << m;
|
2017-01-04 09:49:23 +00:00
|
|
|
memset(m, 0, stringSize);
|
2017-01-03 10:49:15 +00:00
|
|
|
return *m;
|
|
|
|
}
|
2012-12-13 22:46:51 +00:00
|
|
|
|
2017-01-03 10:49:15 +00:00
|
|
|
Heap::Base *MemoryManager::allocData(std::size_t size)
|
|
|
|
{
|
2017-02-13 12:38:48 +00:00
|
|
|
#ifdef MM_STATS
|
2017-02-10 10:51:43 +00:00
|
|
|
lastAllocRequestedSlots = size >> Chunk::SlotSizeShift;
|
2017-02-13 12:38:48 +00:00
|
|
|
++allocationCount;
|
2017-02-10 10:51:43 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
bool didRunGC = false;
|
|
|
|
if (aggressiveGC) {
|
2013-01-02 21:09:31 +00:00
|
|
|
runGC();
|
2017-02-10 10:51:43 +00:00
|
|
|
didRunGC = true;
|
|
|
|
}
|
2017-01-03 10:49:15 +00:00
|
|
|
#ifdef DETAILED_MM_STATS
|
|
|
|
willAllocate(size);
|
|
|
|
#endif // DETAILED_MM_STATS
|
2012-12-13 22:46:51 +00:00
|
|
|
|
2017-01-03 10:49:15 +00:00
|
|
|
Q_ASSERT(size >= Chunk::SlotSize);
|
|
|
|
Q_ASSERT(size % Chunk::SlotSize == 0);
|
2015-01-09 17:52:56 +00:00
|
|
|
|
2017-01-03 11:06:25 +00:00
|
|
|
// qDebug() << "unmanagedHeapSize:" << unmanagedHeapSize << "limit:" << unmanagedHeapSizeGCLimit << "unmanagedSize:" << unmanagedSize;
|
2012-12-04 12:40:18 +00:00
|
|
|
|
2017-02-15 14:40:27 +00:00
|
|
|
if (size > Chunk::DataSize) {
|
|
|
|
HeapItem *h = hugeItemAllocator.allocate(size);
|
|
|
|
// qDebug() << "allocating huge item" << h;
|
|
|
|
return *h;
|
|
|
|
}
|
2013-03-12 18:49:13 +00:00
|
|
|
|
2017-01-03 10:49:15 +00:00
|
|
|
HeapItem *m = blockAllocator.allocate(size);
|
|
|
|
if (!m) {
|
2017-02-10 10:51:43 +00:00
|
|
|
if (!didRunGC && shouldRunGC())
|
2017-01-03 10:49:15 +00:00
|
|
|
runGC();
|
|
|
|
m = blockAllocator.allocate(size, true);
|
|
|
|
}
|
2017-01-04 09:49:23 +00:00
|
|
|
|
|
|
|
memset(m, 0, size);
|
2017-02-15 14:40:27 +00:00
|
|
|
// qDebug() << "allocating data" << m;
|
2017-01-03 10:49:15 +00:00
|
|
|
return *m;
|
2012-12-04 12:40:18 +00:00
|
|
|
}
|
|
|
|
|
2017-01-04 11:45:45 +00:00
|
|
|
Heap::Object *MemoryManager::allocObjectWithMemberData(std::size_t size, uint nMembers)
|
|
|
|
{
|
2017-02-15 11:23:20 +00:00
|
|
|
Heap::Object *o;
|
|
|
|
if (!nMembers) {
|
|
|
|
o = static_cast<Heap::Object *>(allocData(size));
|
|
|
|
} else {
|
|
|
|
// Allocate both in one go through the block allocator
|
2017-01-04 11:45:45 +00:00
|
|
|
std::size_t memberSize = align(sizeof(Heap::MemberData) + (nMembers - 1)*sizeof(Value));
|
2017-02-15 11:23:20 +00:00
|
|
|
size_t totalSize = size + memberSize;
|
|
|
|
Heap::MemberData *m;
|
|
|
|
if (totalSize > Chunk::DataSize) {
|
|
|
|
o = static_cast<Heap::Object *>(allocData(size));
|
|
|
|
m = hugeItemAllocator.allocate(memberSize)->as<Heap::MemberData>();
|
|
|
|
} else {
|
|
|
|
HeapItem *mh = reinterpret_cast<HeapItem *>(allocData(totalSize));
|
|
|
|
Heap::Base *b = *mh;
|
|
|
|
o = static_cast<Heap::Object *>(b);
|
|
|
|
mh += (size >> Chunk::SlotSizeShift);
|
|
|
|
m = mh->as<Heap::MemberData>();
|
|
|
|
Chunk *c = mh->chunk();
|
|
|
|
size_t index = mh - c->realBase();
|
|
|
|
Chunk::setBit(c->objectBitmap, index);
|
|
|
|
Chunk::clearBit(c->extendsBitmap, index);
|
|
|
|
}
|
|
|
|
o->memberData.set(engine, m);
|
|
|
|
m->setVtable(MemberData::staticVTable());
|
|
|
|
m->values.alloc = static_cast<uint>((memberSize - sizeof(Heap::MemberData) + sizeof(Value))/sizeof(Value));
|
|
|
|
m->values.size = o->memberData->values.alloc;
|
|
|
|
m->init();
|
2017-01-04 11:45:45 +00:00
|
|
|
// qDebug() << " got" << o->memberData << o->memberData->size;
|
|
|
|
}
|
2017-02-15 14:40:27 +00:00
|
|
|
// qDebug() << "allocating object with memberData" << o << o->memberData.operator->();
|
2017-01-04 11:45:45 +00:00
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
2017-02-13 12:38:48 +00:00
|
|
|
static uint markStackSize = 0;
|
|
|
|
|
2017-03-09 09:36:16 +00:00
|
|
|
MarkStack::MarkStack(ExecutionEngine *engine)
|
|
|
|
: engine(engine)
|
|
|
|
{
|
|
|
|
base = (Heap::Base **)engine->gcStack->base();
|
|
|
|
top = base;
|
|
|
|
limit = base + ExecutionEngine::GCStackLimit/sizeof(Heap::Base)*3/4;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MarkStack::drain()
|
2014-10-21 09:05:00 +00:00
|
|
|
{
|
2017-03-09 09:36:16 +00:00
|
|
|
while (top > base) {
|
|
|
|
Heap::Base *h = pop();
|
2017-02-13 12:38:48 +00:00
|
|
|
++markStackSize;
|
2016-05-26 15:46:24 +00:00
|
|
|
Q_ASSERT(h); // at this point we should only have Heap::Base objects in this area on the stack. If not, weird things might happen.
|
2017-03-09 09:36:16 +00:00
|
|
|
h->markChildren(this);
|
2014-10-21 09:05:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-09 09:36:16 +00:00
|
|
|
void MemoryManager::collectRoots(MarkStack *markStack)
|
2012-12-04 12:40:18 +00:00
|
|
|
{
|
2017-04-04 08:35:45 +00:00
|
|
|
engine->markObjects(markStack);
|
2013-01-28 15:46:09 +00:00
|
|
|
|
2017-02-13 12:38:48 +00:00
|
|
|
// qDebug() << " mark stack after engine->mark" << (engine->jsStackTop - markBase);
|
|
|
|
|
2017-03-09 09:36:16 +00:00
|
|
|
collectFromJSStack(markStack);
|
2013-10-15 14:00:49 +00:00
|
|
|
|
2017-02-13 12:38:48 +00:00
|
|
|
// qDebug() << " mark stack after js stack collect" << (engine->jsStackTop - markBase);
|
2017-03-09 09:36:16 +00:00
|
|
|
m_persistentValues->mark(markStack);
|
2015-09-08 12:58:55 +00:00
|
|
|
|
2017-02-13 12:38:48 +00:00
|
|
|
// qDebug() << " mark stack after persistants" << (engine->jsStackTop - markBase);
|
|
|
|
|
2013-05-24 11:19:15 +00:00
|
|
|
// Preserve QObject ownership rules within JavaScript: A parent with c++ ownership
|
|
|
|
// keeps all of its children alive in JavaScript.
|
|
|
|
|
|
|
|
// Do this _after_ collectFromStack to ensure that processing the weak
|
|
|
|
// managed objects in the loop down there doesn't make then end up as leftovers
|
|
|
|
// on the stack and thus always get collected.
|
2015-01-12 20:55:51 +00:00
|
|
|
for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) {
|
2016-11-24 14:39:07 +00:00
|
|
|
QObjectWrapper *qobjectWrapper = (*it).as<QObjectWrapper>();
|
|
|
|
if (!qobjectWrapper)
|
2013-05-24 11:19:15 +00:00
|
|
|
continue;
|
2014-11-11 22:30:54 +00:00
|
|
|
QObject *qobject = qobjectWrapper->object();
|
2013-05-24 11:19:15 +00:00
|
|
|
if (!qobject)
|
|
|
|
continue;
|
2013-05-29 10:18:31 +00:00
|
|
|
bool keepAlive = QQmlData::keepAliveDuringGarbageCollection(qobject);
|
2013-05-24 11:19:15 +00:00
|
|
|
|
|
|
|
if (!keepAlive) {
|
|
|
|
if (QObject *parent = qobject->parent()) {
|
|
|
|
while (parent->parent())
|
|
|
|
parent = parent->parent();
|
|
|
|
|
2013-05-29 10:18:31 +00:00
|
|
|
keepAlive = QQmlData::keepAliveDuringGarbageCollection(parent);
|
2013-05-24 11:19:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (keepAlive)
|
2017-03-09 09:36:16 +00:00
|
|
|
qobjectWrapper->mark(markStack);
|
2013-11-02 15:30:26 +00:00
|
|
|
|
2017-03-09 09:36:16 +00:00
|
|
|
if (markStack->top >= markStack->limit)
|
|
|
|
markStack->drain();
|
2013-05-24 11:19:15 +00:00
|
|
|
}
|
2017-03-09 09:36:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MemoryManager::mark()
|
|
|
|
{
|
|
|
|
markStackSize = 0;
|
|
|
|
|
|
|
|
MarkStack markStack(engine);
|
|
|
|
collectRoots(&markStack);
|
2014-10-21 09:05:00 +00:00
|
|
|
|
2017-03-09 09:36:16 +00:00
|
|
|
markStack.drain();
|
2012-12-04 12:40:18 +00:00
|
|
|
}
|
|
|
|
|
2017-02-14 13:03:56 +00:00
|
|
|
void MemoryManager::sweep(bool lastSweep, ClassDestroyStatsCallback classCountPtr)
|
2012-12-04 12:40:18 +00:00
|
|
|
{
|
2015-08-07 12:26:43 +00:00
|
|
|
for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) {
|
2016-05-26 15:46:24 +00:00
|
|
|
Managed *m = (*it).managed();
|
2016-11-24 14:39:07 +00:00
|
|
|
if (!m || m->markBit())
|
2015-08-07 12:26:43 +00:00
|
|
|
continue;
|
2016-07-25 14:07:16 +00:00
|
|
|
// we need to call destroyObject on qobjectwrappers now, so that they can emit the destroyed
|
2015-08-07 12:26:43 +00:00
|
|
|
// signal before we start sweeping the heap
|
2015-08-14 23:31:13 +00:00
|
|
|
if (QObjectWrapper *qobjectWrapper = (*it).as<QObjectWrapper>())
|
2015-08-07 12:26:43 +00:00
|
|
|
qobjectWrapper->destroyObject(lastSweep);
|
|
|
|
|
|
|
|
(*it) = Primitive::undefinedValue();
|
2013-05-23 20:13:42 +00:00
|
|
|
}
|
|
|
|
|
2016-07-25 14:07:16 +00:00
|
|
|
// onDestruction handlers may have accessed other QObject wrappers and reset their value, so ensure
|
|
|
|
// that they are all set to undefined.
|
|
|
|
for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) {
|
2016-11-24 14:39:07 +00:00
|
|
|
Managed *m = (*it).managed();
|
|
|
|
if (!m || m->markBit())
|
2016-07-25 14:07:16 +00:00
|
|
|
continue;
|
|
|
|
(*it) = Primitive::undefinedValue();
|
2013-05-23 20:13:42 +00:00
|
|
|
}
|
|
|
|
|
2015-12-25 13:36:46 +00:00
|
|
|
// Now it is time to free QV4::QObjectWrapper Value, we must check the Value's tag to make sure its object has been destroyed
|
|
|
|
const int pendingCount = m_pendingFreedObjectWrapperValue.count();
|
|
|
|
if (pendingCount) {
|
|
|
|
QVector<Value *> remainingWeakQObjectWrappers;
|
|
|
|
remainingWeakQObjectWrappers.reserve(pendingCount);
|
|
|
|
for (int i = 0; i < pendingCount; ++i) {
|
|
|
|
Value *v = m_pendingFreedObjectWrapperValue.at(i);
|
2016-10-12 09:15:09 +00:00
|
|
|
if (v->isUndefined() || v->isEmpty())
|
2015-12-25 13:36:46 +00:00
|
|
|
PersistentValueStorage::free(v);
|
|
|
|
else
|
|
|
|
remainingWeakQObjectWrappers.append(v);
|
|
|
|
}
|
|
|
|
m_pendingFreedObjectWrapperValue = remainingWeakQObjectWrappers;
|
|
|
|
}
|
|
|
|
|
2015-08-28 11:48:52 +00:00
|
|
|
if (MultiplyWrappedQObjectMap *multiplyWrappedQObjects = engine->m_multiplyWrappedQObjects) {
|
2013-06-04 12:28:13 +00:00
|
|
|
for (MultiplyWrappedQObjectMap::Iterator it = multiplyWrappedQObjects->begin(); it != multiplyWrappedQObjects->end();) {
|
2015-04-28 13:38:09 +00:00
|
|
|
if (!it.value().isNullOrUndefined())
|
2013-06-04 12:28:13 +00:00
|
|
|
it = multiplyWrappedQObjects->erase(it);
|
|
|
|
else
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-14 13:03:56 +00:00
|
|
|
blockAllocator.sweep(classCountPtr);
|
|
|
|
hugeItemAllocator.sweep(classCountPtr);
|
2017-01-03 10:49:15 +00:00
|
|
|
}
|
2013-11-14 11:05:42 +00:00
|
|
|
|
2017-01-03 10:49:15 +00:00
|
|
|
bool MemoryManager::shouldRunGC() const
|
|
|
|
{
|
|
|
|
size_t total = blockAllocator.totalSlots();
|
2017-02-16 09:52:48 +00:00
|
|
|
if (total > MinSlotsGCLimit && usedSlotsAfterLastFullSweep * GCOverallocation < total * 100)
|
2017-01-03 10:49:15 +00:00
|
|
|
return true;
|
|
|
|
return false;
|
2012-12-04 12:40:18 +00:00
|
|
|
}
|
|
|
|
|
2017-02-10 10:51:43 +00:00
|
|
|
size_t dumpBins(BlockAllocator *b, bool printOutput = true)
|
|
|
|
{
|
2017-02-13 12:38:48 +00:00
|
|
|
size_t totalSlotMem = 0;
|
2017-02-10 10:51:43 +00:00
|
|
|
if (printOutput)
|
2017-02-13 12:38:48 +00:00
|
|
|
qDebug() << "Slot map:";
|
2017-02-10 10:51:43 +00:00
|
|
|
for (uint i = 0; i < BlockAllocator::NumBins; ++i) {
|
|
|
|
uint nEntries = 0;
|
|
|
|
HeapItem *h = b->freeBins[i];
|
|
|
|
while (h) {
|
|
|
|
++nEntries;
|
2017-02-13 12:38:48 +00:00
|
|
|
totalSlotMem += h->freeData.availableSlots;
|
2017-02-10 10:51:43 +00:00
|
|
|
h = h->freeData.next;
|
|
|
|
}
|
|
|
|
if (printOutput)
|
|
|
|
qDebug() << " number of entries in slot" << i << ":" << nEntries;
|
|
|
|
}
|
2017-04-27 17:37:35 +00:00
|
|
|
SDUMP() << " large slot map";
|
|
|
|
HeapItem *h = b->freeBins[BlockAllocator::NumBins - 1];
|
|
|
|
while (h) {
|
|
|
|
SDUMP() << " " << hex << (quintptr(h)/32) << h->freeData.availableSlots;
|
|
|
|
h = h->freeData.next;
|
|
|
|
}
|
|
|
|
|
2017-02-10 10:51:43 +00:00
|
|
|
if (printOutput)
|
2017-02-13 12:38:48 +00:00
|
|
|
qDebug() << " total mem in bins" << totalSlotMem*Chunk::SlotSize;
|
|
|
|
return totalSlotMem*Chunk::SlotSize;
|
2017-02-10 10:51:43 +00:00
|
|
|
}
|
2017-01-03 10:49:15 +00:00
|
|
|
|
2017-04-04 08:35:45 +00:00
|
|
|
void MemoryManager::runGC()
|
2012-12-04 12:40:18 +00:00
|
|
|
{
|
2017-01-03 11:06:25 +00:00
|
|
|
if (gcBlocked) {
|
2012-12-04 12:40:18 +00:00
|
|
|
// qDebug() << "Not running GC.";
|
2013-01-02 15:43:47 +00:00
|
|
|
return;
|
2012-12-04 12:40:18 +00:00
|
|
|
}
|
|
|
|
|
2017-01-03 11:06:25 +00:00
|
|
|
QScopedValueRollback<bool> gcBlocker(gcBlocked, true);
|
2017-02-15 14:40:27 +00:00
|
|
|
// qDebug() << "runGC";
|
2016-07-25 07:25:11 +00:00
|
|
|
|
2017-01-03 11:06:25 +00:00
|
|
|
if (!gcStats) {
|
|
|
|
// uint oldUsed = allocator.usedMem();
|
2014-03-25 08:46:43 +00:00
|
|
|
mark();
|
|
|
|
sweep();
|
2017-01-03 11:06:25 +00:00
|
|
|
// DEBUG << "RUN GC: allocated:" << allocator.allocatedMem() << "used before" << oldUsed << "used now" << allocator.usedMem();
|
2014-03-25 08:46:43 +00:00
|
|
|
} else {
|
2017-02-10 10:51:43 +00:00
|
|
|
bool triggeredByUnmanagedHeap = (unmanagedHeapSize > unmanagedHeapSizeGCLimit);
|
|
|
|
size_t oldUnmanagedSize = unmanagedHeapSize;
|
QML: Fix MSVC 2013/64bit warnings.
compiler\qv4ssa.cpp(687) : warning C4267: 'argument' : conversion from 'size_t' to 'int', possible loss of data
compiler\qv4ssa.cpp(950) : warning C4267: 'initializing' : conversion from 'size_t' to 'int', possible loss of data
compiler\qv4ssa.cpp(1117) : warning C4267: 'return' : conversion from 'size_t' to 'unsigned int', possible loss of data
compiler\qv4ssa.cpp(1120) : warning C4267: 'return' : conversion from 'size_t' to 'unsigned int', possible loss of data
compiler\qv4ssa.cpp(1148) : warning C4267: 'initializing' : conversion from 'size_t' to 'unsigned int', possible loss of data
compiler\qv4ssa.cpp(1266) : warning C4267: 'initializing' : conversion from 'size_t' to 'unsigned int', possible loss of data
compiler\qv4ssa.cpp(1622) : warning C4267: 'initializing' : conversion from 'size_t' to 'int', possible loss of data
compiler\qv4ssa.cpp(2246) : warning C4267: 'initializing' : conversion from 'size_t' to 'unsigned int', possible loss of data
compiler\qv4ssa.cpp(4289) : warning C4267: 'initializing' : conversion from 'size_t' to 'int', possible loss of data
compiler\qv4ssa.cpp(4351) : warning C4267: 'initializing' : conversion from 'size_t' to 'unsigned int', possible loss of data
jit\qv4regalloc.cpp(1383) : warning C4267: 'argument' : conversion from 'size_t' to 'int', possible loss of data
jit\qv4regalloc.cpp(1769) : warning C4267: 'initializing' : conversion from 'size_t' to 'int', possible loss of data
jit\qv4regalloc.cpp(1814) : warning C4267: 'initializing' : conversion from 'size_t' to 'int', possible loss of data
jsruntime\qv4mm.cpp(496) : warning C4267: 'initializing' : conversion from 'size_t' to 'int', possible loss of data
jsruntime\qv4mm.cpp(503) : warning C4267: 'initializing' : conversion from 'size_t' to 'int', possible loss of data
jsruntime\qv4mm.cpp(506) : warning C4267: 'initializing' : conversion from 'size_t' to 'int', possible loss of data
jsruntime\qv4regexp.cpp(60) : warning C4267: 'return' : conversion from 'size_t' to 'uint', possible loss of data
jsruntime\qv4typedarray.cpp(85) : warning C4309: '=' : truncation of constant value
Change-Id: I0b04e1a9d379c068fb3efe90a9db8b592061e448
Reviewed-by: Erik Verbruggen <erik.verbruggen@theqtcompany.com>
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
2015-01-22 08:38:22 +00:00
|
|
|
const size_t totalMem = getAllocatedMem();
|
2017-02-10 10:51:43 +00:00
|
|
|
const size_t usedBefore = getUsedMem();
|
|
|
|
const size_t largeItemsBefore = getLargeItemsMem();
|
|
|
|
|
|
|
|
qDebug() << "========== GC ==========";
|
2017-02-13 12:38:48 +00:00
|
|
|
#ifdef MM_STATS
|
2017-02-10 10:51:43 +00:00
|
|
|
qDebug() << " Triggered by alloc request of" << lastAllocRequestedSlots << "slots.";
|
2017-02-13 12:38:48 +00:00
|
|
|
qDebug() << " Allocations since last GC" << allocationCount;
|
2017-02-15 11:23:20 +00:00
|
|
|
allocationCount = 0;
|
2017-02-10 10:51:43 +00:00
|
|
|
#endif
|
|
|
|
qDebug() << "Allocated" << totalMem << "bytes in" << blockAllocator.chunks.size() << "chunks";
|
|
|
|
qDebug() << "Fragmented memory before GC" << (totalMem - usedBefore);
|
|
|
|
dumpBins(&blockAllocator);
|
2014-03-25 08:46:43 +00:00
|
|
|
|
2017-02-13 12:38:48 +00:00
|
|
|
#ifdef MM_STATS
|
2017-02-15 14:40:27 +00:00
|
|
|
nGrayItems = 0;
|
|
|
|
#endif
|
|
|
|
|
2016-08-02 11:25:35 +00:00
|
|
|
QElapsedTimer t;
|
2014-03-25 08:46:43 +00:00
|
|
|
t.start();
|
|
|
|
mark();
|
2017-02-16 09:25:52 +00:00
|
|
|
qint64 markTime = t.nsecsElapsed()/1000;
|
|
|
|
t.restart();
|
2017-02-14 13:03:56 +00:00
|
|
|
sweep(false, increaseFreedCountForClass);
|
QML: Fix MSVC 2013/64bit warnings.
compiler\qv4ssa.cpp(687) : warning C4267: 'argument' : conversion from 'size_t' to 'int', possible loss of data
compiler\qv4ssa.cpp(950) : warning C4267: 'initializing' : conversion from 'size_t' to 'int', possible loss of data
compiler\qv4ssa.cpp(1117) : warning C4267: 'return' : conversion from 'size_t' to 'unsigned int', possible loss of data
compiler\qv4ssa.cpp(1120) : warning C4267: 'return' : conversion from 'size_t' to 'unsigned int', possible loss of data
compiler\qv4ssa.cpp(1148) : warning C4267: 'initializing' : conversion from 'size_t' to 'unsigned int', possible loss of data
compiler\qv4ssa.cpp(1266) : warning C4267: 'initializing' : conversion from 'size_t' to 'unsigned int', possible loss of data
compiler\qv4ssa.cpp(1622) : warning C4267: 'initializing' : conversion from 'size_t' to 'int', possible loss of data
compiler\qv4ssa.cpp(2246) : warning C4267: 'initializing' : conversion from 'size_t' to 'unsigned int', possible loss of data
compiler\qv4ssa.cpp(4289) : warning C4267: 'initializing' : conversion from 'size_t' to 'int', possible loss of data
compiler\qv4ssa.cpp(4351) : warning C4267: 'initializing' : conversion from 'size_t' to 'unsigned int', possible loss of data
jit\qv4regalloc.cpp(1383) : warning C4267: 'argument' : conversion from 'size_t' to 'int', possible loss of data
jit\qv4regalloc.cpp(1769) : warning C4267: 'initializing' : conversion from 'size_t' to 'int', possible loss of data
jit\qv4regalloc.cpp(1814) : warning C4267: 'initializing' : conversion from 'size_t' to 'int', possible loss of data
jsruntime\qv4mm.cpp(496) : warning C4267: 'initializing' : conversion from 'size_t' to 'int', possible loss of data
jsruntime\qv4mm.cpp(503) : warning C4267: 'initializing' : conversion from 'size_t' to 'int', possible loss of data
jsruntime\qv4mm.cpp(506) : warning C4267: 'initializing' : conversion from 'size_t' to 'int', possible loss of data
jsruntime\qv4regexp.cpp(60) : warning C4267: 'return' : conversion from 'size_t' to 'uint', possible loss of data
jsruntime\qv4typedarray.cpp(85) : warning C4309: '=' : truncation of constant value
Change-Id: I0b04e1a9d379c068fb3efe90a9db8b592061e448
Reviewed-by: Erik Verbruggen <erik.verbruggen@theqtcompany.com>
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
2015-01-22 08:38:22 +00:00
|
|
|
const size_t usedAfter = getUsedMem();
|
2015-09-17 13:29:52 +00:00
|
|
|
const size_t largeItemsAfter = getLargeItemsMem();
|
2017-02-16 09:25:52 +00:00
|
|
|
qint64 sweepTime = t.nsecsElapsed()/1000;
|
2014-03-25 08:46:43 +00:00
|
|
|
|
2017-02-10 10:51:43 +00:00
|
|
|
if (triggeredByUnmanagedHeap) {
|
|
|
|
qDebug() << "triggered by unmanaged heap:";
|
|
|
|
qDebug() << " old unmanaged heap size:" << oldUnmanagedSize;
|
|
|
|
qDebug() << " new unmanaged heap:" << unmanagedHeapSize;
|
|
|
|
qDebug() << " unmanaged heap limit:" << unmanagedHeapSizeGCLimit;
|
|
|
|
}
|
|
|
|
size_t memInBins = dumpBins(&blockAllocator);
|
2017-02-16 09:25:52 +00:00
|
|
|
qDebug() << "Marked object in" << markTime << "us.";
|
2017-02-13 12:38:48 +00:00
|
|
|
qDebug() << " " << markStackSize << "objects marked";
|
2017-02-16 09:25:52 +00:00
|
|
|
qDebug() << "Sweeped object in" << sweepTime << "us.";
|
2017-02-14 13:03:56 +00:00
|
|
|
|
|
|
|
// sort our object types by number of freed instances
|
|
|
|
MMStatsHash freedObjectStats;
|
|
|
|
std::swap(freedObjectStats, *freedObjectStatsGlobal());
|
|
|
|
typedef std::pair<const char*, int> ObjectStatInfo;
|
|
|
|
std::vector<ObjectStatInfo> freedObjectsSorted;
|
|
|
|
freedObjectsSorted.reserve(freedObjectStats.count());
|
|
|
|
for (auto it = freedObjectStats.constBegin(); it != freedObjectStats.constEnd(); ++it) {
|
|
|
|
freedObjectsSorted.push_back(std::make_pair(it.key(), it.value()));
|
|
|
|
}
|
|
|
|
std::sort(freedObjectsSorted.begin(), freedObjectsSorted.end(), [](const ObjectStatInfo &a, const ObjectStatInfo &b) {
|
|
|
|
return a.second > b.second && strcmp(a.first, b.first) < 0;
|
|
|
|
});
|
|
|
|
|
2014-03-25 08:46:43 +00:00
|
|
|
qDebug() << "Used memory before GC:" << usedBefore;
|
2017-02-13 12:38:48 +00:00
|
|
|
qDebug() << "Used memory after GC :" << usedAfter;
|
|
|
|
qDebug() << "Freed up bytes :" << (usedBefore - usedAfter);
|
2017-02-10 10:51:43 +00:00
|
|
|
size_t lost = blockAllocator.allocatedMem() - memInBins - usedAfter;
|
|
|
|
if (lost)
|
|
|
|
qDebug() << "!!!!!!!!!!!!!!!!!!!!! LOST MEM:" << lost << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
|
|
|
|
if (largeItemsBefore || largeItemsAfter) {
|
|
|
|
qDebug() << "Large item memory before GC:" << largeItemsBefore;
|
|
|
|
qDebug() << "Large item memory after GC:" << largeItemsAfter;
|
|
|
|
qDebug() << "Large item memory freed up:" << (largeItemsBefore - largeItemsAfter);
|
|
|
|
}
|
2017-02-14 13:03:56 +00:00
|
|
|
|
|
|
|
for (auto it = freedObjectsSorted.cbegin(); it != freedObjectsSorted.cend(); ++it) {
|
|
|
|
qDebug().noquote() << QString::fromLatin1("Freed JS type: %1 (%2 instances)").arg(QString::fromLatin1(it->first), QString::number(it->second));
|
|
|
|
}
|
|
|
|
|
2014-03-25 08:46:43 +00:00
|
|
|
qDebug() << "======== End GC ========";
|
|
|
|
}
|
2017-02-10 10:51:43 +00:00
|
|
|
|
|
|
|
if (aggressiveGC) {
|
|
|
|
// ensure we don't 'loose' any memory
|
|
|
|
Q_ASSERT(blockAllocator.allocatedMem() == getUsedMem() + dumpBins(&blockAllocator, false));
|
|
|
|
}
|
2017-02-15 14:40:27 +00:00
|
|
|
|
2017-04-04 08:35:45 +00:00
|
|
|
usedSlotsAfterLastFullSweep = blockAllocator.usedSlotsAfterLastSweep;
|
2017-02-16 09:52:48 +00:00
|
|
|
|
2017-04-04 08:35:45 +00:00
|
|
|
// reset all black bits
|
|
|
|
blockAllocator.resetBlackBits();
|
|
|
|
hugeItemAllocator.resetBlackBits();
|
2012-12-04 12:40:18 +00:00
|
|
|
}
|
|
|
|
|
2014-06-12 12:33:05 +00:00
|
|
|
size_t MemoryManager::getUsedMem() const
|
2014-03-25 08:46:43 +00:00
|
|
|
{
|
2017-01-03 10:49:15 +00:00
|
|
|
return blockAllocator.usedMem();
|
2014-03-25 08:46:43 +00:00
|
|
|
}
|
|
|
|
|
2014-06-12 12:33:05 +00:00
|
|
|
size_t MemoryManager::getAllocatedMem() const
|
|
|
|
{
|
2017-01-03 10:49:15 +00:00
|
|
|
return blockAllocator.allocatedMem() + hugeItemAllocator.usedMem();
|
2014-06-12 12:33:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t MemoryManager::getLargeItemsMem() const
|
|
|
|
{
|
2017-01-20 10:36:16 +00:00
|
|
|
return hugeItemAllocator.usedMem();
|
2014-06-12 12:33:05 +00:00
|
|
|
}
|
|
|
|
|
2012-12-04 12:40:18 +00:00
|
|
|
MemoryManager::~MemoryManager()
|
|
|
|
{
|
2015-01-12 20:55:51 +00:00
|
|
|
delete m_persistentValues;
|
2013-04-16 09:36:56 +00:00
|
|
|
|
2013-06-13 13:27:00 +00:00
|
|
|
sweep(/*lastSweep*/true);
|
2017-01-20 10:36:16 +00:00
|
|
|
blockAllocator.freeAll();
|
|
|
|
hugeItemAllocator.freeAll();
|
2016-12-22 20:24:27 +00:00
|
|
|
stackAllocator.freeAll();
|
2015-08-07 12:26:43 +00:00
|
|
|
|
|
|
|
delete m_weakValues;
|
2013-06-01 12:27:45 +00:00
|
|
|
#ifdef V4_USE_VALGRIND
|
2014-12-12 15:38:09 +00:00
|
|
|
VALGRIND_DESTROY_MEMPOOL(this);
|
2013-06-01 12:27:45 +00:00
|
|
|
#endif
|
2016-12-22 14:20:05 +00:00
|
|
|
delete chunkAllocator;
|
2012-12-04 12:40:18 +00:00
|
|
|
}
|
|
|
|
|
2015-08-28 11:48:52 +00:00
|
|
|
|
2012-12-04 12:40:18 +00:00
|
|
|
void MemoryManager::dumpStats() const
|
|
|
|
{
|
2013-01-25 18:32:30 +00:00
|
|
|
#ifdef DETAILED_MM_STATS
|
2012-12-04 12:40:18 +00:00
|
|
|
std::cerr << "=================" << std::endl;
|
|
|
|
std::cerr << "Allocation stats:" << std::endl;
|
|
|
|
std::cerr << "Requests for each chunk size:" << std::endl;
|
2017-01-03 11:06:25 +00:00
|
|
|
for (int i = 0; i < allocSizeCounters.size(); ++i) {
|
|
|
|
if (unsigned count = allocSizeCounters[i]) {
|
2012-12-04 12:40:18 +00:00
|
|
|
std::cerr << "\t" << (i << 4) << " bytes chunks: " << count << std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // DETAILED_MM_STATS
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DETAILED_MM_STATS
|
|
|
|
void MemoryManager::willAllocate(std::size_t size)
|
|
|
|
{
|
|
|
|
unsigned alignedSize = (size + 15) >> 4;
|
2017-01-03 11:06:25 +00:00
|
|
|
QVector<unsigned> &counters = allocSizeCounters;
|
2012-12-04 12:40:18 +00:00
|
|
|
if ((unsigned) counters.size() < alignedSize + 1)
|
|
|
|
counters.resize(alignedSize + 1);
|
|
|
|
counters[alignedSize]++;
|
|
|
|
}
|
2012-12-13 22:46:51 +00:00
|
|
|
|
2012-12-04 12:40:18 +00:00
|
|
|
#endif // DETAILED_MM_STATS
|
|
|
|
|
2017-03-09 09:36:16 +00:00
|
|
|
void MemoryManager::collectFromJSStack(MarkStack *markStack) const
|
2013-09-03 10:40:07 +00:00
|
|
|
{
|
2015-08-28 11:48:52 +00:00
|
|
|
Value *v = engine->jsStackBase;
|
|
|
|
Value *top = engine->jsStackTop;
|
2013-09-03 10:40:07 +00:00
|
|
|
while (v < top) {
|
2016-05-26 15:46:24 +00:00
|
|
|
Managed *m = v->managed();
|
2014-04-05 18:23:20 +00:00
|
|
|
if (m && m->inUse())
|
2013-09-03 10:40:07 +00:00
|
|
|
// Skip pointers to already freed objects, they are bogus as well
|
2017-03-09 09:36:16 +00:00
|
|
|
m->mark(markStack);
|
2013-09-03 10:40:07 +00:00
|
|
|
++v;
|
|
|
|
}
|
|
|
|
}
|
2016-12-22 14:20:05 +00:00
|
|
|
|
|
|
|
} // namespace QV4
|
|
|
|
|
2013-06-24 13:28:00 +00:00
|
|
|
QT_END_NAMESPACE
|