Index: libcfa/src/heap.cfa
===================================================================
--- libcfa/src/heap.cfa	(revision d5d3a90b22aaaa0979ed8f696f3909a2c8633243)
+++ libcfa/src/heap.cfa	(revision dd23e667131b7d56bded56b483f9f83d70d702c5)
@@ -10,6 +10,6 @@
 // Created On       : Tue Dec 19 21:58:35 2017
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Mon Aug  3 19:01:22 2020
-// Update Count     : 828
+// Last Modified On : Wed Aug  5 22:21:27 2020
+// Update Count     : 853
 //
 
@@ -80,11 +80,16 @@
 };
 
+size_t default_mmap_start() __attribute__(( weak )) {
+	return __CFA_DEFAULT_MMAP_START__;
+} // default_mmap_start
+
 size_t default_heap_expansion() __attribute__(( weak )) {
 	return __CFA_DEFAULT_HEAP_EXPANSION__;
 } // default_heap_expansion
 
-size_t default_mmap_start() __attribute__(( weak )) {
-	return __CFA_DEFAULT_MMAP_START__;
-} // default_mmap_start
+bool default_heap_exhausted() __attribute__(( weak )) {	// find and free some storage
+	// Returning false prints "out of heap memory" message and aborts.
+	return false;
+} // default_heap_exhausted
 
 
@@ -473,4 +478,5 @@
 } // headers
 
