From 03d5daf9437d8b46db2e89e3a9763ea701fa681c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Brooke?= Date: Fri, 24 Jan 2025 17:09:58 +0100 Subject: [PATCH] Add jemalloc support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Large graphical Qt applications heavily rely on heap allocations. Jemalloc is a general-purpose malloc(3) implementation designed to reduce heap fragmentation and improve scalability. It also provides extensive tuning options. Add a -jemalloc configure option, disabled by default. When enabled, Qt and user code link to jemalloc, overriding the system's default malloc(). Add cooperation with jemalloc for some Qt key classes: QArrayData (used by QByteArray, QString and QList), QBindingStoragePrivate, QDataBuffer (used by the Qt Quick renderer), QDistanceFieldData, QImageData, QObjectPrivate::TaggedSignalVector, QVarLengthArray. This cooperation relies on two jemalloc-specific optimizations: 1. Efficient allocation via fittedMalloc(): Determine the actual allocation size using nallocx(), then adjust the container’s capacity to match. This minimizes future reallocations. Note: we round allocSize to a multiple of sizeof(T) to ensure that we can later recompute the exact allocation size during deallocation. 2. Optimized deallocation via sizedFree(): Use sdallocx(), which is faster than free when the allocation size is known, as it avoids internal size lookups. Adapt the QVarLengthArray auto tests on capacity. Non-standard functions docs are at https://jemalloc.net/jemalloc.3.html [ChangeLog][QtCore] Added optional support for the jemalloc allocator, and optimized memory allocations and deallocations in core Qt classes to cooperate with it. Change-Id: I6166e64e66876dee22662d3f3ea3e42a6647cfeb Reviewed-by: Thiago Macieira --- cmake/FindJeMalloc.cmake | 10 ++ config_help.txt | 1 + src/corelib/CMakeLists.txt | 6 + src/corelib/configure.cmake | 7 + src/corelib/global/qalloc.cpp | 43 ++++++ src/corelib/global/qalloc.h | 128 ++++++++++++++++++ src/corelib/global/qconfig-bootstrapped.h | 1 + src/corelib/kernel/qobject_p_p.h | 8 +- src/corelib/kernel/qproperty.cpp | 7 +- src/corelib/qt_cmdline.cmake | 1 + src/corelib/tools/qarraydata.cpp | 49 +++++-- src/corelib/tools/qvarlengtharray.h | 11 +- src/gui/image/qimage.cpp | 3 +- src/gui/painting/qdatabuffer_p.h | 14 +- src/gui/text/qdistancefield.cpp | 3 +- src/tools/bootstrap/CMakeLists.txt | 1 + .../qvarlengtharray/tst_qvarlengtharray.cpp | 12 +- 17 files changed, 267 insertions(+), 38 deletions(-) create mode 100644 cmake/FindJeMalloc.cmake create mode 100644 src/corelib/global/qalloc.cpp create mode 100644 src/corelib/global/qalloc.h diff --git a/cmake/FindJeMalloc.cmake b/cmake/FindJeMalloc.cmake new file mode 100644 index 00000000000..10ddb76757d --- /dev/null +++ b/cmake/FindJeMalloc.cmake @@ -0,0 +1,10 @@ +# Copyright (C) 2025 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +find_package(PkgConfig QUIET) + +pkg_check_modules(JeMalloc IMPORTED_TARGET "jemalloc") + +if (NOT TARGET PkgConfig::JeMalloc) + set(JeMalloc_FOUND 0) +endif() diff --git a/config_help.txt b/config_help.txt index 09aebf2e653..52ab7590fd9 100644 --- a/config_help.txt +++ b/config_help.txt @@ -272,6 +272,7 @@ Core options: -glib ................ Enable Glib support [no; auto on Unix] -inotify ............. Enable inotify support -icu ................. Enable ICU support [auto] + -jemalloc ............ Enable jemalloc support and cooperation [no] -pcre ................ Select used libpcre2 [system/qt/no] -zlib ................ Select used zlib [system/qt] diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index b8360927def..23cafd2be17 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -40,6 +40,7 @@ qt_internal_add_module(Core # Keep the rest alphabetical compat/removed_api.cpp global/archdetect.cpp + global/qalloc.cpp global/qalloc.h global/qassert.cpp global/qassert.h global/qcheckedint_impl.h global/qcompare_impl.h @@ -922,6 +923,11 @@ qt_internal_extend_target(Core CONDITION UNIX AND NOT MACOS AND NOT QT_FEATURE_i text/qcollator_posix.cpp ) +qt_internal_extend_target(Core CONDITION QT_FEATURE_jemalloc + PUBLIC_LIBRARIES + PkgConfig::JeMalloc +) + qt_internal_extend_target(Core CONDITION QT_FEATURE_regularexpression SOURCES text/qregularexpression.cpp text/qregularexpression.h diff --git a/src/corelib/configure.cmake b/src/corelib/configure.cmake index 7e0c05b0148..e2024bf03a9 100644 --- a/src/corelib/configure.cmake +++ b/src/corelib/configure.cmake @@ -35,6 +35,7 @@ qt_find_package(ICU 50.1 COMPONENTS i18n uc data PROVIDED_TARGETS ICU::i18n ICU: if(QT_FEATURE_dlopen) qt_add_qmake_lib_dependency(icu libdl) endif() +qt_find_package(JeMalloc PROVIDED_TARGETS PkgConfig::JeMalloc MODULE_NAME core QMAKE_LIB jemalloc) qt_find_package(Libsystemd PROVIDED_TARGETS PkgConfig::Libsystemd MODULE_NAME core QMAKE_LIB journald) qt_find_package(WrapAtomic PROVIDED_TARGETS WrapAtomic::WrapAtomic MODULE_NAME core QMAKE_LIB libatomic) qt_find_package(Libb2 PROVIDED_TARGETS Libb2::Libb2 MODULE_NAME core QMAKE_LIB libb2) @@ -733,6 +734,11 @@ qt_feature("ipc_posix" ) ) qt_feature_definition("ipc_posix" "QT_POSIX_IPC") +qt_feature("jemalloc" PUBLIC PRIVATE + LABEL "JeMalloc" + AUTODETECT OFF + CONDITION JeMalloc_FOUND +) qt_feature("journald" PRIVATE LABEL "journald" AUTODETECT OFF @@ -1172,6 +1178,7 @@ qt_configure_add_summary_entry(ARGS "system-doubleconversion") qt_configure_add_summary_entry(ARGS "forkfd_pidfd" CONDITION LINUX) qt_configure_add_summary_entry(ARGS "glib") qt_configure_add_summary_entry(ARGS "icu") +qt_configure_add_summary_entry(ARGS "jemalloc") qt_configure_add_summary_entry(ARGS "timezone_tzdb") qt_configure_add_summary_entry(ARGS "system-libb2") qt_configure_add_summary_entry(ARGS "mimetype-database") diff --git a/src/corelib/global/qalloc.cpp b/src/corelib/global/qalloc.cpp new file mode 100644 index 00000000000..ca893a5789a --- /dev/null +++ b/src/corelib/global/qalloc.cpp @@ -0,0 +1,43 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qalloc.h" + +#include +#include + +#include + +#if QT_CONFIG(jemalloc) +#include +#endif + +QT_BEGIN_NAMESPACE + +size_t QtPrivate::expectedAllocSize(size_t allocSize, size_t alignment) noexcept +{ + Q_ASSERT(qPopulationCount(alignment) == 1); +#if QT_CONFIG(jemalloc) + return ::nallocx(allocSize, MALLOCX_ALIGN(alignment)); +#endif + Q_UNUSED(allocSize); + Q_UNUSED(alignment); + return 0; +} + +void QtPrivate::sizedFree(void *ptr, size_t allocSize) noexcept +{ +#if QT_CONFIG(jemalloc) + // jemalloc is okay with free(nullptr), as required by the standard, + // but will asssert (in debug) or invoke UB (in release) on sdallocx(nullptr, ...), + // so don't allow Qt to do that. + if (Q_LIKELY(ptr)) { + ::sdallocx(ptr, allocSize, 0); + return; + } +#endif + Q_UNUSED(allocSize); + ::free(ptr); +} + +QT_END_NAMESPACE diff --git a/src/corelib/global/qalloc.h b/src/corelib/global/qalloc.h new file mode 100644 index 00000000000..4adb079fb9a --- /dev/null +++ b/src/corelib/global/qalloc.h @@ -0,0 +1,128 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QALLOC_H +#define QALLOC_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace QtPrivate { + +/** + * \internal + * \return the size that would be allocated for the given request. + * + * Computes the actual allocation size for \a allocSize and \a alignment, + * as determined by the active allocator, without performing the allocation. + * + * In practice, it only returns nonzero when using jemalloc. + */ +Q_CORE_EXPORT Q_DECL_PURE_FUNCTION +size_t expectedAllocSize(size_t allocSize, size_t alignment) noexcept; + +/** + * \internal + * \brief Computes the best allocation size for the requested minimum capacity, and updates capacity. + * + * Computes the allocation size starting from \a headerSize and a requested minimum capacity in \a capacity, + * multiplied by the \a elementSize and adjusted by the \a unusedCapacity. + * The final capacity is written back into \a capacity. + * The \a headerSize and \a unusedCapacity values are not included in the final reported capacity. + */ +inline size_t fittedAllocSize(size_t headerSize, size_t *capacity, + size_t elementSize, size_t unusedCapacity, size_t alignment) noexcept +{ + size_t totalCapacity = 0; // = capacity + unusedCapacity + if (Q_UNLIKELY(qAddOverflow(*capacity, unusedCapacity, &totalCapacity))) + return 0; // or handle error + + size_t payloadSize = 0; // = totalCapacity * elementSize + if (Q_UNLIKELY(qMulOverflow(totalCapacity, elementSize, &payloadSize))) + return 0; + + size_t allocSize = 0; // = headerSize + payloadSize + if (Q_UNLIKELY(qAddOverflow(headerSize, payloadSize, &allocSize))) + return 0; + + if (size_t fittedSize = expectedAllocSize(allocSize, alignment); fittedSize != 0) { + // no need to overflow/underflow check from fittedSize, + // since allocSize <= fittedSize <= SIZE_T_MAX + *capacity = (fittedSize - headerSize) / elementSize - unusedCapacity; + size_t newTotalCapacity = *capacity + unusedCapacity; + size_t newPayloadSize = newTotalCapacity * elementSize; + return headerSize + newPayloadSize; + } + + return allocSize; +} + +#ifdef Q_CC_GNU +__attribute__((malloc)) +#endif +inline void *fittedMalloc(size_t headerSize, size_t *capacity, + size_t elementSize, size_t unusedCapacity) noexcept +{ + size_t allocSize = fittedAllocSize(headerSize, capacity, + elementSize, unusedCapacity, alignof(std::max_align_t)); + if (Q_LIKELY(allocSize != 0)) + return malloc(allocSize); + else + return nullptr; +} +inline void *fittedMalloc(size_t headerSize, qsizetype *capacity, + size_t elementSize, size_t unusedCapacity = 0) noexcept +{ + size_t uCapacity = size_t(*capacity); + void *ptr = fittedMalloc(headerSize, &uCapacity, elementSize, unusedCapacity); + *capacity = qsizetype(uCapacity); + return ptr; +} + +inline void *fittedRealloc(void *ptr, size_t headerSize, size_t *capacity, + size_t elementSize, size_t unusedCapacity) noexcept +{ + size_t allocSize = fittedAllocSize(headerSize, capacity, + elementSize, unusedCapacity, alignof(std::max_align_t)); + if (Q_LIKELY(allocSize != 0)) + return realloc(ptr, allocSize); + else + return nullptr; +} +inline void *fittedRealloc(void *ptr, size_t headerSize, qsizetype *capacity, + size_t elementSize, size_t unusedCapacity = 0) noexcept +{ + size_t uCapacity = size_t(*capacity); + ptr = fittedRealloc(ptr, headerSize, &uCapacity, elementSize, unusedCapacity); + *capacity = qsizetype(uCapacity); + return ptr; +} + +Q_CORE_EXPORT void sizedFree(void *ptr, size_t allocSize) noexcept; +inline void sizedFree(void *ptr, size_t capacity, size_t elementSize) noexcept +{ + sizedFree(ptr, capacity * elementSize); +} + +} // namespace QtPrivate + +QT_END_NAMESPACE + +#endif // QALLOC_H diff --git a/src/corelib/global/qconfig-bootstrapped.h b/src/corelib/global/qconfig-bootstrapped.h index e2dbf9e2e40..97afd0cba01 100644 --- a/src/corelib/global/qconfig-bootstrapped.h +++ b/src/corelib/global/qconfig-bootstrapped.h @@ -62,6 +62,7 @@ #define QT_FEATURE_itemmodel -1 #define QT_FEATURE_islamiccivilcalendar -1 #define QT_FEATURE_jalalicalendar -1 +#define QT_FEATURE_jemalloc -1 #define QT_FEATURE_journald -1 #define QT_FEATURE_library -1 #ifdef __linux__ diff --git a/src/corelib/kernel/qobject_p_p.h b/src/corelib/kernel/qobject_p_p.h index 2277af04976..3569e907155 100644 --- a/src/corelib/kernel/qobject_p_p.h +++ b/src/corelib/kernel/qobject_p_p.h @@ -20,6 +20,7 @@ // code lives here is that some special apps/libraries for e.g., QtJambi, // Gammaray need access to the structs in this file. +#include #include #include @@ -152,8 +153,9 @@ struct QObjectPrivate::ConnectionData deleteOrphaned(c); SignalVector *v = signalVector.loadRelaxed(); if (v) { + const size_t allocSize = sizeof(SignalVector) + (v->allocated + 1) * sizeof(ConnectionList); v->~SignalVector(); - free(v); + QtPrivate::sizedFree(v, allocSize); } } @@ -179,13 +181,13 @@ struct QObjectPrivate::ConnectionData return signalVector.loadRelaxed()->at(signal); } - void resizeSignalVector(uint size) + void resizeSignalVector(size_t size) { SignalVector *vector = this->signalVector.loadRelaxed(); if (vector && vector->allocated > size) return; size = (size + 7) & ~7; - void *ptr = malloc(sizeof(SignalVector) + (size + 1) * sizeof(ConnectionList)); + void *ptr = QtPrivate::fittedMalloc(sizeof(SignalVector), &size, sizeof(ConnectionList), 1); auto newVector = new (ptr) SignalVector; int start = -1; diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp index d849d78f0fd..fdb82baf86e 100644 --- a/src/corelib/kernel/qproperty.cpp +++ b/src/corelib/kernel/qproperty.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -2212,7 +2213,8 @@ struct QBindingStoragePrivate } } // data has been moved, no need to call destructors on old Pairs - free(d); + const size_t oldAllocSize = sizeof(QBindingStorageData) + d->size*sizeof(Pair); + QtPrivate::sizedFree(d, oldAllocSize); d = newData; } @@ -2269,7 +2271,8 @@ struct QBindingStoragePrivate p->~Pair(); ++p; } - free(d); + const size_t allocSize = sizeof(QBindingStorageData) + d->size*sizeof(Pair); + QtPrivate::sizedFree(d, allocSize); } }; diff --git a/src/corelib/qt_cmdline.cmake b/src/corelib/qt_cmdline.cmake index dddb74cbafc..4d0b3eca86d 100644 --- a/src/corelib/qt_cmdline.cmake +++ b/src/corelib/qt_cmdline.cmake @@ -5,6 +5,7 @@ qt_commandline_option(doubleconversion TYPE enum VALUES no qt system) qt_commandline_option(glib TYPE boolean) qt_commandline_option(icu TYPE boolean) qt_commandline_option(inotify TYPE boolean) +qt_commandline_option(jemalloc TYPE boolean) qt_commandline_option(journald TYPE boolean) qt_commandline_option(libb2 TYPE enum VALUES no qt system) qt_commandline_option(mimetype-database TYPE boolean) diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index 5452fddaa89..37d6dea35f9 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -2,6 +2,7 @@ // Copyright (C) 2016 Intel Corporation. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#include #include #include #include @@ -101,12 +102,34 @@ qCalculateGrowingBlockSize(qsizetype elementCount, qsizetype elementSize, qsizet } else { bytes = qsizetype(morebytes); } + size_t fittedBytes = QtPrivate::expectedAllocSize(bytes, alignof(std::max_align_t)); + if (fittedBytes != 0) + bytes = fittedBytes; result.elementCount = (bytes - headerSize) / elementSize; result.size = result.elementCount * elementSize + headerSize; return result; } +using QtPrivate::AlignedQArrayData; + +static qsizetype calculateHeaderSize(qsizetype alignment) +{ + qsizetype headerSize = sizeof(AlignedQArrayData); + const qsizetype headerAlignment = alignof(AlignedQArrayData); + + if (alignment > headerAlignment) { + // Allocate extra (alignment - Q_ALIGNOF(AlignedQArrayData)) padding + // bytes so we can properly align the data array. This assumes malloc is + // able to provide appropriate alignment for the header -- as it should! + // Effectively, we allocate one QTypedArrayData::AlignmentDummy. + headerSize += alignment - headerAlignment; + } + Q_ASSERT(headerSize > 0); + + return headerSize; +} + /* Calculate the byte size for a block of \a capacity objects of size \a objectSize, with a header of size \a headerSize. If the \a option is @@ -140,7 +163,6 @@ struct AllocationResult { QArrayData *header; }; } -using QtPrivate::AlignedQArrayData; static inline AllocationResult allocateHelper(qsizetype objectSize, qsizetype alignment, qsizetype capacity, @@ -149,16 +171,7 @@ allocateHelper(qsizetype objectSize, qsizetype alignment, qsizetype capacity, if (capacity == 0) return {}; - qsizetype headerSize = sizeof(AlignedQArrayData); - const qsizetype headerAlignment = alignof(AlignedQArrayData); - - if (alignment > headerAlignment) { - // Allocate extra (alignment - Q_ALIGNOF(AlignedQArrayData)) padding - // bytes so we can properly align the data array. This assumes malloc is - // able to provide appropriate alignment for the header -- as it should! - // Effectively, we allocate one QTypedArrayData::AlignmentDummy. - headerSize += alignment - headerAlignment; - } + const qsizetype headerSize = calculateHeaderSize(alignment); Q_ASSERT(headerSize > 0); auto blockSize = calculateBlockSize(capacity, objectSize, headerSize, option); @@ -248,10 +261,18 @@ void QArrayData::deallocate(QArrayData *data, qsizetype objectSize, // Alignment is a power of two Q_ASSERT(alignment >= qsizetype(alignof(QArrayData)) && !(alignment & (alignment - 1))); - Q_UNUSED(objectSize); - Q_UNUSED(alignment); - ::free(data); + const qsizetype capacity = data->alloc; + const qsizetype headerSize = calculateHeaderSize(alignment); + Q_ASSERT(headerSize > 0); + const auto blockSize = calculateBlockSize(capacity, objectSize, + headerSize, QArrayData::KeepSize); + const qsizetype allocSize = blockSize.size; + + if (Q_LIKELY(allocSize > 0)) + QtPrivate::sizedFree(data, size_t(allocSize)); + else // something went wrong, fallback to slow free() + free(data); } QT_END_NAMESPACE diff --git a/src/corelib/tools/qvarlengtharray.h b/src/corelib/tools/qvarlengtharray.h index 5e9a335abc0..fc4eb5bf9c6 100644 --- a/src/corelib/tools/qvarlengtharray.h +++ b/src/corelib/tools/qvarlengtharray.h @@ -9,6 +9,7 @@ #pragma qt_sync_stop_processing #endif +#include #include #include #include @@ -368,7 +369,7 @@ public: if constexpr (QTypeInfo::isComplex) std::destroy_n(data(), size()); if (data() != reinterpret_cast(this->array)) - free(data()); + QtPrivate::sizedFree(data(), capacity(), sizeof(T)); } inline QVarLengthArray &operator=(const QVarLengthArray &other) { @@ -729,9 +730,9 @@ Q_INLINE_TEMPLATE QVarLengthArray::QVarLengthArray(qsizetype asize) // resize(asize) // this requires a movable or copyable T, can't use, need to do it by hand if (asize > Prealloc) { - this->ptr = malloc(asize * sizeof(T)); - Q_CHECK_PTR(this->ptr); this->a = asize; + this->ptr = QtPrivate::fittedMalloc(0, &this->a, sizeof(T)); + Q_CHECK_PTR(this->ptr); } if constexpr (QTypeInfo::isComplex) std::uninitialized_default_construct_n(data(), asize); @@ -877,7 +878,7 @@ Q_OUTOFLINE_TEMPLATE void QVLABase::reallocate_impl(qsizetype prealloc, void void *newPtr; qsizetype newA; if (aalloc > prealloc) { - newPtr = malloc(aalloc * sizeof(T)); + newPtr = QtPrivate::fittedMalloc(0, &aalloc, sizeof(T)); guard.reset(newPtr); Q_CHECK_PTR(newPtr); // could throw // by design: in case of QT_NO_EXCEPTIONS malloc must not fail or it crashes here @@ -902,7 +903,7 @@ Q_OUTOFLINE_TEMPLATE void QVLABase::reallocate_impl(qsizetype prealloc, void } if (oldPtr != reinterpret_cast(array) && oldPtr != data()) - free(oldPtr); + QtPrivate::sizedFree(oldPtr, osize, sizeof(T)); } template diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index fbae59519a2..840bff26e53 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -162,7 +163,7 @@ QImageData::~QImageData() QImagePixmapCleanupHooks::executeImageHooks((((qint64) ser_no) << 32) | ((qint64) detach_no)); delete paintEngine; if (data && own_data) - free(data); + QtPrivate::sizedFree(data, nbytes); data = nullptr; } diff --git a/src/gui/painting/qdatabuffer_p.h b/src/gui/painting/qdatabuffer_p.h index aa8335542de..c7474dc57a3 100644 --- a/src/gui/painting/qdatabuffer_p.h +++ b/src/gui/painting/qdatabuffer_p.h @@ -17,6 +17,7 @@ #include +#include "QtCore/qalloc.h" #include "QtCore/qbytearray.h" #include "QtCore/qtypeinfo.h" @@ -34,7 +35,7 @@ public: if (res) { QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Walloc-size-larger-than=") - buffer = (Type*) malloc(capacity * sizeof(Type)); + buffer = (Type*) QtPrivate::fittedMalloc(0, &capacity, sizeof(Type)); QT_WARNING_POP Q_CHECK_PTR(buffer); } else { @@ -47,7 +48,7 @@ public: { static_assert(!QTypeInfo::isComplex); if (buffer) - free(buffer); + QtPrivate::sizedFree(buffer, capacity, sizeof(Type)); } inline void reset() { siz = 0; } @@ -86,20 +87,21 @@ public: capacity = 1; while (capacity < size) capacity *= 2; - buffer = (Type*) realloc(static_cast(buffer), capacity * sizeof(Type)); + buffer = (Type*) QtPrivate::fittedRealloc(static_cast(buffer), 0, &capacity, sizeof(Type)); Q_CHECK_PTR(buffer); } } void shrink(qsizetype size) { Q_ASSERT(capacity >= size); - capacity = size; if (size) { - buffer = (Type*) realloc(static_cast(buffer), capacity * sizeof(Type)); + capacity = size; + buffer = (Type*) QtPrivate::fittedRealloc(static_cast(buffer), 0, &capacity, sizeof(Type)); Q_CHECK_PTR(buffer); siz = std::min(siz, size); } else { - free(buffer); + QtPrivate::sizedFree(buffer, capacity, sizeof(Type)); + capacity = size; buffer = nullptr; siz = 0; } diff --git a/src/gui/text/qdistancefield.cpp b/src/gui/text/qdistancefield.cpp index 8b5efe24721..f4dca6185bc 100644 --- a/src/gui/text/qdistancefield.cpp +++ b/src/gui/text/qdistancefield.cpp @@ -3,6 +3,7 @@ #include "qdistancefield_p.h" #include +#include #include #include #include @@ -826,7 +827,7 @@ QDistanceFieldData::QDistanceFieldData(const QDistanceFieldData &other) QDistanceFieldData::~QDistanceFieldData() { - free(data); + QtPrivate::sizedFree(data, nbytes); } QDistanceFieldData *QDistanceFieldData::create(const QSize &size) diff --git a/src/tools/bootstrap/CMakeLists.txt b/src/tools/bootstrap/CMakeLists.txt index c0f5e6f37e7..0d83f058599 100644 --- a/src/tools/bootstrap/CMakeLists.txt +++ b/src/tools/bootstrap/CMakeLists.txt @@ -17,6 +17,7 @@ qt_internal_add_sync_header_dependencies(Bootstrap Core) qt_internal_extend_target(Bootstrap SOURCES + ../../corelib/global/qalloc.cpp ../../corelib/global/qassert.cpp ../../corelib/global/qtenvironmentvariables.cpp ../../corelib/io/qabstractfileengine.cpp diff --git a/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp b/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp index 6a92663bc4f..459bce09df6 100644 --- a/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp +++ b/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp @@ -1171,11 +1171,11 @@ void tst_QVarLengthArray::squeeze() list.resize(0); QCOMPARE(list.capacity(), sizeOnStack); list.resize(sizeOnHeap); - QCOMPARE(list.capacity(), sizeOnHeap); + QCOMPARE_GE(list.capacity(), sizeOnHeap); list.resize(sizeOnStack); - QCOMPARE(list.capacity(), sizeOnHeap); + QCOMPARE_GE(list.capacity(), sizeOnHeap); list.resize(0); - QCOMPARE(list.capacity(), sizeOnHeap); + QCOMPARE_GE(list.capacity(), sizeOnHeap); list.squeeze(); QCOMPARE(list.capacity(), sizeOnStack); list.resize(sizeOnStack); @@ -1183,7 +1183,7 @@ void tst_QVarLengthArray::squeeze() QCOMPARE(list.capacity(), sizeOnStack); list.resize(sizeOnHeap); list.squeeze(); - QCOMPARE(list.capacity(), sizeOnHeap); + QCOMPARE_GE(list.capacity(), sizeOnHeap); } void tst_QVarLengthArray::operators() @@ -1545,14 +1545,14 @@ void tst_QVarLengthArray::reserve() arr.reserve(150); // Allocate memory on heap, as we reserve more than pre-allocated - QCOMPARE(arr.capacity(), 150); + QCOMPARE_GE(arr.capacity(), 150); QCOMPARE(arr.size(), 0); const auto *heapPtr = arr.constData(); QVERIFY(heapPtr != stackPtr); arr.reserve(50); // Nothing changed - QCOMPARE(arr.capacity(), 150); + QCOMPARE_GE(arr.capacity(), 150); QCOMPARE(arr.constData(), heapPtr); arr.squeeze();