Commit 3fb074b5 authored by Ian Lynagh's avatar Ian Lynagh

Return memory to the OS; trac #698

parent 429dc9a0
......@@ -12,17 +12,17 @@
#ifndef RTS_STORAGE_MBLOCK_H
#define RTS_STORAGE_MBLOCK_H
extern lnat peak_mblocks_allocated;
extern lnat mblocks_allocated;
extern void initMBlocks(void);
extern void * getMBlock(void);
extern void * getMBlocks(nat n);
extern void freeMBlocks(void *addr, nat n);
extern void freeAllMBlocks(void);
#ifdef DEBUG
extern void *getFirstMBlock(void);
extern void *getNextMBlock(void *mblock);
#endif
#ifdef THREADED_RTS
// needed for HEAP_ALLOCED below
......
......@@ -741,7 +741,7 @@ stat_exit(int alloc)
AvgResidency*sizeof(W_)/ResidencySamples,
MaxResidency*sizeof(W_),
ResidencySamples,
(unsigned long)(mblocks_allocated * MBLOCK_SIZE / (1024L * 1024L)),
(unsigned long)(peak_mblocks_allocated * MBLOCK_SIZE / (1024L * 1024L)),
TICK_TO_DBL(InitUserTime), TICK_TO_DBL(InitElapsedTime),
TICK_TO_DBL(MutUserTime), TICK_TO_DBL(MutElapsedTime),
TICK_TO_DBL(GC_tot_time), TICK_TO_DBL(GCe_tot_time));
......
......@@ -37,16 +37,7 @@
#include <mach/vm_map.h>
#endif
/* keep track of maps returned by my_mmap */
typedef struct _map_rec {
char* base; /* base addr */
int size; /* map size */
struct _map_rec* next; /* next pointer */
} map_rec;
static caddr_t next_request = 0;
static map_rec* mmap_rec = NULL;
void osMemInit(void)
{
......@@ -187,7 +178,6 @@ osGetMBlocks(nat n)
{
caddr_t ret;
lnat size = MBLOCK_SIZE * (lnat)n;
map_rec* rec;
if (next_request == 0) {
// use gen_map_mblocks the first time.
......@@ -209,11 +199,6 @@ osGetMBlocks(nat n)
ret = gen_map_mblocks(size);
}
}
rec = (map_rec*)stgMallocBytes(sizeof(map_rec),"OSMem: osGetMBlocks");
rec->size = size;
rec->base = ret;
rec->next = mmap_rec;
mmap_rec = rec;
// Next time, we'll try to allocate right after the block we just got.
// ToDo: check that we haven't already grabbed the memory at next_request
next_request = ret + size;
......@@ -221,18 +206,19 @@ osGetMBlocks(nat n)
return ret;
}
void osFreeAllMBlocks(void)
void osFreeMBlocks(char *addr, nat n)
{
map_rec* tmp = mmap_rec;
map_rec* next = NULL;
munmap(addr, n * MBLOCK_SIZE);
}
for(; tmp!=NULL;) {
if(munmap(tmp->base,tmp->size))
barf("osFreeAllMBlocks: munmap failed!");
void osFreeAllMBlocks(void)
{
void *mblock;
next = tmp->next;
stgFree(tmp);
tmp = next;
for (mblock = getFirstMBlock();
mblock != NULL;
mblock = getNextMBlock(mblock)) {
munmap(mblock, MBLOCK_SIZE);
}
}
......
......@@ -21,6 +21,7 @@
#include "Storage.h"
#include "RtsUtils.h"
#include "BlockAlloc.h"
#include "OSMem.h"
#include <string.h>
......@@ -671,6 +672,39 @@ countAllocdBlocks(bdescr *bd)
return n;
}
void returnMemoryToOS(nat n /* megablocks */)
{
static bdescr *bd;
nat size;
bd = free_mblock_list;
while ((n > 0) && (bd != NULL)) {
size = BLOCKS_TO_MBLOCKS(bd->blocks);
if (size > n) {
nat newSize = size - n;
char *freeAddr = MBLOCK_ROUND_DOWN(bd->start);
freeAddr += newSize * MBLOCK_SIZE;
bd->blocks = MBLOCK_GROUP_BLOCKS(newSize);
freeMBlocks(freeAddr, n);
n = 0;
}
else {
char *freeAddr = MBLOCK_ROUND_DOWN(bd->start);
n -= size;
bd = bd->link;
freeMBlocks(freeAddr, size);
}
}
free_mblock_list = bd;
IF_DEBUG(gc,
if (n != 0) {
debugBelch("Wanted to free %d more MBlocks than are freeable\n",
n);
}
);
}
/* -----------------------------------------------------------------------------
Debugging
-------------------------------------------------------------------------- */
......
......@@ -15,6 +15,7 @@
extern nat countBlocks (bdescr *bd);
extern nat countAllocdBlocks (bdescr *bd);
extern void returnMemoryToOS(nat n);
#ifdef DEBUG
void checkFreeListSanity(void);
......
......@@ -746,6 +746,19 @@ SET_GCT(gc_threads[0]);
scheduleFinalizers(cap, old_weak_ptr_list);
ACQUIRE_SM_LOCK;
if (major_gc) {
nat need, got;
need = BLOCKS_TO_MBLOCKS(n_alloc_blocks);
got = mblocks_allocated;
/* If the amount of data remains constant, next major GC we'll
require (F+1)*need. We leave (F+2)*need in order to reduce
repeated deallocation and reallocation. */
need = (RtsFlags.GcFlags.oldGenFactor + 2) * need;
if (got > need) {
returnMemoryToOS(got - need);
}
}
// check sanity after GC
IF_DEBUG(sanity, checkSanity(rtsTrue));
......
......@@ -18,6 +18,7 @@
#include <string.h>
lnat peak_mblocks_allocated = 0;
lnat mblocks_allocated = 0;
lnat mpc_misses = 0;
......@@ -29,9 +30,9 @@ lnat mpc_misses = 0;
StgWord8 mblock_map[MBLOCK_MAP_SIZE]; // initially all zeros
static void
markHeapAlloced(void *p)
setHeapAlloced(void *p, StgWord8 i)
{
mblock_map[MBLOCK_MAP_ENTRY(p)] = 1;
mblock_map[MBLOCK_MAP_ENTRY(p)] = i;
}
#elif SIZEOF_VOID_P == 8
......@@ -81,7 +82,7 @@ StgBool HEAP_ALLOCED_miss(StgWord mblock, void *p)
}
static void
markHeapAlloced(void *p)
setHeapAlloced(void *p, StgWord8 i)
{
MBlockMap *map = findMBlockMap(p);
if(map == NULL)
......@@ -95,7 +96,7 @@ markHeapAlloced(void *p)
map->addrHigh32 = (StgWord32) (((StgWord)p) >> 32);
}
map->lines[MBLOCK_MAP_LINE(p)] = 1;
map->lines[MBLOCK_MAP_LINE(p)] = i;
{
StgWord mblock;
......@@ -103,19 +104,22 @@ markHeapAlloced(void *p)
mblock = (StgWord)p >> MBLOCK_SHIFT;
entry_no = mblock & (MBC_ENTRIES-1);
mblock_cache[entry_no] = (mblock << 1) + 1;
mblock_cache[entry_no] = (mblock << 1) + i;
}
}
#endif
/* ----------------------------------------------------------------------------
Debugging code for traversing the allocated MBlocks
This is used for searching for lost blocks when a memory leak is
detected; see Blocks.c:findUnmarkedBlock().
------------------------------------------------------------------------ */
static void
markHeapAlloced(void *p)
{
setHeapAlloced(p, 1);
}
#ifdef DEBUG
static void
markHeapUnalloced(void *p)
{
setHeapAlloced(p, 0);
}
#if SIZEOF_VOID_P == 4
......@@ -207,8 +211,6 @@ void * getFirstMBlock(void)
#endif // SIZEOF_VOID_P
#endif // DEBUG
/* -----------------------------------------------------------------------------
Allocate new mblock(s)
-------------------------------------------------------------------------- */
......@@ -238,13 +240,31 @@ getMBlocks(nat n)
}
mblocks_allocated += n;
peak_mblocks_allocated = stg_max(peak_mblocks_allocated, mblocks_allocated);
return ret;
}
void
freeMBlocks(void *addr, nat n)
{
nat i;
debugTrace(DEBUG_gc, "freeing %d megablock(s) at %p",n,addr);
mblocks_allocated -= n;
for (i = 0; i < n; i++) {
markHeapUnalloced( (StgWord8*)addr + i * MBLOCK_SIZE );
}
osFreeMBlocks(addr, n);
}
void
freeAllMBlocks(void)
{
debugTrace(DEBUG_gc, "freeing all megablocks");
osFreeAllMBlocks();
}
......
......@@ -13,6 +13,7 @@
void osMemInit(void);
void *osGetMBlocks(nat n);
void osFreeMBlocks(char *addr, nat n);
void osFreeAllMBlocks(void);
lnat getPageSize (void);
void setExecutable (void *p, lnat len, rtsBool exec);
......
......@@ -203,6 +203,42 @@ osGetMBlocks(nat n) {
return ret;
}
void osFreeMBlocks(char *addr, nat n)
{
alloc_rec *p;
lnat nBytes = (lnat)n * MBLOCK_SIZE;
insertFree(addr, nBytes);
p = allocs;
while ((p != NULL) && (addr >= (p->base + p->size))) {
p = p->next;
}
while (nBytes > 0) {
if ((p == NULL) || (p->base > addr)) {
errorBelch("Memory to be freed isn't allocated\n");
stg_exit(EXIT_FAILURE);
}
if (p->base + p->size >= addr + nBytes) {
if (!VirtualFree(addr, nBytes, MEM_DECOMMIT)) {
sysErrorBelch("osFreeMBlocks: VirtualFree MEM_DECOMMIT failed");
stg_exit(EXIT_FAILURE);
}
nBytes = 0;
}
else {
lnat bytesToFree = p->base + p->size - addr;
if (!VirtualFree(addr, bytesToFree, MEM_DECOMMIT)) {
sysErrorBelch("osFreeMBlocks: VirtualFree MEM_DECOMMIT failed");
stg_exit(EXIT_FAILURE);
}
addr += bytesToFree;
nBytes -= bytesToFree;
p = p->next;
}
}
}
void
osFreeAllMBlocks(void)
{
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment