-/* src/mm/memory.c -
+/* src/mm/memory.c - memory management
- Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel,
- C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
- E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
- J. Wenninger, Institut f. Computersprachen - TU Wien
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
This file is part of CACAO.
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
- Contact: cacao@cacaojvm.org
-
- Authors: Reinhard Grafl
-
- Changes: Christian Thalinger
- Edwin Steiner
-
- $Id: memory.c 4908 2006-05-12 16:49:50Z edwin $
-
*/
+#include "config.h"
+
#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/mman.h>
+#include <stdint.h>
#if defined(__DARWIN__)
-/* If we compile with -ansi on darwin, <sys/types.h> is not included. So */
-/* let's do it here. */
+/* If we compile with -ansi on darwin, <sys/types.h> is not
+ included. So let's do it here. */
# include <sys/types.h>
#endif
-#include "config.h"
#include "vm/types.h"
#include "arch.h"
#include "mm/memory.h"
-#include "native/native.h"
-#if defined(USE_THREADS)
-# if defined(NATIVE_THREADS)
-# include "threads/native/threads.h"
-# else
-# include "threads/green/threads.h"
-# endif
-#endif
+#include "native/native.hpp"
-#include "toolbox/logging.h"
-#include "vm/exceptions.h"
-#include "vm/global.h"
-#include "vm/options.h"
-#include "vm/statistics.h"
-#include "vm/stringlocal.h"
-
-
-/*******************************************************************************
-
- This structure is used for dump memory allocation if cacao
- runs without threads.
-
-*******************************************************************************/
-
-#if !defined(USE_THREADS) || (defined(USE_THREADS) && !defined(NATIVE_THREADS))
-static dumpinfo _no_threads_dumpinfo;
-#endif
-
-#if defined(USE_THREADS) && defined(NATIVE_THREADS)
-#define DUMPINFO &((threadobject *) THREADOBJECT)->dumpinfo
-#else
-#define DUMPINFO &_no_threads_dumpinfo
-#endif
+#include "threads/lock.hpp"
+#include "threads/thread.hpp"
+#include "toolbox/logging.h"
-/* global code memory variables ***********************************************/
+#include "vm/global.h"
+#include "vm/string.hpp"
+#include "vm/vm.hpp"
-#define DEFAULT_CODEMEM_SIZE 128 * 1024 /* defaulting to 128kB */
+#include "vm/options.h"
+#include "vm/os.hpp"
-#if defined(USE_THREADS)
-static java_objectheader *codememlock = NULL;
+#if defined(ENABLE_STATISTICS)
+# include "vm/statistics.h"
#endif
-static int codememsize = 0;
-static void *codememptr = NULL;
-/* memory_init *****************************************************************
+/* memory_mprotect *************************************************************
- Initialize the memory subsystem.
+ Convenience function for mprotect. This function also does error
+ checking.
*******************************************************************************/
-bool memory_init(void)
+void memory_mprotect(void *addr, size_t len, int prot)
{
-#if defined(USE_THREADS)
- codememlock = NEW(java_objectheader);
-
-# if defined(NATIVE_THREADS)
- lock_init_object_lock(codememlock);
-# endif
-#endif /* defined(USE_THREADS) */
-
- /* everything's ok */
-
- return true;
+ if (os_mprotect(addr, len, prot) != 0)
+ vm_abort_errno("memory_mprotect: os_mprotect failed");
}
*******************************************************************************/
-static void *memory_checked_alloc(s4 size)
+void *memory_checked_alloc(size_t size)
{
/* always allocate memory zeroed out */
- void *p = calloc(size, 1);
+ void *p = os_calloc(size, 1);
- if (!p)
- exceptions_throw_outofmemory_exit();
+ if (p == NULL)
+ vm_abort("memory_checked_alloc: calloc failed: out of memory");
return p;
}
-/* memory_cnew *****************************************************************
-
- Allocates memory from the heap, aligns it to architecutres PAGESIZE
- and make the memory read-, write-, and executeable.
-
-*******************************************************************************/
-
-void *memory_cnew(s4 size)
+void *mem_alloc(int32_t size)
{
- void *p;
- int pagesize;
-
-#if defined(USE_THREADS)
- builtin_monitorenter(codememlock);
-#endif
-
- size = ALIGN(size, ALIGNSIZE);
-
- /* check if enough memory is available */
-
- if (size > codememsize) {
- /* set default code size */
-
- codememsize = DEFAULT_CODEMEM_SIZE;
+ void *m;
- /* do we need more? */
-
- if (size > codememsize)
- codememsize = size;
-
- /* get the pagesize of this architecture */
-
- pagesize = getpagesize();
-
- /* allocate normal heap memory */
-
- if ((p = memory_checked_alloc(codememsize + pagesize - 1)) == NULL)
- return NULL;
-
-#if defined(ENABLE_STATISTICS)
- if (opt_stat) {
- codememusage += codememsize + pagesize - 1;
-
- if (codememusage > maxcodememusage)
- maxcodememusage = codememusage;
- }
-#endif
-
- /* align the memory allocated to a multiple of PAGESIZE,
- mprotect requires this */
-
- p = (void *) (((ptrint) p + pagesize - 1) & ~(pagesize - 1));
-
- /* make the memory read-, write-, and executeable */
-
- if (mprotect(p, codememsize, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
- throw_cacao_exception_exit(string_java_lang_InternalError,
- strerror(errno));
-
- /* set global code memory pointer */
-
- codememptr = p;
- }
-
- /* get a memory chunk of the allocated memory */
-
- p = codememptr;
- codememptr = (void *) ((ptrint) codememptr + size);
- codememsize -= size;
-
-#if defined(USE_THREADS)
- builtin_monitorexit(codememlock);
-#endif
-
- return p;
-}
-
-
-void *mem_alloc(s4 size)
-{
if (size == 0)
return NULL;
}
#endif
- return memory_checked_alloc(size);
+ m = memory_checked_alloc(size);
+
+#if defined(ENABLE_MEMCHECK)
+ /* XXX we would like to poison the memory, but callers rely on */
+ /* the zeroing. This should change sooner or later. */
+ /* memset(m, MEMORY_CLEAR_BYTE, size); */
+#endif
+
+ return m;
}
-void *mem_realloc(void *src, s4 len1, s4 len2)
+void *mem_realloc(void *src, int32_t len1, int32_t len2)
{
void *dst;
- if (!src) {
- if (len1 != 0) {
- log_text("reallocating memoryblock with address NULL, length != 0");
- assert(0);
- }
- }
+ /* prevent compiler warnings */
+
+ dst = NULL;
+
+ if (src == NULL)
+ if (len1 != 0)
+ vm_abort("mem_realloc: reallocating memoryblock with address NULL, length != 0");
#if defined(ENABLE_STATISTICS)
if (opt_stat)
memoryusage = (memoryusage - len1) + len2;
#endif
+#if defined(ENABLE_MEMCHECK)
+ if (len2 < len1)
+ os_memset((u1*)dst + len2, MEMORY_CLEAR_BYTE, len1 - len2);
+#endif
+
dst = realloc(src, len2);
- if (!dst)
- exceptions_throw_outofmemory_exit();
+ if (dst == NULL)
+ vm_abort("mem_realloc: realloc failed: out of memory");
+
+#if defined(ENABLE_MEMCHECK)
+ if (len2 > len1)
+ os_memset((u1*)dst + len1, MEMORY_CLEAR_BYTE, len2 - len1);
+#endif
return dst;
}
-void mem_free(void *m, s4 size)
+void mem_free(void *m, int32_t size)
{
if (!m) {
if (size == 0)
memoryusage -= size;
#endif
- free(m);
-}
-
-
-/* dump_alloc ******************************************************************
-
- Allocate memory in the dump area.
-
- IN:
- size.........size of block to allocate, in bytes
- may be zero, in which case NULL is returned
-
- RETURN VALUE:
- pointer to allocated memory, or
- NULL iff `size` was zero
-
- ERROR HANDLING:
- XXX This function uses `memory_checked_alloc`, which *exits* if no
- memory could be allocated.
-
- THREADS:
- dump_alloc is thread safe. Each thread has its own dump memory area.
-
- dump_alloc is a fast allocator suitable for scratch memory that can be
- collectively freed when the current activity (eg. compiling) is done.
-
- You cannot selectively free dump memory. Before you start allocating it,
- you remember the current size returned by `dump_size`. Later, when you no
- longer need the memory, call `dump_release` with the remembered size and
- all dump memory allocated since the call to `dump_size` will be freed.
-
-*******************************************************************************/
-
-void *dump_alloc(s4 size)
-{
-#if defined(DISABLE_DUMP)
-
- /* use malloc memory for dump memory (for debugging only!) */
-
- return mem_alloc(size);
-
-#else /* !defined(DISABLE_DUMP) */
-
- void *m;
- dumpinfo *di;
-
- /* If no threads are used, the dumpinfo structure is a static structure */
- /* defined at the top of this file. */
-
- di = DUMPINFO;
-
- if (size == 0)
- return NULL;
-
- size = ALIGN(size, ALIGNSIZE);
-
- if (di->useddumpsize + size > di->allocateddumpsize) {
- dumpblock *newdumpblock;
- s4 newdumpblocksize;
-
- /* allocate a new dumplist structure */
-
- newdumpblock = memory_checked_alloc(sizeof(dumpblock));
-
- /* If requested size is greater than the default, make the new dump */
- /* block as big as the size requested. Else use the default size. */
-
- if (size > DUMPBLOCKSIZE) {
- newdumpblocksize = size;
-
- } else {
- newdumpblocksize = DUMPBLOCKSIZE;
- }
-
- /* allocate dumpblock memory */
-
- newdumpblock->dumpmem = memory_checked_alloc(newdumpblocksize);
-
- newdumpblock->prev = di->currentdumpblock;
- newdumpblock->size = newdumpblocksize;
- di->currentdumpblock = newdumpblock;
-
- /* Used dump size is previously allocated dump size, because the */
- /* remaining free memory of the previous dump block cannot be used. */
-
- di->useddumpsize = di->allocateddumpsize;
-
- /* increase the allocated dump size by the size of the new dump block */
-
- di->allocateddumpsize += newdumpblocksize;
-
-#if defined(ENABLE_STATISTICS)
- /* the amount of globally allocated dump memory (thread save) */
-
- if (opt_stat)
- globalallocateddumpsize += newdumpblocksize;
-#endif
- }
-
- /* current dump block base address + the size of the current dump block - */
- /* the size of the unused memory = new start address */
-
- m = di->currentdumpblock->dumpmem + di->currentdumpblock->size -
- (di->allocateddumpsize - di->useddumpsize);
-
- /* increase used dump size by the allocated memory size */
-
- di->useddumpsize += size;
-
-#if defined(ENABLE_STATISTICS)
- if (opt_stat)
- if (di->useddumpsize > maxdumpsize)
- maxdumpsize = di->useddumpsize;
+#if defined(ENABLE_MEMCHECK)
+ /* destroy the contents */
+ os_memset(m, MEMORY_CLEAR_BYTE, size);
#endif
- return m;
-
-#endif /* defined(DISABLE_DUMP) */
-}
-
-
-/* dump_realloc ****************************************************************
-
- Stupid realloc implementation for dump memory. Avoid, if possible.
-
-*******************************************************************************/
-
-void *dump_realloc(void *src, s4 len1, s4 len2)
-{
-#if defined(DISABLE_DUMP)
- /* use malloc memory for dump memory (for debugging only!) */
-
- return mem_realloc(src, len1, len2);
-#else
- void *dst = dump_alloc(len2);
-
- memcpy(dst, src, len1);
-
- return dst;
-#endif
+ os_free(m);
}
-/* dump_release ****************************************************************
-
- Release dump memory above the given size.
+/* memory_thread ***************************************************************
- IN:
- size........All dump memory above this mark will be freed. Usually
- `size` will be the return value of a `dump_size` call
- made earlier.
-
- ERROR HANDLING:
- XXX If the given size is invalid, this function *exits* with an
- error message.
-
- See `dump_alloc`.
+ Prints regularly memory statistics.
*******************************************************************************/
-void dump_release(s4 size)
+#if defined(ENABLE_THREADS) && !defined(NDEBUG)
+static void memory_thread(void)
{
-#if defined(DISABLE_DUMP)
-
- /* use malloc memory for dump memory (for debugging only!) */
+ int32_t seconds;
- /* do nothing */
+ /* Prevent compiler warning. */
-#else /* !defined(DISABLE_DUMP) */
+ seconds = 1;
- dumpinfo *di;
+ /* If both arguments are specified, use the value of
+ ProfileMemoryUsage. */
- /* If no threads are used, the dumpinfo structure is a static structure */
- /* defined at the top of this file. */
+ if (opt_ProfileGCMemoryUsage)
+ seconds = opt_ProfileGCMemoryUsage;
- di = DUMPINFO;
+ if (opt_ProfileMemoryUsage)
+ seconds = opt_ProfileMemoryUsage;
- if (size < 0 || size > di->useddumpsize)
- throw_cacao_exception_exit(string_java_lang_InternalError,
- "Illegal dump release size %d", size);
+ while (true) {
+ /* sleep thread */
- /* reset the used dump size to the size specified */
+ threads_sleep(seconds * 1000, 0);
- di->useddumpsize = size;
+# if defined(ENABLE_STATISTICS)
+ /* Print current date and time (only when we print to the
+ stdout). */
- while (di->currentdumpblock && di->allocateddumpsize - di->currentdumpblock->size >= di->useddumpsize) {
- dumpblock *tmp = di->currentdumpblock;
+ if (!opt_ProfileMemoryUsageGNUPlot)
+ statistics_print_date();
-#if 0
- /* XXX TWISTI: can someone explain this to me? */
-#ifdef TRACECALLARGS
- /* Keep the first dumpblock if we don't free memory. Otherwise
- * a new dumpblock is allocated each time and we run out of
- * memory.
- */
- if (!oldtop->prev) break;
-#endif
-#endif
-
- di->allocateddumpsize -= tmp->size;
- di->currentdumpblock = tmp->prev;
-
-#if defined(ENABLE_STATISTICS)
- /* the amount of globally allocated dump memory (thread save) */
+ /* print memory usage */
- if (opt_stat)
- globalallocateddumpsize -= tmp->size;
-#endif
+ if (opt_ProfileMemoryUsage)
+ statistics_print_memory_usage();
- /* release the dump memory and the dumpinfo structure */
+ /* print GC memory usage */
- free(tmp->dumpmem);
- free(tmp);
+ if (opt_ProfileGCMemoryUsage)
+ statistics_print_gc_memory_usage();
+# endif
}
-
-#endif /* defined(DISABLE_DUMP) */
}
+#endif
-/* dump_size *******************************************************************
+/* memory_start_thread *********************************************************
- Return the current size of the dump memory area. See `dump_alloc`.
+ Starts the memory profiling thread.
*******************************************************************************/
-s4 dump_size(void)
+#if defined(ENABLE_THREADS) && !defined(NDEBUG)
+bool memory_start_thread(void)
{
-#if defined(DISABLE_DUMP)
- /* use malloc memory for dump memory (for debugging only!) */
-
- return 0;
+ utf *name;
-#else /* !defined(DISABLE_DUMP) */
+ name = utf_new_char("Memory Profiler");
- dumpinfo *di;
+ /* start the memory profiling thread */
- /* If no threads are used, the dumpinfo structure is a static structure */
- /* defined at the top of this file. */
+ if (!threads_thread_start_internal(name, memory_thread))
+ return false;
- di = DUMPINFO;
-
- if (!di)
- return 0;
-
- return di->useddumpsize;
+ /* everything's ok */
-#endif /* defined(DISABLE_DUMP) */
+ return true;
}
+#endif
/*