2012-12-04 12:40:18 +00:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
2015-01-28 11:55:39 +00:00
|
|
|
** Copyright (C) 2015 The Qt Company Ltd.
|
|
|
|
** Contact: http://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
|
|
|
**
|
2014-08-22 06:13:59 +00:00
|
|
|
** $QT_BEGIN_LICENSE:LGPL21$
|
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
|
|
|
|
** and conditions see http://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at http://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
|
2014-08-22 06:13:59 +00:00
|
|
|
** General Public License version 2.1 or version 3 as published by the Free
|
|
|
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
|
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
|
|
** following information to ensure the GNU Lesser General Public License
|
|
|
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
2012-12-04 12:40:18 +00:00
|
|
|
**
|
2015-01-28 11:55:39 +00:00
|
|
|
** As a special exception, The Qt Company gives you certain additional
|
|
|
|
** rights. These rights are described in The Qt Company LGPL Exception
|
2012-12-04 12:40:18 +00:00
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
**
|
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"
|
2013-05-24 11:19:15 +00:00
|
|
|
#include <qqmlengine.h>
|
2012-12-18 14:03:26 +00:00
|
|
|
#include "PageAllocation.h"
|
|
|
|
#include "StdLibExtras.h"
|
2012-12-04 12:40:18 +00:00
|
|
|
|
|
|
|
#include <QTime>
|
2012-12-13 22:46:51 +00:00
|
|
|
#include <QMap>
|
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
|
|
|
|
2013-03-12 18:49:13 +00:00
|
|
|
#ifdef V4_USE_VALGRIND
|
|
|
|
#include <valgrind/valgrind.h>
|
|
|
|
#include <valgrind/memcheck.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
|
|
|
|
|
2015-09-08 13:24:00 +00:00
|
|
|
#define MIN_UNMANAGED_HEAPSIZE_GC_LIMIT (std::size_t)128*1024
|
|
|
|
|
2015-01-09 17:52:56 +00:00
|
|
|
using namespace WTF;
|
|
|
|
|
2013-06-24 13:28:00 +00:00
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
2015-10-22 10:35:08 +00:00
|
|
|
static uint maxShiftValue()
|
|
|
|
{
|
|
|
|
static uint result = 0;
|
|
|
|
if (!result) {
|
|
|
|
result = 6;
|
|
|
|
if (Q_UNLIKELY(qEnvironmentVariableIsSet("QV4_MM_MAXBLOCK_SHIFT"))) {
|
|
|
|
bool ok;
|
|
|
|
const uint overrideValue = qgetenv("QV4_MM_MAXBLOCK_SHIFT").toUInt(&ok);
|
|
|
|
if (ok && overrideValue <= 11 && overrideValue > 0)
|
|
|
|
result = overrideValue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::size_t maxChunkSizeValue()
|
|
|
|
{
|
|
|
|
static std::size_t result = 0;
|
|
|
|
if (!result) {
|
|
|
|
result = 32 * 1024;
|
|
|
|
if (Q_UNLIKELY(qEnvironmentVariableIsSet("QV4_MM_MAX_CHUNK_SIZE"))) {
|
|
|
|
bool ok;
|
|
|
|
const std::size_t overrideValue = qgetenv("QV4_MM_MAX_CHUNK_SIZE").toUInt(&ok);
|
|
|
|
if (ok)
|
|
|
|
result = overrideValue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2013-04-19 11:03:42 +00:00
|
|
|
using namespace QV4;
|
2012-12-04 12:40:18 +00:00
|
|
|
|
|
|
|
struct MemoryManager::Data
|
|
|
|
{
|
2015-01-09 17:52:56 +00:00
|
|
|
struct ChunkHeader {
|
|
|
|
Heap::Base freeItems;
|
|
|
|
ChunkHeader *nextNonFull;
|
|
|
|
char *itemStart;
|
|
|
|
char *itemEnd;
|
|
|
|
int itemSize;
|
|
|
|
};
|
|
|
|
|
2012-12-04 12:40:18 +00:00
|
|
|
bool gcBlocked;
|
|
|
|
bool aggressiveGC;
|
2014-03-25 08:46:43 +00:00
|
|
|
bool gcStats;
|
2012-12-04 12:40:18 +00:00
|
|
|
ExecutionEngine *engine;
|
|
|
|
|
2013-04-15 10:40:04 +00:00
|
|
|
enum { MaxItemSize = 512 };
|
2015-01-09 17:52:56 +00:00
|
|
|
ChunkHeader *nonFullChunks[MaxItemSize/16];
|
2013-01-30 07:58:27 +00:00
|
|
|
uint nChunks[MaxItemSize/16];
|
2013-03-01 12:34:52 +00:00
|
|
|
uint availableItems[MaxItemSize/16];
|
|
|
|
uint allocCount[MaxItemSize/16];
|
2013-11-02 17:39:55 +00:00
|
|
|
int totalItems;
|
|
|
|
int totalAlloc;
|
2014-02-20 16:43:09 +00:00
|
|
|
uint maxShift;
|
2014-03-25 15:34:52 +00:00
|
|
|
std::size_t maxChunkSize;
|
2015-01-09 17:52:56 +00:00
|
|
|
QVector<PageAllocation> heapChunks;
|
2015-07-03 11:20:18 +00:00
|
|
|
std::size_t unmanagedHeapSize; // 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;
|
2013-11-14 11:05:42 +00:00
|
|
|
|
|
|
|
struct LargeItem {
|
|
|
|
LargeItem *next;
|
2014-06-02 16:33:19 +00:00
|
|
|
size_t size;
|
2013-11-14 11:05:42 +00:00
|
|
|
void *data;
|
|
|
|
|
2014-11-13 20:53:27 +00:00
|
|
|
Heap::Base *heapObject() {
|
|
|
|
return reinterpret_cast<Heap::Base *>(&data);
|
2013-11-14 11:05:42 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
LargeItem *largeItems;
|
2014-10-27 12:51:27 +00:00
|
|
|
std::size_t totalLargeItemsAllocated;
|
2013-11-14 11:05:42 +00:00
|
|
|
|
2012-12-04 12:40:18 +00:00
|
|
|
// statistics:
|
|
|
|
#ifdef DETAILED_MM_STATS
|
|
|
|
QVector<unsigned> allocSizeCounters;
|
|
|
|
#endif // DETAILED_MM_STATS
|
|
|
|
|
2014-03-25 08:44:52 +00:00
|
|
|
Data()
|
|
|
|
: gcBlocked(false)
|
2015-10-22 10:35:08 +00:00
|
|
|
, aggressiveGC(!qEnvironmentVariableIsEmpty("QV4_MM_AGGRESSIVE_GC"))
|
|
|
|
, gcStats(!qEnvironmentVariableIsEmpty("QV4_MM_STATS"))
|
2012-12-04 12:40:18 +00:00
|
|
|
, engine(0)
|
2013-11-06 15:04:10 +00:00
|
|
|
, totalItems(0)
|
|
|
|
, totalAlloc(0)
|
2015-10-22 10:35:08 +00:00
|
|
|
, maxShift(maxShiftValue())
|
|
|
|
, maxChunkSize(maxChunkSizeValue())
|
2015-07-03 11:20:18 +00:00
|
|
|
, unmanagedHeapSize(0)
|
2015-09-08 13:24:00 +00:00
|
|
|
, unmanagedHeapSizeGCLimit(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT)
|
2013-11-14 11:05:42 +00:00
|
|
|
, largeItems(0)
|
2014-10-27 12:51:27 +00:00
|
|
|
, totalLargeItemsAllocated(0)
|
2012-12-04 12:40:18 +00:00
|
|
|
{
|
2015-01-09 17:52:56 +00:00
|
|
|
memset(nonFullChunks, 0, sizeof(nonFullChunks));
|
2013-01-30 07:58:27 +00:00
|
|
|
memset(nChunks, 0, sizeof(nChunks));
|
2013-03-01 12:34:52 +00:00
|
|
|
memset(availableItems, 0, sizeof(availableItems));
|
|
|
|
memset(allocCount, 0, sizeof(allocCount));
|
2012-12-04 12:40:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
~Data()
|
|
|
|
{
|
2015-01-09 17:52:56 +00:00
|
|
|
for (QVector<PageAllocation>::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) {
|
|
|
|
Q_V4_PROFILE_DEALLOC(engine, 0, i->size(), Profiling::HeapPage);
|
|
|
|
i->deallocate();
|
2014-06-02 16:33:19 +00:00
|
|
|
}
|
2012-12-04 12:40:18 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-12-03 16:25:11 +00:00
|
|
|
namespace {
|
|
|
|
|
2015-07-03 11:20:18 +00:00
|
|
|
bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, ExecutionEngine *engine, std::size_t *unmanagedHeapSize)
|
2014-12-03 16:25:11 +00:00
|
|
|
{
|
2015-07-03 11:20:18 +00:00
|
|
|
Q_ASSERT(unmanagedHeapSize);
|
|
|
|
|
2015-01-09 17:52:56 +00:00
|
|
|
bool isEmpty = true;
|
|
|
|
Heap::Base *tail = &header->freeItems;
|
|
|
|
// qDebug("chunkStart @ %p, size=%x, pos=%x", header->itemStart, header->itemSize, header->itemSize>>4);
|
2014-12-03 16:25:11 +00:00
|
|
|
#ifdef V4_USE_VALGRIND
|
|
|
|
VALGRIND_DISABLE_ERROR_REPORTING;
|
|
|
|
#endif
|
2015-01-09 17:52:56 +00:00
|
|
|
for (char *item = header->itemStart; item <= header->itemEnd; item += header->itemSize) {
|
2014-12-03 16:25:11 +00:00
|
|
|
Heap::Base *m = reinterpret_cast<Heap::Base *>(item);
|
2015-07-03 11:20:18 +00:00
|
|
|
// qDebug("chunk @ %p, in use: %s, mark bit: %s",
|
|
|
|
// item, (m->inUse() ? "yes" : "no"), (m->isMarked() ? "true" : "false"));
|
2014-12-03 16:25:11 +00:00
|
|
|
|
|
|
|
Q_ASSERT((qintptr) item % 16 == 0);
|
|
|
|
|
2015-01-09 21:02:40 +00:00
|
|
|
if (m->isMarked()) {
|
2015-01-09 18:48:56 +00:00
|
|
|
Q_ASSERT(m->inUse());
|
2015-01-09 21:02:40 +00:00
|
|
|
m->clearMarkBit();
|
2015-01-09 17:52:56 +00:00
|
|
|
isEmpty = false;
|
2014-12-03 16:25:11 +00:00
|
|
|
++(*itemsInUse);
|
|
|
|
} else {
|
2015-01-09 18:48:56 +00:00
|
|
|
if (m->inUse()) {
|
2015-01-09 17:52:56 +00:00
|
|
|
// qDebug() << "-- collecting it." << m << tail << m->nextFree();
|
2014-12-03 16:25:11 +00:00
|
|
|
#ifdef V4_USE_VALGRIND
|
|
|
|
VALGRIND_ENABLE_ERROR_REPORTING;
|
|
|
|
#endif
|
2015-08-18 08:29:10 +00:00
|
|
|
if (std::size_t(header->itemSize) == MemoryManager::align(sizeof(Heap::String)) && m->vtable()->isString) {
|
2015-07-03 11:20:18 +00:00
|
|
|
std::size_t heapBytes = static_cast<Heap::String *>(m)->retainedTextSize();
|
|
|
|
Q_ASSERT(*unmanagedHeapSize >= heapBytes);
|
|
|
|
// qDebug() << "-- it's a string holding on to" << heapBytes << "bytes";
|
|
|
|
*unmanagedHeapSize -= heapBytes;
|
|
|
|
}
|
|
|
|
|
2015-08-07 11:56:31 +00:00
|
|
|
if (m->vtable()->destroy)
|
|
|
|
m->vtable()->destroy(m);
|
2014-12-03 16:25:11 +00:00
|
|
|
|
2015-01-09 17:52:56 +00:00
|
|
|
memset(m, 0, header->itemSize);
|
2014-12-03 16:25:11 +00:00
|
|
|
#ifdef V4_USE_VALGRIND
|
|
|
|
VALGRIND_DISABLE_ERROR_REPORTING;
|
2014-12-12 15:38:09 +00:00
|
|
|
VALGRIND_MEMPOOL_FREE(engine->memoryManager, m);
|
2014-12-03 16:25:11 +00:00
|
|
|
#endif
|
2015-01-09 17:52:56 +00:00
|
|
|
Q_V4_PROFILE_DEALLOC(engine, m, header->itemSize, Profiling::SmallItem);
|
2014-12-03 16:25:11 +00:00
|
|
|
++(*itemsInUse);
|
|
|
|
}
|
|
|
|
// Relink all free blocks to rewrite references to any released chunk.
|
2015-01-09 17:52:56 +00:00
|
|
|
tail->setNextFree(m);
|
|
|
|
tail = m;
|
2014-12-03 16:25:11 +00:00
|
|
|
}
|
|
|
|
}
|
2015-01-09 17:52:56 +00:00
|
|
|
tail->setNextFree(0);
|
2014-12-03 16:25:11 +00:00
|
|
|
#ifdef V4_USE_VALGRIND
|
|
|
|
VALGRIND_ENABLE_ERROR_REPORTING;
|
|
|
|
#endif
|
2015-01-09 17:52:56 +00:00
|
|
|
return isEmpty;
|
2014-12-03 16:25:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2015-01-12 20:55:51 +00:00
|
|
|
MemoryManager::MemoryManager(ExecutionEngine *engine)
|
2015-08-28 11:48:52 +00:00
|
|
|
: engine(engine)
|
|
|
|
, m_d(new Data)
|
2015-01-12 20:55:51 +00:00
|
|
|
, m_persistentValues(new PersistentValueStorage(engine))
|
|
|
|
, m_weakValues(new PersistentValueStorage(engine))
|
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
|
2015-01-12 20:55:51 +00:00
|
|
|
m_d->engine = engine;
|
2012-12-04 12:40:18 +00:00
|
|
|
}
|
|
|
|
|
2015-07-03 11:20:18 +00:00
|
|
|
Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize)
|
2012-12-04 12:40:18 +00:00
|
|
|
{
|
|
|
|
if (m_d->aggressiveGC)
|
|
|
|
runGC();
|
|
|
|
#ifdef DETAILED_MM_STATS
|
|
|
|
willAllocate(size);
|
|
|
|
#endif // DETAILED_MM_STATS
|
|
|
|
|
2013-11-02 17:39:55 +00:00
|
|
|
Q_ASSERT(size >= 16);
|
|
|
|
Q_ASSERT(size % 16 == 0);
|
2012-12-04 12:40:18 +00:00
|
|
|
|
2015-07-03 11:20:18 +00:00
|
|
|
// qDebug() << "unmanagedHeapSize:" << m_d->unmanagedHeapSize << "limit:" << m_d->unmanagedHeapSizeGCLimit << "unmanagedSize:" << unmanagedSize;
|
|
|
|
m_d->unmanagedHeapSize += unmanagedSize;
|
|
|
|
bool didGCRun = false;
|
|
|
|
if (m_d->unmanagedHeapSize > m_d->unmanagedHeapSizeGCLimit) {
|
|
|
|
runGC();
|
|
|
|
|
2015-09-08 13:24:00 +00:00
|
|
|
if (3*m_d->unmanagedHeapSizeGCLimit <= 4*m_d->unmanagedHeapSize)
|
|
|
|
// more than 75% full, raise limit
|
2015-07-03 11:20:18 +00:00
|
|
|
m_d->unmanagedHeapSizeGCLimit = std::max(m_d->unmanagedHeapSizeGCLimit, m_d->unmanagedHeapSize) * 2;
|
|
|
|
else if (m_d->unmanagedHeapSize * 4 <= m_d->unmanagedHeapSizeGCLimit)
|
2015-09-08 13:24:00 +00:00
|
|
|
// less than 25% full, lower limit
|
|
|
|
m_d->unmanagedHeapSizeGCLimit = qMax(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT, m_d->unmanagedHeapSizeGCLimit/2);
|
2015-07-03 11:20:18 +00:00
|
|
|
didGCRun = true;
|
|
|
|
}
|
|
|
|
|
2012-12-13 22:46:51 +00:00
|
|
|
size_t pos = size >> 4;
|
2012-12-04 12:40:18 +00:00
|
|
|
|
2013-11-14 11:05:42 +00:00
|
|
|
// doesn't fit into a small bucket
|
|
|
|
if (size >= MemoryManager::Data::MaxItemSize) {
|
2015-07-03 11:20:18 +00:00
|
|
|
if (!didGCRun && m_d->totalLargeItemsAllocated > 8 * 1024 * 1024)
|
2014-10-27 12:51:27 +00:00
|
|
|
runGC();
|
|
|
|
|
2013-11-14 11:05:42 +00:00
|
|
|
// we use malloc for this
|
2014-06-02 16:33:19 +00:00
|
|
|
MemoryManager::Data::LargeItem *item = static_cast<MemoryManager::Data::LargeItem *>(
|
2015-08-28 11:48:52 +00:00
|
|
|
malloc(Q_V4_PROFILE_ALLOC(engine, size + sizeof(MemoryManager::Data::LargeItem),
|
2014-06-02 16:33:19 +00:00
|
|
|
Profiling::LargeItem)));
|
2013-12-05 13:44:46 +00:00
|
|
|
memset(item, 0, size + sizeof(MemoryManager::Data::LargeItem));
|
2013-11-14 11:05:42 +00:00
|
|
|
item->next = m_d->largeItems;
|
2014-06-02 16:33:19 +00:00
|
|
|
item->size = size;
|
2013-11-14 11:05:42 +00:00
|
|
|
m_d->largeItems = item;
|
2014-10-27 12:51:27 +00:00
|
|
|
m_d->totalLargeItemsAllocated += size;
|
2014-11-13 20:53:27 +00:00
|
|
|
return item->heapObject();
|
2013-11-14 11:05:42 +00:00
|
|
|
}
|
2012-12-04 12:40:18 +00:00
|
|
|
|
2015-01-09 17:52:56 +00:00
|
|
|
Heap::Base *m = 0;
|
|
|
|
Data::ChunkHeader *header = m_d->nonFullChunks[pos];
|
|
|
|
if (header) {
|
|
|
|
m = header->freeItems.nextFree();
|
2013-01-02 15:43:47 +00:00
|
|
|
goto found;
|
2015-01-09 17:52:56 +00:00
|
|
|
}
|
2012-12-13 22:46:51 +00:00
|
|
|
|
2013-01-02 15:43:47 +00:00
|
|
|
// try to free up space, otherwise allocate
|
2015-07-03 11:20:18 +00:00
|
|
|
if (!didGCRun && m_d->allocCount[pos] > (m_d->availableItems[pos] >> 1) && m_d->totalAlloc > (m_d->totalItems >> 1) && !m_d->aggressiveGC) {
|
2013-01-02 21:09:31 +00:00
|
|
|
runGC();
|
2015-01-09 17:52:56 +00:00
|
|
|
header = m_d->nonFullChunks[pos];
|
|
|
|
if (header) {
|
|
|
|
m = header->freeItems.nextFree();
|
2013-01-14 15:15:01 +00:00
|
|
|
goto found;
|
2015-01-09 17:52:56 +00:00
|
|
|
}
|
2013-01-14 15:15:01 +00:00
|
|
|
}
|
2012-12-13 22:46:51 +00:00
|
|
|
|
2013-01-02 15:43:47 +00:00
|
|
|
// no free item available, allocate a new chunk
|
|
|
|
{
|
2014-03-25 09:08:01 +00:00
|
|
|
// allocate larger chunks at a time to avoid excessive GC, but cap at maximum chunk size (2MB by default)
|
2013-01-30 07:58:27 +00:00
|
|
|
uint shift = ++m_d->nChunks[pos];
|
2014-02-20 16:43:09 +00:00
|
|
|
if (shift > m_d->maxShift)
|
|
|
|
shift = m_d->maxShift;
|
2014-03-25 15:34:52 +00:00
|
|
|
std::size_t allocSize = m_d->maxChunkSize*(size_t(1) << shift);
|
2013-01-02 15:43:47 +00:00
|
|
|
allocSize = roundUpToMultipleOf(WTF::pageSize(), allocSize);
|
2015-01-09 17:52:56 +00:00
|
|
|
PageAllocation allocation = PageAllocation::allocate(
|
2015-08-28 11:48:52 +00:00
|
|
|
Q_V4_PROFILE_ALLOC(engine, allocSize, Profiling::HeapPage),
|
2014-06-02 16:33:19 +00:00
|
|
|
OSAllocator::JSGCHeapPages);
|
2013-01-02 15:43:47 +00:00
|
|
|
m_d->heapChunks.append(allocation);
|
2014-04-03 10:35:26 +00:00
|
|
|
|
2015-01-09 17:52:56 +00:00
|
|
|
header = reinterpret_cast<Data::ChunkHeader *>(allocation.base());
|
|
|
|
header->itemSize = int(size);
|
|
|
|
header->itemStart = reinterpret_cast<char *>(allocation.base()) + roundUpToMultipleOf(16, sizeof(Data::ChunkHeader));
|
|
|
|
header->itemEnd = reinterpret_cast<char *>(allocation.base()) + allocation.size() - header->itemSize;
|
|
|
|
|
|
|
|
header->nextNonFull = m_d->nonFullChunks[pos];
|
|
|
|
m_d->nonFullChunks[pos] = header;
|
|
|
|
|
|
|
|
Heap::Base *last = &header->freeItems;
|
|
|
|
for (char *item = header->itemStart; item <= header->itemEnd; item += header->itemSize) {
|
|
|
|
Heap::Base *o = reinterpret_cast<Heap::Base *>(item);
|
2015-01-09 18:48:56 +00:00
|
|
|
last->setNextFree(o);
|
|
|
|
last = o;
|
2015-01-09 17:52:56 +00:00
|
|
|
|
2013-01-02 15:43:47 +00:00
|
|
|
}
|
2015-01-09 18:48:56 +00:00
|
|
|
last->setNextFree(0);
|
2015-01-09 17:52:56 +00:00
|
|
|
m = header->freeItems.nextFree();
|
|
|
|
const size_t increase = (header->itemEnd - header->itemStart) / header->itemSize;
|
2013-11-25 13:08:19 +00:00
|
|
|
m_d->availableItems[pos] += uint(increase);
|
|
|
|
m_d->totalItems += int(increase);
|
2013-03-12 18:49:13 +00:00
|
|
|
#ifdef V4_USE_VALGRIND
|
2015-01-09 17:52:56 +00:00
|
|
|
VALGRIND_MAKE_MEM_NOACCESS(allocation.base(), allocSize);
|
|
|
|
VALGRIND_MEMPOOL_ALLOC(this, header, sizeof(Data::ChunkHeader));
|
2013-03-12 18:49:13 +00:00
|
|
|
#endif
|
2012-12-04 12:40:18 +00:00
|
|
|
}
|
|
|
|
|
2013-01-02 15:43:47 +00:00
|
|
|
found:
|
2013-03-12 18:49:13 +00:00
|
|
|
#ifdef V4_USE_VALGRIND
|
2014-12-12 15:38:09 +00:00
|
|
|
VALGRIND_MEMPOOL_ALLOC(this, m, size);
|
2013-03-12 18:49:13 +00:00
|
|
|
#endif
|
2015-08-28 11:48:52 +00:00
|
|
|
Q_V4_PROFILE_ALLOC(engine, size, Profiling::SmallItem);
|
2013-03-12 18:49:13 +00:00
|
|
|
|
2013-11-02 17:39:55 +00:00
|
|
|
++m_d->allocCount[pos];
|
|
|
|
++m_d->totalAlloc;
|
2015-01-09 17:52:56 +00:00
|
|
|
header->freeItems.setNextFree(m->nextFree());
|
|
|
|
if (!header->freeItems.nextFree())
|
|
|
|
m_d->nonFullChunks[pos] = header->nextNonFull;
|
2012-12-04 12:40:18 +00:00
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2014-10-21 09:05:00 +00:00
|
|
|
static void drainMarkStack(QV4::ExecutionEngine *engine, Value *markBase)
|
|
|
|
{
|
|
|
|
while (engine->jsStackTop > markBase) {
|
2014-11-01 22:04:20 +00:00
|
|
|
Heap::Base *h = engine->popForGC();
|
2015-08-07 11:56:31 +00:00
|
|
|
Q_ASSERT (h->vtable()->markObjects);
|
|
|
|
h->vtable()->markObjects(h, engine);
|
2014-10-21 09:05:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-28 15:46:09 +00:00
|
|
|
void MemoryManager::mark()
|
2012-12-04 12:40:18 +00:00
|
|
|
{
|
2015-08-28 11:48:52 +00:00
|
|
|
Value *markBase = engine->jsStackTop;
|
2013-01-28 15:46:09 +00:00
|
|
|
|
2015-08-28 11:48:52 +00:00
|
|
|
engine->markObjects();
|
2013-01-28 15:46:09 +00:00
|
|
|
|
2013-10-15 14:00:49 +00:00
|
|
|
collectFromJSStack();
|
|
|
|
|
2015-09-08 12:58:55 +00:00
|
|
|
m_persistentValues->mark(engine);
|
|
|
|
|
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) {
|
|
|
|
if (!(*it).isManaged())
|
2013-05-24 11:19:15 +00:00
|
|
|
continue;
|
2015-08-14 23:31:13 +00:00
|
|
|
if (!(*it).as<QObjectWrapper>())
|
2015-01-09 21:02:40 +00:00
|
|
|
continue;
|
2015-01-12 20:55:51 +00:00
|
|
|
QObjectWrapper *qobjectWrapper = static_cast<QObjectWrapper*>((*it).managed());
|
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)
|
2015-08-28 11:48:52 +00:00
|
|
|
qobjectWrapper->mark(engine);
|
2013-11-02 15:30:26 +00:00
|
|
|
|
2015-08-28 11:48:52 +00:00
|
|
|
if (engine->jsStackTop >= engine->jsStackLimit)
|
|
|
|
drainMarkStack(engine, markBase);
|
2013-05-24 11:19:15 +00:00
|
|
|
}
|
2014-10-21 09:05:00 +00:00
|
|
|
|
2015-08-28 11:48:52 +00:00
|
|
|
drainMarkStack(engine, markBase);
|
2012-12-04 12:40:18 +00:00
|
|
|
}
|
|
|
|
|
2013-10-15 20:27:10 +00:00
|
|
|
void MemoryManager::sweep(bool lastSweep)
|
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) {
|
|
|
|
if (!(*it).isManaged())
|
|
|
|
continue;
|
|
|
|
Managed *m = (*it).as<Managed>();
|
|
|
|
if (m->markBit())
|
|
|
|
continue;
|
|
|
|
// we need to call detroyObject on qobjectwrappers now, so that they can emit the destroyed
|
|
|
|
// 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
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
if (v->tag() == Value::Undefined_Type)
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-09 17:52:56 +00:00
|
|
|
bool *chunkIsEmpty = (bool *)alloca(m_d->heapChunks.size() * sizeof(bool));
|
2014-12-03 16:25:11 +00:00
|
|
|
uint itemsInUse[MemoryManager::Data::MaxItemSize/16];
|
|
|
|
memset(itemsInUse, 0, sizeof(itemsInUse));
|
2015-01-09 17:52:56 +00:00
|
|
|
memset(m_d->nonFullChunks, 0, sizeof(m_d->nonFullChunks));
|
2014-12-03 16:25:11 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < m_d->heapChunks.size(); ++i) {
|
2015-01-09 17:52:56 +00:00
|
|
|
Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(m_d->heapChunks[i].base());
|
2015-08-28 11:48:52 +00:00
|
|
|
chunkIsEmpty[i] = sweepChunk(header, &itemsInUse[header->itemSize >> 4], engine, &m_d->unmanagedHeapSize);
|
2014-12-03 16:25:11 +00:00
|
|
|
}
|
|
|
|
|
2015-01-09 17:52:56 +00:00
|
|
|
QVector<PageAllocation>::iterator chunkIter = m_d->heapChunks.begin();
|
|
|
|
for (int i = 0; i < m_d->heapChunks.size(); ++i) {
|
2014-12-03 16:25:11 +00:00
|
|
|
Q_ASSERT(chunkIter != m_d->heapChunks.end());
|
2015-01-09 17:52:56 +00:00
|
|
|
Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(chunkIter->base());
|
|
|
|
const size_t pos = header->itemSize >> 4;
|
|
|
|
const size_t decrease = (header->itemEnd - header->itemStart) / header->itemSize;
|
2014-12-03 16:25:11 +00:00
|
|
|
|
|
|
|
// Release that chunk if it could have been spared since the last GC run without any difference.
|
2015-01-09 17:52:56 +00:00
|
|
|
if (chunkIsEmpty[i] && m_d->availableItems[pos] - decrease >= itemsInUse[pos]) {
|
2015-08-28 11:48:52 +00:00
|
|
|
Q_V4_PROFILE_DEALLOC(engine, 0, chunkIter->size(), Profiling::HeapPage);
|
2015-01-09 17:52:56 +00:00
|
|
|
#ifdef V4_USE_VALGRIND
|
|
|
|
VALGRIND_MEMPOOL_FREE(this, header);
|
|
|
|
#endif
|
2014-12-03 16:25:11 +00:00
|
|
|
--m_d->nChunks[pos];
|
|
|
|
m_d->availableItems[pos] -= uint(decrease);
|
|
|
|
m_d->totalItems -= int(decrease);
|
2015-01-09 17:52:56 +00:00
|
|
|
chunkIter->deallocate();
|
2014-12-03 16:25:11 +00:00
|
|
|
chunkIter = m_d->heapChunks.erase(chunkIter);
|
|
|
|
continue;
|
2015-01-09 17:52:56 +00:00
|
|
|
} else if (header->freeItems.nextFree()) {
|
|
|
|
header->nextNonFull = m_d->nonFullChunks[pos];
|
|
|
|
m_d->nonFullChunks[pos] = header;
|
2014-12-03 16:25:11 +00:00
|
|
|
}
|
|
|
|
++chunkIter;
|
|
|
|
}
|
2014-12-12 14:29:22 +00:00
|
|
|
|
2013-11-14 11:05:42 +00:00
|
|
|
Data::LargeItem *i = m_d->largeItems;
|
|
|
|
Data::LargeItem **last = &m_d->largeItems;
|
|
|
|
while (i) {
|
2014-11-13 20:53:27 +00:00
|
|
|
Heap::Base *m = i->heapObject();
|
2015-01-09 18:48:56 +00:00
|
|
|
Q_ASSERT(m->inUse());
|
2015-01-09 21:02:40 +00:00
|
|
|
if (m->isMarked()) {
|
|
|
|
m->clearMarkBit();
|
2013-11-14 11:05:42 +00:00
|
|
|
last = &i->next;
|
|
|
|
i = i->next;
|
|
|
|
continue;
|
|
|
|
}
|
2015-08-07 11:56:31 +00:00
|
|
|
if (m->vtable()->destroy)
|
|
|
|
m->vtable()->destroy(m);
|
2013-11-14 11:05:42 +00:00
|
|
|
|
|
|
|
*last = i->next;
|
2015-08-28 11:48:52 +00:00
|
|
|
free(Q_V4_PROFILE_DEALLOC(engine, i, i->size + sizeof(Data::LargeItem),
|
2014-06-02 16:33:19 +00:00
|
|
|
Profiling::LargeItem));
|
2013-11-14 11:05:42 +00:00
|
|
|
i = *last;
|
|
|
|
}
|
|
|
|
|
2015-01-09 21:02:40 +00:00
|
|
|
// some execution contexts are allocated on the stack, make sure we clear their markBit as well
|
|
|
|
if (!lastSweep) {
|
2015-08-28 11:48:52 +00:00
|
|
|
QV4::ExecutionContext *ctx = engine->currentContext;
|
2015-01-09 21:02:40 +00:00
|
|
|
while (ctx) {
|
2015-08-24 13:31:46 +00:00
|
|
|
ctx->d()->clearMarkBit();
|
2015-08-28 11:48:52 +00:00
|
|
|
ctx = engine->parentContext(ctx);
|
2015-01-09 21:02:40 +00:00
|
|
|
}
|
|
|
|
}
|
2012-12-04 12:40:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool MemoryManager::isGCBlocked() const
|
|
|
|
{
|
|
|
|
return m_d->gcBlocked;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MemoryManager::setGCBlocked(bool blockGC)
|
|
|
|
{
|
|
|
|
m_d->gcBlocked = blockGC;
|
|
|
|
}
|
|
|
|
|
2013-01-02 15:43:47 +00:00
|
|
|
void MemoryManager::runGC()
|
2012-12-04 12:40:18 +00:00
|
|
|
{
|
2014-03-25 08:44:52 +00:00
|
|
|
if (m_d->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
|
|
|
}
|
|
|
|
|
2014-03-25 08:46:43 +00:00
|
|
|
if (!m_d->gcStats) {
|
|
|
|
mark();
|
|
|
|
sweep();
|
|
|
|
} else {
|
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();
|
2014-03-25 08:46:43 +00:00
|
|
|
|
|
|
|
QTime t;
|
|
|
|
t.start();
|
|
|
|
mark();
|
|
|
|
int markTime = t.elapsed();
|
|
|
|
t.restart();
|
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 usedBefore = getUsedMem();
|
2015-09-17 13:29:52 +00:00
|
|
|
const size_t largeItemsBefore = getLargeItemsMem();
|
2014-12-03 16:25:11 +00:00
|
|
|
int chunksBefore = m_d->heapChunks.size();
|
2014-03-25 08:46:43 +00:00
|
|
|
sweep();
|
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();
|
2014-03-25 08:46:43 +00:00
|
|
|
int sweepTime = t.elapsed();
|
|
|
|
|
|
|
|
qDebug() << "========== GC ==========";
|
|
|
|
qDebug() << "Marked object in" << markTime << "ms.";
|
|
|
|
qDebug() << "Sweeped object in" << sweepTime << "ms.";
|
|
|
|
qDebug() << "Allocated" << totalMem << "bytes in" << m_d->heapChunks.size() << "chunks.";
|
|
|
|
qDebug() << "Used memory before GC:" << usedBefore;
|
|
|
|
qDebug() << "Used memory after GC:" << usedAfter;
|
|
|
|
qDebug() << "Freed up bytes:" << (usedBefore - usedAfter);
|
2014-12-03 16:25:11 +00:00
|
|
|
qDebug() << "Released chunks:" << (chunksBefore - m_d->heapChunks.size());
|
2015-09-17 13:29:52 +00:00
|
|
|
qDebug() << "Large item memory before GC:" << largeItemsBefore;
|
|
|
|
qDebug() << "Large item memory after GC:" << largeItemsAfter;
|
|
|
|
qDebug() << "Large item memory freed up:" << (largeItemsBefore - largeItemsAfter);
|
2014-03-25 08:46:43 +00:00
|
|
|
qDebug() << "======== End GC ========";
|
|
|
|
}
|
2012-12-04 12:40:18 +00:00
|
|
|
|
2013-03-01 12:34:52 +00:00
|
|
|
memset(m_d->allocCount, 0, sizeof(m_d->allocCount));
|
2013-11-02 17:39:55 +00:00
|
|
|
m_d->totalAlloc = 0;
|
2014-10-27 12:51:27 +00:00
|
|
|
m_d->totalLargeItemsAllocated = 0;
|
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
|
|
|
{
|
2014-06-12 12:33:05 +00:00
|
|
|
size_t usedMem = 0;
|
2015-05-14 12:31:52 +00:00
|
|
|
for (QVector<PageAllocation>::const_iterator i = m_d->heapChunks.cbegin(), ei = m_d->heapChunks.cend(); i != ei; ++i) {
|
2015-01-09 17:52:56 +00:00
|
|
|
Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(i->base());
|
|
|
|
for (char *item = header->itemStart; item <= header->itemEnd; item += header->itemSize) {
|
|
|
|
Heap::Base *m = reinterpret_cast<Heap::Base *>(item);
|
|
|
|
Q_ASSERT((qintptr) item % 16 == 0);
|
2015-01-09 18:48:56 +00:00
|
|
|
if (m->inUse())
|
2015-01-09 17:52:56 +00:00
|
|
|
usedMem += header->itemSize;
|
2014-03-25 08:46:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return usedMem;
|
|
|
|
}
|
|
|
|
|
2014-06-12 12:33:05 +00:00
|
|
|
size_t MemoryManager::getAllocatedMem() const
|
|
|
|
{
|
|
|
|
size_t total = 0;
|
|
|
|
for (int i = 0; i < m_d->heapChunks.size(); ++i)
|
2015-01-09 17:52:56 +00:00
|
|
|
total += m_d->heapChunks.at(i).size();
|
2014-06-12 12:33:05 +00:00
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t MemoryManager::getLargeItemsMem() const
|
|
|
|
{
|
|
|
|
size_t total = 0;
|
|
|
|
for (const Data::LargeItem *i = m_d->largeItems; i != 0; i = i->next)
|
|
|
|
total += i->size;
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
2015-07-03 11:20:18 +00:00
|
|
|
void MemoryManager::growUnmanagedHeapSizeUsage(size_t delta)
|
|
|
|
{
|
|
|
|
m_d->unmanagedHeapSize += delta;
|
|
|
|
}
|
|
|
|
|
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);
|
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
|
2012-12-04 12:40:18 +00:00
|
|
|
}
|
|
|
|
|
2015-08-28 11:48:52 +00:00
|
|
|
|
2014-07-23 11:56:43 +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;
|
|
|
|
for (int i = 0; i < m_d->allocSizeCounters.size(); ++i) {
|
|
|
|
if (unsigned count = m_d->allocSizeCounters[i]) {
|
|
|
|
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;
|
|
|
|
QVector<unsigned> &counters = m_d->allocSizeCounters;
|
|
|
|
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
|
|
|
|
|
2013-09-03 10:40:07 +00:00
|
|
|
void MemoryManager::collectFromJSStack() const
|
|
|
|
{
|
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) {
|
2015-02-13 11:19:04 +00:00
|
|
|
Managed *m = v->as<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
|
2015-08-28 11:48:52 +00:00
|
|
|
m->mark(engine);
|
2013-09-03 10:40:07 +00:00
|
|
|
++v;
|
|
|
|
}
|
|
|
|
}
|
2013-06-24 13:28:00 +00:00
|
|
|
QT_END_NAMESPACE
|