8000 Fixed bug #70098 (Real memory usage doesn't decrease) · php/php-src@668ecaa · GitHub
[go: up one dir, main page]

Skip to content

Commit 668ecaa

Browse files
committed
Fixed bug #70098 (Real memory usage doesn't decrease)
1 parent ed8d16b commit 668ecaa

File tree

4 files changed

+202
-39
lines changed

4 files changed

+202
-39
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ PHP NEWS
44

55
- Core:
66
. Fixed "finally" issues. (Nikita, Dmitry)
7+
. Fixed bug #70098 (Real memory usage doesn't decrease). (Dmitry)
78
. Fixed bug #70159 (__CLASS__ is lost in closures). (Julien)
89
. Fixed bug #70156 (Segfault in zend_find_alias_name). (Laruence)
910
. Fixed bug #70124 (null ptr deref / seg fault in ZEND_HANDLE_EXCEPTION).

Zend/zend_alloc.c

Lines changed: 188 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -169,12 +169,22 @@ typedef zend_mm_bitset zend_mm_page_map[ZEND_MM_PAGE_MAP_LEN]; /* 64B */
169169
#define ZEND_MM_SRUN_BIN_NUM_MASK 0x0000001f
170170
#define ZEND_MM_SRUN_BIN_NUM_OFFSET 0
171171

172+
#define ZEND_MM_SRUN_FREE_COUNTER_MASK 0x01ff0000
173+
#define ZEND_MM_SRUN_FREE_COUNTER_OFFSET 16
174+
175+
#define ZEND_MM_NRUN_OFFSET_MASK 0x01ff0000
176+
#define ZEND_MM_NRUN_OFFSET_OFFSET 16
177+
172178
#define ZEND_MM_LRUN_PAGES(info) (((info) & ZEND_MM_LRUN_PAGES_MASK) >> ZEND_MM_LRUN_PAGES_OFFSET)
173179
#define ZEND_MM_SRUN_BIN_NUM(info) (((info) & ZEND_MM_SRUN_BIN_NUM_MASK) >> ZEND_MM_SRUN_BIN_NUM_OFFSET)
180+
#define ZEND_MM_SRUN_FREE_COUNTER(info) (((info) & ZEND_MM_SRUN_FREE_COUNTER_MASK) >> ZEND_MM_SRUN_FREE_COUNTER_OFFSET)
181+
#define ZEND_MM_NRUN_OFFSET(info) (((info) & ZEND_MM_NRUN_OFFSET_MASK) >> ZEND_MM_NRUN_OFFSET_OFFSET)
174182

175183
#define ZEND_MM_FRUN() ZEND_MM_IS_FRUN
176184
#define ZEND_MM_LRUN(count) (ZEND_MM_IS_LRUN | ((count) << ZEND_MM_LRUN_PAGES_OFFSET))
177185
#define ZEND_MM_SRUN(bin_num) (ZEND_MM_IS_SRUN | ((bin_num) << ZEND_MM_SRUN_BIN_NUM_OFFSET))
186+
#define ZEND_MM_SRUN_EX(bin_num, count) (ZEND_MM_IS_SRUN | ((bin_num) << ZEND_MM_SRUN_BIN_NUM_OFFSET) | ((count) << ZEND_MM_SRUN_FREE_COUNTER_OFFSET))
187+
#define ZEND_MM_NRUN(bin_num, offset) (ZEND_MM_IS_SRUN | ZEND_MM_IS_SRUN | ((bin_num) << ZEND_MM_SRUN_BIN_NUM_OFFSET) | ((offset) << ZEND_MM_NRUN_OFFSET_OFFSET))
178188

179189
#define ZEND_MM_BINS 30
180190

