Heap::String *ExecutionEngine::newString(const QString &s)
{
Scope scope(this);
- return ScopedString(scope, memoryManager->alloc<String>(s))->d();
+ return ScopedString(scope, memoryManager->allocWithStringData<String>(s.length() * sizeof(QChar), s))->d();
}
Heap::String *ExecutionEngine::newIdentifier(const QString &text)
break;
}
// duplicate arguments, need some trick to store them
- arg = engine->memoryManager->alloc<String>(arg->d(), engine->newString(QString(0xfffe)));
+ MemoryManager *mm = engine->memoryManager;
+ arg = mm->alloc<String>(mm, arg->d(), engine->newString(QString(0xfffe)));
}
}
uint maxShift;
std::size_t maxChunkSize;
QVector<PageAllocation> heapChunks;
+ 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;
struct LargeItem {
LargeItem *next;
, totalAlloc(0)
, maxShift(6)
, maxChunkSize(32*1024)
+ , unmanagedHeapSize(0)
+ , unmanagedHeapSizeGCLimit(64 * 1024)
, largeItems(0)
, totalLargeItemsAllocated(0)
, deletable(0)
namespace {
-bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, ExecutionEngine *engine)
+bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, ExecutionEngine *engine, std::size_t *unmanagedHeapSize)
{
+ Q_ASSERT(unmanagedHeapSize);
+
bool isEmpty = true;
Heap::Base *tail = &header->freeItems;
// qDebug("chunkStart @ %p, size=%x, pos=%x", header->itemStart, header->itemSize, header->itemSize>>4);
#endif
for (char *item = header->itemStart; item <= header->itemEnd; item += header->itemSize) {
Heap::Base *m = reinterpret_cast<Heap::Base *>(item);
-// qDebug("chunk @ %p, size = %lu, in use: %s, mark bit: %s",
-// item, m->size, (m->inUse ? "yes" : "no"), (m->markBit ? "true" : "false"));
+// qDebug("chunk @ %p, in use: %s, mark bit: %s",
+// item, (m->inUse() ? "yes" : "no"), (m->isMarked() ? "true" : "false"));
Q_ASSERT((qintptr) item % 16 == 0);
#ifdef V4_USE_VALGRIND
VALGRIND_ENABLE_ERROR_REPORTING;
#endif
+ if (std::size_t(header->itemSize) == MemoryManager::align(sizeof(Heap::String)) && m->gcGetVtable()->isString) {
+ 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;
+ }
+
if (m->gcGetVtable()->destroy)
m->gcGetVtable()->destroy(m);
m_d->engine = engine;
}
-Heap::Base *MemoryManager::allocData(std::size_t size)
+Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize)
{
if (m_d->aggressiveGC)
runGC();
Q_ASSERT(size >= 16);
Q_ASSERT(size % 16 == 0);
+// 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();
+
+ if (m_d->unmanagedHeapSizeGCLimit <= m_d->unmanagedHeapSize)
+ m_d->unmanagedHeapSizeGCLimit = std::max(m_d->unmanagedHeapSizeGCLimit, m_d->unmanagedHeapSize) * 2;
+ else if (m_d->unmanagedHeapSize * 4 <= m_d->unmanagedHeapSizeGCLimit)
+ m_d->unmanagedHeapSizeGCLimit /= 2;
+ else if (m_d->unmanagedHeapSizeGCLimit - m_d->unmanagedHeapSize < 5 * unmanagedSize)
+ // try preventing running the GC all the time when we're just below the threshold limit and manage to collect just enough to do this one allocation
+ m_d->unmanagedHeapSizeGCLimit += std::max(std::size_t(8 * 1024), 5 * unmanagedSize);
+ didGCRun = true;
+ }
+
size_t pos = size >> 4;
// doesn't fit into a small bucket
if (size >= MemoryManager::Data::MaxItemSize) {
- if (m_d->totalLargeItemsAllocated > 8 * 1024 * 1024)
+ if (!didGCRun && m_d->totalLargeItemsAllocated > 8 * 1024 * 1024)
runGC();
// we use malloc for this
}
// try to free up space, otherwise allocate
- if (m_d->allocCount[pos] > (m_d->availableItems[pos] >> 1) && m_d->totalAlloc > (m_d->totalItems >> 1) && !m_d->aggressiveGC) {
+ if (!didGCRun && m_d->allocCount[pos] > (m_d->availableItems[pos] >> 1) && m_d->totalAlloc > (m_d->totalItems >> 1) && !m_d->aggressiveGC) {
runGC();
header = m_d->nonFullChunks[pos];
if (header) {
for (int i = 0; i < m_d->heapChunks.size(); ++i) {
Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(m_d->heapChunks[i].base());
- chunkIsEmpty[i] = sweepChunk(header, &itemsInUse[header->itemSize >> 4], m_d->engine);
+ chunkIsEmpty[i] = sweepChunk(header, &itemsInUse[header->itemSize >> 4], m_d->engine, &m_d->unmanagedHeapSize);
}
QVector<PageAllocation>::iterator chunkIter = m_d->heapChunks.begin();
return total;
}
+void MemoryManager::growUnmanagedHeapSizeUsage(size_t delta)
+{
+ m_d->unmanagedHeapSize += delta;
+}
+
MemoryManager::~MemoryManager()
{
delete m_persistentValues;
{ return (size + 15) & ~0xf; }
template<typename ManagedType>
- inline typename ManagedType::Data *allocManaged(std::size_t size)
+ inline typename ManagedType::Data *allocManaged(std::size_t size, std::size_t unmanagedSize = 0)
{
size = align(size);
- Heap::Base *o = allocData(size);
+ Heap::Base *o = allocData(size, unmanagedSize);
o->vtable = ManagedType::staticVTable();
return static_cast<typename ManagedType::Data *>(o);
}
return t->d();
}
+ template <typename ManagedType, typename Arg1>
+ typename ManagedType::Data *allocWithStringData(std::size_t unmanagedSize, Arg1 arg1)
+ {
+ Scope scope(engine());
+ Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data), unmanagedSize));
+ (void)new (t->d()) typename ManagedType::Data(this, arg1);
+ return t->d();
+ }
+
template <typename ManagedType, typename Arg1, typename Arg2>
typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2)
{
size_t getAllocatedMem() const;
size_t getLargeItemsMem() const;
+ void growUnmanagedHeapSizeUsage(size_t delta); // called when a JS object grows itself. Specifically: Heap::String::append
+
protected:
/// expects size to be aligned
// TODO: try to inline
- Heap::Base *allocData(std::size_t size);
+ Heap::Base *allocData(std::size_t size, std::size_t unmanagedSize);
#ifdef DETAILED_MM_STATS
void willAllocate(std::size_t size);
return pright->asReturnedValue();
if (!pright->stringValue()->d()->length())
return pleft->asReturnedValue();
- return (engine->memoryManager->alloc<String>(pleft->stringValue()->d(), pright->stringValue()->d()))->asReturnedValue();
+ MemoryManager *mm = engine->memoryManager;
+ return (mm->alloc<String>(mm, pleft->stringValue()->d(), pright->stringValue()->d()))->asReturnedValue();
}
double x = RuntimeHelpers::toNumber(pleft);
double y = RuntimeHelpers::toNumber(pright);
return right.asReturnedValue();
if (!right.stringValue()->d()->length())
return left.asReturnedValue();
- return (engine->memoryManager->alloc<String>(left.stringValue()->d(), right.stringValue()->d()))->asReturnedValue();
+ MemoryManager *mm = engine->memoryManager;
+ return (mm->alloc<String>(mm, left.stringValue()->d(), right.stringValue()->d()))->asReturnedValue();
}
Scope scope(engine);
return pright->asReturnedValue();
if (!pright->stringValue()->d()->length())
return pleft->asReturnedValue();
- return (engine->memoryManager->alloc<String>(pleft->stringValue()->d(), pright->stringValue()->d()))->asReturnedValue();
+ MemoryManager *mm = engine->memoryManager;
+ return (mm->alloc<String>(mm, pleft->stringValue()->d(), pright->stringValue()->d()))->asReturnedValue();
}
void Runtime::setProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)
}
-Heap::String::String(const QString &t)
+Heap::String::String(MemoryManager *mm, const QString &t)
+ : mm(mm)
{
subtype = String::StringType_Unknown;
len = text->size;
}
-Heap::String::String(String *l, String *r)
+Heap::String::String(MemoryManager *mm, String *l, String *r)
+ : mm(mm)
{
subtype = String::StringType_Unknown;
text->ref.ref();
identifier = 0;
largestSubLength = 0;
+ mm->growUnmanagedHeapSizeUsage(size_t(text->size) * sizeof(QChar));
}
void Heap::String::createHashValue() const
StringType_ArrayIndex
};
- String(const QString &text);
- String(String *l, String *n);
+ String(MemoryManager *mm, const QString &text);
+ String(MemoryManager *mm, String *l, String *n);
~String() {
if (!largestSubLength && !text->ref.deref())
QStringData::deallocate(text);
len == (uint)text->size);
return len;
}
+ std::size_t retainedTextSize() const {
+ return largestSubLength ? 0 : (std::size_t(text->size) * sizeof(QChar));
+ }
void createHashValue() const;
inline unsigned hashValue() const {
if (subtype == StringType_Unknown)
mutable uint stringHash;
mutable uint largestSubLength;
uint len;
+ MemoryManager *mm;
private:
static void append(const String *data, QChar *ch);
};