+#define NO_MEMORY_MSG "no heap memory available for allocating %zd new bytes."
 
 static inline void * extend( size_t size ) with( heapManager ) {
@@ -481,10 +487,13 @@
 
 		size_t increase = libCeiling( size > heapExpand ? size : heapExpand, libAlign() );
-		if ( sbrk( increase ) == (void *)-1 ) {
+	  Succeed:
+		{
+			if ( sbrk( increase ) != (void *)-1 ) break Succeed; // succeed ?
+			if ( default_heap_exhausted() )	{			// try fix
+				if ( sbrk( increase ) != (void *)-1 ) break Succeed; // succeed ?
+			} // if
 			unlock( extlock );
-			errno = ENOMEM;
-//			return 0p;
-			abort( "no memory" );
-		} // if
+			abort( NO_MEMORY_MSG, size );				// give up
+		}
 		#ifdef __STATISTICS__
 		sbrk_calls += 1;
@@ -554,5 +563,4 @@
 
 			block = (HeapManager.Storage *)extend( tsize );	// mutual exclusion on call
-//	if ( unlikely( block == 0p ) ) return 0p;
 		#if BUCKETLOCK == SPINLOCK
 		} else {
@@ -570,9 +578,16 @@
 		__atomic_add_fetch( &mmap_storage, tsize, __ATOMIC_SEQ_CST );
 		#endif // __STATISTICS__
-		block = (HeapManager.Storage *)mmap( 0, tsize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, mmapFd, 0 );
-		if ( block == (HeapManager.Storage *)MAP_FAILED ) {
+	  Succeed:
+		{
+			block = (HeapManager.Storage *)mmap( 0, tsize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, mmapFd, 0 );
+			if ( block != (HeapManager.Storage *)MAP_FAILED ) break Succeed; // succeed ?
+			if ( errno == ENOMEM && default_heap_exhausted() ) { // out of memory and try again ?
+				block = (HeapManager.Storage *)mmap( 0, tsize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, mmapFd, 0 );
+				if ( block != (HeapManager.Storage *)MAP_FAILED ) break Succeed; // succeed ?
+			} // if
+			if ( errno == ENOMEM ) abort( NO_MEMORY_MSG, tsize );
 			// Do not call strerror( errno ) as it may call malloc.
 			abort( "(HeapManager &)0x%p.doMalloc() : internal error, mmap failure, size:%zu error:%d.", &heapManager, tsize, errno );
-		} // if
+		}
 		#ifdef __CFA_DEBUG__
 		// Set new memory to garbage so subsequent uninitialized usages might fail.
@@ -751,5 +766,5 @@
 static inline void * mallocNoStats( size_t size ) {		// necessary for malloc statistics
 	verify( heapManager.heapBegin != 0 );				// called before memory_startup ?
-  if ( size == 0 ) return 0p;							// 0 BYTE ALLOCATION RETURNS NULL POINTER
+  if ( unlikely( size ) == 0 ) return 0p;				// 0 BYTE ALLOCATION RETURNS NULL POINTER
 
 #if __SIZEOF_POINTER__ == 8
@@ -762,5 +777,5 @@
 static inline void * callocNoStats( size_t dim, size_t elemSize ) {
 	size_t size = dim * elemSize;
-  if ( size == 0 ) return 0p;							// 0 BYTE ALLOCATION RETURNS NULL POINTER
+  if ( unlikely( size ) == 0 ) return 0p;				// 0 BYTE ALLOCATION RETURNS NULL POINTER
 	char * addr = (char *)mallocNoStats( size );
 
@@ -773,4 +788,5 @@
 		headers( "calloc", addr, header, freeElem, bsize, alignment );
 	#ifndef __CFA_DEBUG__
+
 	// Mapped storage is zero filled, but in debug mode mapped memory is scrubbed in doMalloc, so it has to be reset to zero.
 	if ( ! mapped )
@@ -786,5 +802,5 @@
 
 static inline void * memalignNoStats( size_t alignment, size_t size ) { // necessary for malloc statistics
-  if ( size == 0 ) return 0p;							// 0 BYTE ALLOCATION RETURNS NULL POINTER
+  if ( unlikely( size ) == 0 ) return 0p;				// 0 BYTE ALLOCATION RETURNS NULL POINTER
 
 	#ifdef __CFA_DEBUG__
@@ -825,5 +841,5 @@
 static inline void * cmemalignNoStats( size_t alignment, size_t dim, size_t elemSize ) {
 	size_t size = dim * elemSize;
-  if ( size == 0 ) return 0p;							// 0 BYTE ALLOCATION RETURNS NULL POINTER
+  if ( unlikely( size ) == 0 ) return 0p;				// 0 BYTE ALLOCATION RETURNS NULL POINTER
 	char * addr = (char *)memalignNoStats( alignment, size );
 
@@ -831,10 +847,16 @@
 	HeapManager.FreeHeader * freeElem;
 	size_t bsize;
-	bool mapped __attribute__(( unused )) = headers( "cmemalign", addr, header, freeElem, bsize, alignment );
 	#ifndef __CFA_DEBUG__
+	bool mapped =
+	#endif // __CFA_DEBUG__
+		headers( "cmemalign", addr, header, freeElem, bsize, alignment );
+	#ifndef __CFA_DEBUG__
+
 	// Mapped storage is zero filled, but in debug mode mapped memory is scrubbed in doMalloc, so it has to be reset to zero.
 	if ( ! mapped )
 	#endif // __CFA_DEBUG__
-		memset( addr, '\0', dataStorage( bsize, addr, header ) ); // set to zeros
+		// <-------0000000000000000000000000000UUUUUUUUUUUUUUUUUUUUUUUUU> bsize (bucket size) U => undefined
+		// `-header`-addr                      `-size
+		memset( addr, '\0', size );						// set to zeros
 
 	header->kind.real.blockSize |= 2;					// mark as zero filled
@@ -1135,5 +1157,5 @@
 
 		headers( "malloc_usable_size", addr, header, freeElem, bsize, alignment );
-		return dataStorage( bsize, addr, header );	// data storage in bucket
+		return dataStorage( bsize, addr, header );		// data storage in bucket
 	} // malloc_usable_size
 
@@ -1233,14 +1255,12 @@
 		if ( size <= odsize && odsize <= size * 2 ) {	// allow 50% wasted storage for smaller size
 			header->kind.real.blockSize &= -2;			// turn off 0 fill
-			if ( size != odsize ) header->kind.real.size = size; // reset allocation size
+			header->kind.real.size = size;				// reset allocation size
 			return oaddr;
 		} // if
 	} // if
 
-	// change size
-
-	void * naddr = memalignNoStats( nalign, size );		// create new aligned area
+	// change size, DO NOT preserve STICKY PROPERTIES.
 	free( oaddr );
-	return naddr;
+	return memalignNoStats( nalign, size );				// create new aligned area
 } // resize
 
@@ -1272,17 +1292,12 @@
 	#endif // __STATISTICS__
 
-	size_t osize = header->kind.real.size;			// old allocation size
-	bool ozfill = (header->kind.real.blockSize & 2) != 0; // old allocation zero filled
-
 	// If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned.
   if ( unlikely( size == 0 ) ) { free( oaddr ); return 0p; } // special cases
   if ( unlikely( oaddr == 0p ) ) return memalignNoStats( nalign, size );
 
-	void * naddr;
-	if ( unlikely( header->kind.real.blockSize & 2 ) ) { // previous request zero fill
-		naddr = cmemalignNoStats( nalign, 1, size );	// create new aligned area
-	} else {
-		naddr = memalignNoStats( nalign, size );		// create new aligned area
-	} // if
+	size_t osize = header->kind.real.size;				// old allocation size
+	bool ozfill = (header->kind.real.blockSize & 2) != 0; // old allocation zero filled
+
+	void * naddr = memalignNoStats( nalign, size );		// create new aligned area
 
 	headers( "realloc", naddr, header, freeElem, bsize, oalign );