@@ -982,14 +992,17 @@ static void *zend_mm_alloc_pages(zend_mm_heap *heap, int pages_count ZEND_FILE_L
982992

983993
not_found:
984994
if (chunk->next == heap->main_chunk) {
995+
get_chunk:
985996
if (heap->cached_chunks) {
986997
heap->cached_chunks_count--;
987998
chunk = heap->cached_chunks;
988999
heap->cached_chunks = chunk->next;
9891000
} else {
9901001
#if ZEND_MM_LIMIT
991-
if (heap->real_size + ZEND_MM_CHUNK_SIZE > heap->limit) {
992-
if (heap->overflow == 0) {
1002+
if (UNEXPECTED(heap->real_size + ZEND_MM_CHUNK_SIZE > heap->limit)) {
1003+
if (zend_mm_gc(heap)) {
1004+
goto get_chunk;
1005+
} else if (heap->overflow == 0) {
9931006
#if ZEND_DEBUG
9941007
zend_mm_safe_error(heap, "Allowed memory size of %zu bytes exhausted at %s:%d (tried to allocate %zu bytes)", heap->limit, __zend_filename, __zend_lineno, size);
9951008
#else
@@ -1002,14 +1015,19 @@ static void *zend_mm_alloc_pages(zend_mm_heap *heap, int pages_count ZEND_FILE_L
10021015
chunk = (zend_mm_chunk*)zend_mm_chunk_alloc(heap, ZEND_MM_CHUNK_SIZE, ZEND_MM_CHUNK_SIZE);
10031016
if (UNEXPECTED(chunk == NULL)) {
10041017
/* insufficient memory */
1018+
if (zend_mm_gc(heap) &&
1019+
(chunk = (zend_mm_chunk*)zend_mm_chunk_alloc(heap, ZEND_MM_CHUNK_SIZE, ZEND_MM_CHUNK_SIZE)) != NULL) {
1020+
/* pass */
1021+
} else {
10051022
#if !ZEND_MM_LIMIT
1006-
zend_mm_safe_error(heap, "Out of memory");
1023+
zend_mm_safe_error(heap, "Out of memory");
10071024
#elif ZEND_DEBUG
1008-
zend_mm_safe_error(heap, "Out of memory (allocated %zu) at %s:%d (tried to allocate %zu bytes)", heap->real_size, __zend_filename, __zend_lineno, size);
1025+
zend_mm_safe_error(heap, "Out of memory (allocated %zu) at %s:%d (tried to allocate %zu bytes)", heap->real_size, __zend_filename, __zend_lineno, size);
10091026
#else
1010-
zend_mm_safe_error(heap, "Out of memory (allocated %zu) (tried to allocate %zu bytes)", heap->real_size, ZEND_MM_PAGE_SIZE * pages_count);
1027+
zend_mm_safe_error(heap, "Out of memory (allocated %zu) (tried to allocate %zu bytes)", heap->real_size, ZEND_MM_PAGE_SIZE * pages_count);
10111028
#endif
1012-
return NULL;
1029+
return NULL;
1030+
}
10131031
}
10141032
#if ZEND_MM_STAT
10151033
do {
@@ -1066,7 +1084,32 @@ static zend_always_inline void *zend_mm_alloc_large(zend_mm_heap *heap, size_t s
10661084
return ptr;
10671085
}
10681086

1069-
static void zend_mm_free_pages(zend_mm_heap *heap, zend_mm_chunk *chunk, int page_num, int pages_count)
1087+
static zend_always_inline void zend_mm_delete_chunk(zend_mm_heap *heap, zend_mm_chunk *chunk)
1088+
{
1089+
chunk->next->prev = chunk->prev;
1090+
chunk->prev->next = chunk->next;
1091+
heap->chunks_count--;
1092+
if (heap->chunks_count + heap->cached_chunks_count < heap->avg_chunks_count + 0.1) {
1093+
/* delay deletion */
1094+
heap->cached_chunks_count++;
1095+
chunk->next = heap->cached_chunks;
1096+
heap->cached_chunks = chunk;
1097+
} else {
1098+
#if ZEND_MM_STAT || ZEND_MM_LIMIT
1099+
heap->real_size -= ZEND_MM_CHUNK_SIZE;
1100+
#endif
1101+
if (!heap->cached_chunks || chunk->num > heap->cached_chunks->num) {
1102+
zend_mm_chunk_free(heap, chunk, ZEND_MM_CHUNK_SIZE);
1103+
} else {
1104+
//TODO: select the best chunk to delete???
1105+
chunk->next = heap->cached_chunks->next;
1106+
zend_mm_chunk_free(heap, heap->cached_chunks, ZEND_MM_CHUNK_SIZE);
1107+
heap->cached_chunks = chunk;
1108+
}
1109+
}
1110+
}
1111+
1112+
static zend_always_inline void zend_mm_free_pages_ex(zend_mm_heap *heap, zend_mm_chunk *chunk, int page_num, int pages_count, int free_chunk)
10701113
{
10711114
chunk->free_pages += pages_count;
10721115
zend_mm_bitset_reset_range(chunk->free_map, page_num, pages_count);
@@ -1075,32 +1118,16 @@ static void zend_mm_free_pages(zend_mm_heap *heap, zend_mm_chunk *chunk, int pag
10751118
/* this setting may be not accurate */
10761119
chunk->free_tail = page_num;
10771120
}
1078-
if (chunk->free_pages == ZEND_MM_PAGES - ZEND_MM_FIRST_PAGE) {
1079-
/* delete chunk */
1080-
chunk->next->prev = chunk->prev;
1081-
chunk->prev->next = chunk->next;
1082-
heap->chunks_count--;
1083-
if (heap->chunks_count + heap->cached_chunks_count < heap->avg_chunks_count + 0.1) {
1084-
/* delay deletion */
1085-
heap->cached_chunks_count++;
1086-
chunk->next = heap->cached_chunks;
1087-
heap->cached_chunks = chunk;
1088-
} else {
1089-
#if ZEND_MM_STAT || ZEND_MM_LIMIT
1090-
heap->real_size -= ZEND_MM_CHUNK_SIZE;
1091-
#endif
1092-
if (!heap->cached_chunks || chunk->num > heap->cached_chunks->num) {
1093-
zend_mm_chunk_free(heap, chunk, ZEND_MM_CHUNK_SIZE);
1094-
} else {
1095-
//TODO: select the best chunk to delete???
1096-
chunk->next = heap->cached_chunks->next;
1097-
zend_mm_chunk_free(heap, heap->cached_chunks, ZEND_MM_CHUNK_SIZE);
1098-
heap->cached_chunks = chunk;
1099-
}
1100-
}
1121+
if (free_chunk && chunk->free_pages == ZEND_MM_PAGES - ZEND_MM_FIRST_PAGE) {
1122+
zend_mm_delete_chunk(heap, chunk);
11011123
}
11021124
}
11031125

1126+
static void zend_mm_free_pages(zend_mm_heap *heap, zend_mm_chunk *chunk, int page_num, int pages_count)
1127+
{
1128+
zend_mm_free_pages_ex(heap, chunk, page_num, pages_count, 1);
1129+
}
1130+
11041131
static zend_always_inline void zend_mm_free_large(zend_mm_heap *heap, zend_mm_chunk *chunk, int page_num, int pages_count)
11051132
{
11061133
#if ZEND_MM_STAT
@@ -1196,7 +1223,7 @@ static zend_never_inline void *zend_mm_alloc_small_slow(zend_mm_heap *heap, int
11961223
if (bin_pages[bin_num] > 1) {
11971224
int i = 1;
11981225
do {
1199-
chunk->map[page_num+i] = ZEND_MM_SRUN(bin_num);
1226+
chunk->map[page_num+i] = ZEND_MM_NRUN(bin_num, i);
12001227
i++;
12011228
} while (i < bin_pages[bin_num]);
12021229
}
@@ -1441,8 +1468,10 @@ static void *zend_mm_realloc_heap(zend_mm_heap *heap, void *ptr, size_t size, si
14411468
}
14421469
} else /* if (new_size > old_size) */ {
14431470
#if ZEND_MM_LIMIT
1444-
if (heap->real_size + (new_size - old_size) > heap->limit) {
1445-
if (heap->overflow == 0) {
1471+
if (UNEXPECTED(heap->real_size + (new_size - old_size) > heap->limit)) {
1472+
if (zend_mm_gc(heap) && heap->real_size + (new_size - old_size) <= heap->limit) {
1473+
/* pass */
1474+
} else if (heap->overflow == 0) {
14461475
#if ZEND_DEBUG
14471476
zend_mm_safe_error(heap, "Allowed memory size of %zu bytes exhausted at %s:%d (tried to allocate %zu bytes)", heap->limit, __zend_filename, __zend_lineno, size);
14481477
#else
@@ -1665,8 +1694,10 @@ static void *zend_mm_alloc_huge(zend_mm_heap *heap, size_t size ZEND_FILE_LINE_D
16651694
void *ptr;
16661695

16671696
#if ZEND_MM_LIMIT
1668-
if (heap->real_size + new_size > heap->limit) {
1669-
if (heap->overflow == 0) {
1697+
if (UNEXPECTED(heap->real_size + new_size > heap->limit)) {
1698+
if (zend_mm_gc(heap) && heap->real_size + new_size <= heap->limit) {
1699+
/* pass */
1700+
} else if (heap->overflow == 0) {
16701701
#if ZEND_DEBUG
16711702
zend_mm_safe_error(heap, "Allowed memory size of %zu bytes exhausted at %s:%d (tried to allocate %zu bytes)", heap->limit, __zend_filename, __zend_lineno, size);
16721703
#else
@@ -1679,14 +1710,19 @@ static void *zend_mm_alloc_huge(zend_mm_heap *heap, size_t size ZEND_FILE_LINE_D
16791710
ptr = zend_mm_chunk_alloc(heap, new_size, ZEND_MM_CHUNK_SIZE);
16801711
if (UNEXPECTED(ptr == NULL)) {
16811712
/* insufficient memory */
1713+
if (zend_mm_gc(heap) &&
1714+
(ptr = zend_mm_chunk_alloc(heap, new_size, ZEND_MM_CHUNK_SIZE)) != NULL) {
1715+
/* pass */
1716+
} else {
16821717
#if !ZEND_MM_LIMIT
1683-
zend_mm_safe_error(heap, "Out of memory");
1718+
zend_mm_safe_error(heap, "Out of memory");
16841719
#elif ZEND_DEBUG
1685-
zend_mm_safe_error(heap, "Out of memory (allocated %zu) at %s:%d (tried to allocate %zu bytes)", heap->real_size, __zend_filename, __zend_lineno, size);
1720+
zend_mm_safe_error(heap, "Out of memory (allocated %zu) at %s:%d (tried to allocate %zu bytes)", heap->real_size, __zend_filename, __zend_lineno, size);
16861721
#else
1687-
zend_mm_safe_error(heap, "Out of memory (allocated %zu) (tried to allocate %zu bytes)", heap->real_size, size);
1722+
zend_mm_safe_error(heap, "Out of memory (allocated %zu) (tried to allocate %zu bytes)", heap->real_size, size);
16881723
#endif
1689-
return NULL;
1724+
return NULL;
1725+
}
16901726
}
16911727
#if ZEND_DEBUG
16921728
zend_mm_add_huge_block(heap, ptr, new_size, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
@@ -1783,6 +1819,119 @@ static zend_mm_heap *zend_mm_init(void)
17831819
return heap;
17841820
}
17851821

1822+
ZEND_API size_t zend_mm_gc(zend_mm_heap *heap)
1823+
{
1824+
zend_mm_free_slot *p, **q;
1825+
zend_mm_chunk *chunk;
1826+
size_t page_offset;
1827+
int page_num;
1828+
zend_mm_page_info info;
1829+
int i, has_free_pages, free_counter;
1830+
size_t collected = 0;
1831+
1832+
#if ZEND_MM_CUSTOM
1833+
if (heap->use_custom_heap) {
1834+
return 0;
1835+
}
1836+
#endif
1837+
1838+
for (i = 0; i < ZEND_MM_BINS; i++) {
1839+
has_free_pages = 0;
1840+
p = heap->free_slot[i];
1841+
while (p != NULL) {
1842+
chunk = (zend_mm_chunk*)ZEND_MM_ALIGNED_BASE(p, ZEND_MM_CHUNK_SIZE);
1843+
ZEND_MM_CHECK(chunk->heap == heap, "zend_mm_heap corrupted");
1844+
page_offset = ZEND_MM_ALIGNED_OFFSET(p, ZEND_MM_CHUNK_SIZE);
1845+
ZEND_ASSERT(page_offset != 0);
1846+
page_num = (int)(page_offset / ZEND_MM_PAGE_SIZE);
1847+
info = chunk->map[page_num];
1848+
ZEND_ASSERT(info & ZEND_MM_IS_SRUN);
1849+
if (info & ZEND_MM_IS_LRUN) {
1850+
page_num -= ZEND_MM_NRUN_OFFSET(info);
1851+
info = chunk->map[page_num];
1852+
ZEND_ASSERT(info & ZEND_MM_IS_SRUN);
1853+
ZEND_ASSERT(!(info & ZEND_MM_IS_LRUN));
1854+
}
1855+
ZEND_ASSERT(ZEND_MM_SRUN_BIN_NUM(info) == i);
1856+
free_counter = ZEND_MM_SRUN_FREE_COUNTER(info) + 1;
1857+
if (free_counter == bin_elements[i]) {
1858+
has_free_pages = 1;
1859+
}
1860+
chunk->map[page_num] = ZEND_MM_SRUN_EX(i, free_counter);;
1861+
p = p->next_free_slot;
1862+
}
1863+
1864+
if (!has_free_pages) {
1865+
continue;
1866+
}
1867+
1868+
q = &heap->free_slot[i];
1869+
p = *q;
1870+
while (p != NULL) {
1871+
chunk = (zend_mm_chunk*)ZEND_MM_ALIGNED_BASE(p, ZEND_MM_CHUNK_SIZE);
1872+
ZEND_MM_CHECK(chunk->heap == heap, "zend_mm_heap corrupted");
1873+
page_offset = ZEND_MM_ALIGNED_OFFSET(p, ZEND_MM_CHUNK_SIZE);
1874+
ZEND_ASSERT(page_offset != 0);
1875+
page_num = (int)(page_offset / ZEND_MM_PAGE_SIZE);
1876+
info = chunk->map[page_num];
1877+
ZEND_ASSERT(info & ZEND_MM_IS_SRUN);
1878+
if (info & ZEND_MM_IS_LRUN) {
1879+
page_num -= ZEND_MM_NRUN_OFFSET(info);
1880+
info = chunk->map[page_num];
1881+
ZEND_ASSERT(info & ZEND_MM_IS_SRUN);
1882+
ZEND_ASSERT(!(info & ZEND_MM_IS_LRUN));
1883+
}
1884+
ZEND_ASSERT(ZEND_MM_SRUN_BIN_NUM(info) == i);
1885+
if (ZEND_MM_SRUN_FREE_COUNTER(info) == bin_elements[i]) {
1886+
/* remove from cache */
1887+
p = p->next_free_slot;;
1888+
*q = p;
1889+
} else {
1890+
q = &p->next_free_slot;
1891+
p = *q;
1892+
}
1893+
}
1894+
}
1895+
1896+
chunk = heap->main_chunk;
1897+
do {
1898+
i = ZEND_MM_FIRST_PAGE;
1899+
while (i < chunk->free_tail) {
1900+
if (zend_mm_bitset_is_set(chunk->free_map, i)) {
1901+
info = chunk->map[i];
1902+
if (info & ZEND_MM_IS_SRUN) {
1903+
int bin_num = ZEND_MM_SRUN_BIN_NUM(info);
1904+
int pages_count = bin_pages[bin_num];
1905+
1906+
if (ZEND_MM_SRUN_FREE_COUNTER(info) == bin_elements[bin_num]) {
1907+
/* all elemens are free */
1908+
zend_mm_free_pages_ex(heap, chunk, i, pages_count, 0);
1909+
collected += pages_count;
1910+
} else {
1911+
/* reset counter */
1912+
chunk->map[i] = ZEND_MM_SRUN(bin_num);
1913+
}
1914+
i += bin_pages[bin_num];
1915+
} else /* if (info & ZEND_MM_IS_LRUN) */ {
1916+
i += ZEND_MM_LRUN_PAGES(info);
1917+
}
1918+
} else {
1919+
i++;
1920+
}
1921+
}
1922+
if (chunk->free_pages == ZEND_MM_PAGES - ZEND_MM_FIRST_PAGE) {
1923+
zend_mm_chunk *next_chunk = chunk->next;
1924+
1925+
zend_mm_delete_chunk(heap, chunk);
1926+
chunk = next_chunk;
1927+
} else {
1928+
chunk = chunk->next;
1929+
}
1930+
} while (chunk != heap->main_chunk);
1931+
1932+
return collected * ZEND_MM_PAGE_SIZE;
1933+
}
1934+
17861935
#if ZEND_DEBUG
17871936
/******************/
17881937
/* Leak detection */

Zend/zend_alloc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,8 @@ ZEND_API size_t ZEND_FASTCALL _zend_mm_block_size(zend_mm_heap *heap, void *p ZE
282282
ZEND_API zend_mm_heap *zend_mm_set_heap(zend_mm_heap *new_heap);
283283
ZEND_API zend_mm_heap *zend_mm_get_heap(void);
284284

285+
ZEND_API size_t zend_mm_gc(zend_mm_heap *heap);
286+
285287
ZEND_API int zend_mm_is_custom_heap(zend_mm_heap *new_heap);
286288
ZEND_API void zend_mm_set_custom_handlers(zend_mm_heap *heap,
287289
void* (*_malloc)(size_t),

Zend/zend_builtin_functions.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ static ZEND_FUNCTION(zend_test_func2);
9393
static ZEND_FUNCTION(zend_thread_id);
9494
#endif
9595
#endif
96+
static ZEND_FUNCTION(gc_mem_caches);
9697
static ZEND_FUNCTION(gc_collect_cycles);
9798
static ZEND_FUNCTION(gc_enabled);
9899
static ZEND_FUNCTION(gc_enable);
@@ -321,6 +322,7 @@ static const zend_function_entry builtin_functions[] = { /* {{{ */
321322
ZEND_FE(zend_thread_id, NULL)
322323
#endif
323324
#endif
325+
ZEND_FE(gc_mem_caches, arginfo_zend__void)
324326
ZEND_FE(gc_collect_cycles, arginfo_zend__void)
325327
ZEND_FE(gc_enabled, arginfo_zend__void)
326328
ZEND_FE(gc_enable, arginfo_zend__void)
@@ -371,6 +373,15 @@ ZEND_FUNCTION(zend_version)
371373
}
372374
/* }}} */
373375

376+
/* {{{ proto int gc_mem_caches(void)
377+
Reclaims memory used by MM caches.
378+
Returns number of freed bytes */
379+
ZEND_FUNCTION(gc_mem_caches)
380+
{
381+
RETURN_LONG(zend_mm_gc(zend_mm_get_heap()));
382+
}
383+
/* }}} */
384+
374385
/* {{{ proto int gc_collect_cycles(void)
375386
Forces collection of any existing garbage cycles.
376387
Returns number of freed zvals */

0 commit comments

Comments
 (0)
0