@@ -169,12 +169,22 @@ typedef zend_mm_bitset zend_mm_page_map[ZEND_MM_PAGE_MAP_LEN]; /* 64B */
169
169
#define ZEND_MM_SRUN_BIN_NUM_MASK 0x0000001f
170
170
#define ZEND_MM_SRUN_BIN_NUM_OFFSET 0
171
171
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
+
172
178
#define ZEND_MM_LRUN_PAGES (info ) (((info) & ZEND_MM_LRUN_PAGES_MASK) >> ZEND_MM_LRUN_PAGES_OFFSET)
173
179
#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)
174
182
175
183
#define ZEND_MM_FRUN () ZEND_MM_IS_FRUN
176
184
#define ZEND_MM_LRUN (count ) (ZEND_MM_IS_LRUN | ((count) << ZEND_MM_LRUN_PAGES_OFFSET))
177
185
#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))
178
188
179
189
#define ZEND_MM_BINS 30
180
190
@@ -982,14 +992,17 @@ static void *zend_mm_alloc_pages(zend_mm_heap *heap, int pages_count ZEND_FILE_L
982
992
983
993
not_found :
984
994
if (chunk -> next == heap -> main_chunk ) {
995
+ get_chunk :
985
996
if (heap -> cached_chunks ) {
986
997
heap -> cached_chunks_count -- ;
987
998
chunk = heap -> cached_chunks ;
988
999
heap -> cached_chunks = chunk -> next ;
989
1000
} else {
990
1001
#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 ) {
993
1006
#if ZEND_DEBUG
994
1007
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 );
995
1008
#else
@@ -1002,14 +1015,19 @@ static void *zend_mm_alloc_pages(zend_mm_heap *heap, int pages_count ZEND_FILE_L
1002
1015
chunk = (zend_mm_chunk * )zend_mm_chunk_alloc (heap , ZEND_MM_CHUNK_SIZE , ZEND_MM_CHUNK_SIZE );
1003
1016
if (UNEXPECTED (chunk == NULL )) {
1004
1017
/* 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 {
1005
1022
#if !ZEND_MM_LIMIT
1006
- zend_mm_safe_error (heap , "Out of memory" );
1023
+ zend_mm_safe_error (heap , "Out of memory" );
1007
1024
#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 );
1009
1026
#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 );
1011
1028
#endif
1012
- return NULL ;
1029
+ return NULL ;
1030
+ }
1013
1031
}
1014
1032
#if ZEND_MM_STAT
1015
1033
do {
@@ -1066,7 +1084,32 @@ static zend_always_inline void *zend_mm_alloc_large(zend_mm_heap *heap, size_t s
1066
1084
return ptr ;
1067
1085
}
1068
1086
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 )
1070
1113
{
1071
1114
chunk -> free_pages += pages_count ;
1072
1115
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
1075
1118
/* this setting may be not accurate */
1076
1119
chunk -> free_tail = page_num ;
1077
1120
}
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 );
1101
1123
}
1102
1124
}
1103
1125
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
+
1104
1131
static zend_always_inline void zend_mm_free_large (zend_mm_heap * heap , zend_mm_chunk * chunk , int page_num , int pages_count )
1105
1132
{
1106
1133
#if ZEND_MM_STAT
@@ -1196,7 +1223,7 @@ static zend_never_inline void *zend_mm_alloc_small_slow(zend_mm_heap *heap, int
1196
1223
if (bin_pages [bin_num ] > 1 ) {
1197
1224
int i = 1 ;
1198
1225
do {
1199
- chunk -> map [page_num + i ] = ZEND_MM_SRUN (bin_num );
1226
+ chunk -> map [page_num + i ] = ZEND_MM_NRUN (bin_num , i );
1200
1227
i ++ ;
1201
1228
} while (i < bin_pages [bin_num ]);
1202
1229
}
@@ -1441,8 +1468,10 @@ static void *zend_mm_realloc_heap(zend_mm_heap *heap, void *ptr, size_t size, si
1441
1468
}
1442
1469
} else /* if (new_size > old_size) */ {
1443
1470
#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 ) {
1446
1475
#if ZEND_DEBUG
1447
1476
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 );
1448
1477
#else
@@ -1665,8 +1694,10 @@ static void *zend_mm_alloc_huge(zend_mm_heap *heap, size_t size ZEND_FILE_LINE_D
1665
1694
void * ptr ;
1666
1695
1667
1696
#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 ) {
1670
1701
#if ZEND_DEBUG
1671
1702
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 );
1672
1703
#else
@@ -1679,14 +1710,19 @@ static void *zend_mm_alloc_huge(zend_mm_heap *heap, size_t size ZEND_FILE_LINE_D
1679
1710
ptr = zend_mm_chunk_alloc (heap , new_size , ZEND_MM_CHUNK_SIZE );
1680
1711
if (UNEXPECTED (ptr == NULL )) {
1681
1712
/* 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 {
1682
1717
#if !ZEND_MM_LIMIT
1683
- zend_mm_safe_error (heap , "Out of memory" );
1718
+ zend_mm_safe_error (heap , "Out of memory" );
1684
1719
#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 );
1686
1721
#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 );
1688
1723
#endif
1689
- return NULL ;
1724
+ return NULL ;
1725
+ }
1690
1726
}
1691
1727
#if ZEND_DEBUG
1692
1728
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)
1783
1819
return heap ;
1784
1820
}
1785
1821
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
+
1786
1935
#if ZEND_DEBUG
1787
1936
/******************/
1788
1937
/* Leak detection */
0 commit comments