/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * This source file is part of SableVM. * * * * See the file "LICENSE" for the copyright information and for * * the terms and conditions for copying, distribution and * * modification of this source file. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if defined (_SABLEVM_COPYING_GC) #if defined (_SABLEVM_GC_STATISTICS) svm_static void _svmf_print_gc_stat (_svmt_JNIEnv *env) { _svmt_heap *heap = &(env->vm->heap); _svmf_printf (env, stderr,"\n\n\nCOLLECTOR STATISTICS\n"); _svmf_printf (env, stderr, "\ntotal time : %ld s", heap->gc_total_secs); _svmf_printf (env, stderr, " %ld us", heap->gc_total_usecs); _svmf_printf (env, stderr, "\ncollections : %d", heap->gc_collections); if (heap->gc_collections > 0) { _svmf_printf (env, stderr, "\ntotal time / collections : %.2f us", (float) (heap->gc_total_secs * 1000000 + heap->gc_total_usecs) / (float) heap->gc_collections); _svmf_printf (env, stderr, "\nbytes copied : %ld", heap->gc_bytes_copied); _svmf_printf (env, stderr, "\nobjects copied : %ld", heap->gc_objects_copied); _svmf_printf (env, stderr, "\nbytes / objects : %.2f", (float) heap->gc_bytes_copied / (float) heap->gc_objects_copied); } _svmf_printf (env, stderr,"\n\n\nALLOCATOR STATISTICS\n"); _svmf_printf (env, stderr, "\nallocated bytes : %ld", heap->al_bytes_allocated); _svmf_printf (env, stderr, "\nallocated objects : %ld", heap->al_objects_allocated); _svmf_printf (env, stderr, "\nbytes / objects : %.2lf", (float) heap->al_bytes_allocated / (float) heap->al_objects_allocated); _svmf_printf (env, stderr,"\n\n\nHEAP SETUP\n"); _svmf_printf (env, stderr, "\nstart size : %ld", (char *) heap->size - ((jint) heap->increment_size) * heap->gc_collections); _svmf_printf (env, stderr, "\nheap size : %ld", heap->size); _svmf_printf (env, stderr, "\nincrement size : %ld", heap->increment_size); _svmf_printf (env, stderr,"\n\n\n"); fflush (NULL); } #endif /* ---------------------------------------------------------------------- _svmf_get_start_offset ---------------------------------------------------------------------- */ inline svm_static size_t _svmf_get_start_offset (_svmt_object_instance *obj) { #if defined (_SABLEVM_BIDIRECTIONAL_OBJECT_LAYOUT) size_t start_offset = 0; /* it's an array. */ if (_svmf_lockword_is_array (obj->lockword)) { assert (obj->vtable->type->is_array); if (_svmf_lockword_get_array_type (obj->lockword) == SVM_TYPE_REFERENCE) { start_offset = _svmf_aligned_size_t (((size_t) ((_svmt_array_instance *) obj)->size) * sizeof (void *)); } return start_offset; } /* it's a normal object. */ assert (!obj->vtable->type->is_array); return obj->vtable->start_offset; #elif defined (_SABLEVM_TRADITIONAL_OBJECT_LAYOUT) return 0; #endif } /* ---------------------------------------------------------------------- _svmf_get_end_offset ---------------------------------------------------------------------- */ inline svm_static size_t _svmf_get_end_offset (_svmt_object_instance *obj) { _svmt_word lockword = obj->lockword; /* it's an array. */ if (_svmf_lockword_is_array (lockword)) { size_t end_offset = 0; size_t size; assert (obj->vtable->type->is_array); size = (size_t) ((_svmt_array_instance *) obj)->size; end_offset = _svmf_aligned_size_t (sizeof (_svmt_array_instance)); switch (_svmf_lockword_get_array_type (lockword)) { case SVM_TYPE_BOOLEAN: { end_offset += (size + 7) / 8; } break; case SVM_TYPE_BYTE: { end_offset += size; } break; case SVM_TYPE_SHORT: { end_offset += size * 2; } break; case SVM_TYPE_CHAR: { end_offset += size * 2; } break; case SVM_TYPE_INT: { end_offset += size * 4; } break; case SVM_TYPE_LONG: { end_offset += size * 8; } break; case SVM_TYPE_FLOAT: { end_offset += size * 4; } break; case SVM_TYPE_DOUBLE: { end_offset += size * 8; } break; case SVM_TYPE_REFERENCE: { #if defined (_SABLEVM_TRADITIONAL_OBJECT_LAYOUT) end_offset += size * sizeof (void *); #endif } break; default: { _svmm_fatal_error ("impossible control flow"); } break; } return _svmf_aligned_size_t (end_offset); } /* it's a normal object. */ assert (!obj->vtable->type->is_array); if (_svmf_lockword_get_hashstate (lockword) != SVM_HASH_MOVED) { return obj->vtable->next_offset_no_hashcode; } else { return obj->vtable->next_offset_with_hashcode; } } /* -------------------------------------------------------------------------------- _svmf_get_available_space -------------------------------------------------------------------------------- */ inline svm_static size_t _svmf_get_free_memory (_svmt_heap *heap) { return (size_t) ((char *) heap->end - (char *) heap->alloc); } /* -------------------------------------------------------------------------------- _svmf_is_free -------------------------------------------------------------------------------- */ inline svm_static jint _svmf_is_free (_svmt_heap *heap, size_t size) { return (jint) (size <= _svmf_get_free_memory (heap)); } /* ---------------------------------------------------------------------- _svmf_get_total_space ---------------------------------------------------------------------- */ inline svm_static jlong _svmf_get_total_space (_svmt_heap *heap) { return ((char *) heap->end) - ((char *) heap->start); } /* ---------------------------------------------------------------------- _svmf_get_max_size ---------------------------------------------------------------------- */ inline svm_static jlong _svmf_get_max_size (_svmt_heap *heap) { if (heap->max_size == 0) return SVM_JLONG_MAX; else return heap->max_size; } /* ---------------------------------------------------------------------- _svmf_get_hashcode_of_space ---------------------------------------------------------------------- */ inline svm_static jint _svmf_get_hashcode_of_space (_svmt_heap *heap, void *space) { return heap->hashcode_base + (size_t) space; } /* ---------------------------------------------------------------------- _svmf_heap_init_defaults ---------------------------------------------------------------------- */ #define SVM_DEFAULT_FRAME_SIZE _svmf_aligned_size_t (8192) #define SVM_DEFAULT_GENERATION_COUNT 3 #define SVM_DEFAULT_GENERATION_SIZE SVM_DEFAULT_FRAME_SIZE * 512 #define SVM_DEFAULT_MIN_SIZE SVM_DEFAULT_GENERATION_SIZE * 7 #define SVM_DEFAULT_MAX_SIZE SVM_DEFAULT_GENERATION_SIZE * 100 svm_static void _svmf_heap_init_defaults (_svmt_heap *heap) { heap->min_size = SVM_DEFAULT_MIN_SIZE; /*SVM_HEAP_DEFAULT_MIN_SIZE; */ heap->max_size = SVM_DEFAULT_MAX_SIZE; /*SVM_HEAP_DEFAULT_MAX_SIZE; */ heap->increment_size = SVM_DEFAULT_GENERATION_SIZE * 3; /*SVM_HEAP_DEFAULT_ALLOCATION_INCREMENT; */ #if defined (_SABLEVM_GC_STATISTICS) /* collector */ heap->gc_total_secs = 0; heap->gc_total_usecs = 0; heap->gc_collections = 0; heap->gc_bytes_copied = 0; heap->gc_objects_copied = 0; /* allocator */ heap->al_bytes_allocated = 0; heap->al_objects_allocated = 0; #endif } /* ---------------------------------------------------------------------- _svmf_heap_init ---------------------------------------------------------------------- */ svm_static jint _svmf_heap_init (_svmt_heap *heap) { /* if (_svmm_validate_min_max_increment (heap->min_size, heap->max_size, heap->increment_size) != JNI_OK) { return JNI_ERR; } */ /* if the heap has a fixed size, allocate both semi-spaces once and for all, if not, allocate one semi-space */ if (heap->increment_size == 0 && (2 * heap->min_size) < heap->min_size) { return JNI_ERR; } if (heap->increment_size == 0) { heap->start = _svmf_malloc (2 * heap->min_size); heap->size = 2 * heap->min_size; if (heap->start == NULL) { return JNI_ERR; } heap->end = ((char *) heap->start) + heap->min_size; heap->alloc = heap->start; heap->hashcode_base = 0 - (size_t) heap->start; heap->next_heap = heap->end; } else { heap->start = _svmf_malloc (heap->min_size); heap->size = heap->min_size; if (heap->start == NULL) { return JNI_ERR; } heap->end = ((char *) heap->start) + heap->min_size; heap->alloc = heap->start; heap->hashcode_base = 0 - (size_t) heap->start; heap->suggested_next_heap_size = heap->min_size; } return JNI_OK; } /* ---------------------------------------------------------------------- _svmf_do_copy_object ---------------------------------------------------------------------- */ inline svm_static _svmt_object_instance * _svmf_do_copy_object (_svmt_object_instance *obj, size_t start_offset, size_t requested_size, size_t instance_size, void **pto_space) { _svmt_object_instance *new_obj; memcpy (*pto_space, (void *) (((char *) obj) - start_offset), instance_size); new_obj = (_svmt_object_instance *) (((char *) *pto_space) + start_offset); *pto_space = ((char *) *pto_space) + requested_size; #if defined (MAGIC) obj->magic[0] = 0; #endif /* Set forward reference. */ *((_svmt_object_instance **) obj) = new_obj; return new_obj; } /* ---------------------------------------------------------------------- _svmf_copy_object ---------------------------------------------------------------------- */ /* This function copies the object pointed to by obj to to_space. obj * may be NULL or pointing to an already copied object. In all cases, * this function returns a pointer to the to_space copy (or NULL). */ inline svm_static _svmt_object_instance * _svmf_copy_object (_svmt_JNIEnv *env, _svmt_object_instance *obj, void **pto_space_tail) { _svmt_word lockword; if (obj == NULL) { return NULL; } assert (((void *) obj) >= env->vm->heap.start && ((void *) obj) < env->vm->heap.alloc); lockword = obj->lockword; /* Lockword is a forward reference. */ if (_svmf_lockword_is_forward_reference (lockword)) { assert (((void *) lockword) < *pto_space_tail); assert (obj->vtable == ((_svmt_object_instance *) lockword)->vtable); return (_svmt_object_instance *) lockword; } #if defined (MAGIC) assert (strcmp (obj->magic, "SableVM") == 0); #endif { size_t end_offset = _svmf_get_end_offset (obj); size_t start_offset = _svmf_get_start_offset (obj); size_t instance_size = start_offset + end_offset; #if defined (_SABLEVM_GC_STATISTICS) env->vm->heap.gc_bytes_copied += instance_size; env->vm->heap.gc_objects_copied++; #endif if (_svmf_lockword_is_array (obj->lockword)) { return _svmf_do_copy_object (obj, start_offset, instance_size, instance_size, pto_space_tail); } else { switch (_svmf_lockword_get_hashstate (obj->lockword)) { case SVM_HASH_NONE: { } case SVM_HASH_MOVED: { return _svmf_do_copy_object (obj, start_offset, instance_size, instance_size, pto_space_tail); } break; case SVM_HASH_NOT_MOVED: { _svmt_object_instance *new_obj; new_obj = _svmf_do_copy_object (obj, start_offset, instance_size + SVM_ALIGNMENT, instance_size, pto_space_tail); if (new_obj != NULL) { _svmm_lockword_set_hashstate (new_obj->lockword, SVM_HASH_MOVED); _svmf_put_INT_field (new_obj, end_offset, (jint) (env->vm->heap.hashcode_base + (size_t) obj)); } return new_obj; } break; default: { _svmm_fatal_error ("impossible control flow"); return NULL; } break; } } } } /* ---------------------------------------------------------------------- _svmf_trace_native_ref_list ---------------------------------------------------------------------- */ inline svm_static void _svmf_trace_native_ref_list (_svmt_JNIEnv *env, _svmt_native_ref *native_list, void **pto_space_tail) { while (native_list != NULL) { native_list->ref = _svmf_copy_object (env, native_list->ref, pto_space_tail); native_list = native_list->next; } } /* ---------------------------------------------------------------------- _svmf_trace_stack ---------------------------------------------------------------------- */ inline svm_static void _svmf_trace_stack (_svmt_JNIEnv *env, _svmt_JNIEnv *thread, void **pto_space_tail) { _svmt_JavaVM *vm = env->vm; _svmt_stack_frame *frame = thread->stack.current_frame; _svmt_method_info *method = frame->method; while (method != &vm->stack_bottom_method) { if (!_svmf_is_set_flag (method->access_flags, SVM_ACC_INTERNAL)) { _svmt_stack_value *locals = (_svmt_stack_value *) (void *) (((char *) frame) - method->frame_info->start_offset); _svmt_gc_map_node *parameters_gc_map = method->parameters_gc_map; jint non_parameter_ref_locals_count = method->frame_info->non_parameter_ref_locals_count; _svmt_stack_value *stack = (_svmt_stack_value *) (void *) (((char *) frame) + _svmv_stack_offset); jint stack_size = frame->stack_size; _svmt_gc_map_node *stack_gc_map = (stack_size == 0) ? NULL : (frame->pc - 1)->stack_gc_map; /* trace pointers embeded in the stack frame */ frame->this = _svmf_copy_object (env, frame->this, pto_space_tail); frame->stack_trace_element = _svmf_copy_object (env, frame->stack_trace_element, pto_space_tail); /* method formal parameters */ { jint i; jint count = parameters_gc_map->size; for (i = 0; i < count; i++) { if (_svmf_get_bit (parameters_gc_map->bits, i)) { locals[i].reference = _svmf_copy_object (env, locals[i].reference, pto_space_tail); } } } /* other ref locals */ { jint i; jint start = method->java_args_count; jint end = start + non_parameter_ref_locals_count; for (i = start; i < end; i++) { locals[i].reference = _svmf_copy_object (env, locals[i].reference, pto_space_tail); } } /* stack */ if (stack_size > 0) { jint i; jint max = _svmf_min_jint (stack_size, stack_gc_map->size); for (i = 0; i < max; i++) { if (_svmf_get_bit (stack_gc_map->bits, i)) { stack[i].reference = _svmf_copy_object (env, stack[i].reference, pto_space_tail); } } } } frame = (_svmt_stack_frame *) (void *) (((char *) frame) - frame->previous_offset); method = frame->method; } } /* ---------------------------------------------------------------------- _svmf_trace_native_references ---------------------------------------------------------------------- */ inline svm_static void _svmf_trace_native_references (_svmt_JNIEnv *env, void **pto_space_tail) { _svmt_JavaVM *vm = env->vm; _svmt_JNIEnv *thread; _svmf_trace_native_ref_list (env, vm->native_globals.list, pto_space_tail); for (thread = vm->threads.user; thread != NULL; thread = thread->next) { _svmf_trace_native_ref_list (env, thread->native_locals.list, pto_space_tail); _svmf_trace_stack (env, thread, pto_space_tail); } for (thread = vm->threads.system; thread != NULL; thread = thread->next) { _svmf_trace_native_ref_list (env, thread->native_locals.list, pto_space_tail); _svmf_trace_stack (env, thread, pto_space_tail); } } /* ---------------------------------------------------------------------- _svmf_trace_heap ---------------------------------------------------------------------- */ inline svm_static void _svmf_trace_heap (_svmt_JNIEnv *env, _svmt_word **pto_space_head, void **pto_space_tail, void *to_space) { _svmt_JavaVM *vm = env->vm; _svmt_object_instance *obj; _svmt_word word; while (((void *) *pto_space_head) < *pto_space_tail) { word = *((_svmt_word *) *pto_space_head); #if defined (_SABLEVM_BIDIRECTIONAL_OBJECT_LAYOUT) if (_svmf_word_is_reference (word)) { obj = ((_svmt_object_instance *) word); assert ((((void *) obj) == NULL) || (((void *) obj) >= vm->heap.start && ((void *) obj) < vm->heap.end) || (((void *) obj) >= to_space && ((void *) obj) < *pto_space_tail)); *((_svmt_object_instance **) ((*pto_space_head)++)) = _svmf_copy_object (env, obj, pto_space_tail); continue; } #endif /* The current word is pointing to an object header. */ assert (!_svmf_word_is_reference (word)); obj = (_svmt_object_instance *) *pto_space_head; #if defined(_SABLEVM_TRADITIONAL_OBJECT_LAYOUT) /* Is it an array ? */ if (_svmf_lockword_is_array (word)) { if (_svmf_lockword_get_array_type (word) == SVM_TYPE_REFERENCE) { /* trace references */ jint i; _svmt_object_instance **elements = (_svmt_object_instance **) (((char *) obj) + _svmf_aligned_size_t (sizeof (_svmt_array_instance))); for (i = 0; i < size; i++) { elements[i] = _svmf_copy_object (env, elements[i], pto_space_tail); } } } /* It's a normal object. */ else { /* Is it info in header ? */ if (_svmf_lockword_object_is_info_in_header (word)) { _svmt_word ref_layout = _svmf_lockword_object_get_ref_layout (word); _svmt_object_instance **references = (_svmt_object_instance **) (((char *) obj) + _svmf_aligned_size_t (sizeof (_svmt_object_instance))); jint i = 0; /* Trace reference fields. */ while (ref_layout != 0) { if ((ref_layout & 0x01) != 0) { references[i] = _svmf_copy_object (env, references[i], pto_space_tail); } i++; ref_layout = ref_layout >> 1; } } /* Info is in vtable. */ else { jint count = obj->vtable->ref_field_count; size_t *offsets = obj->vtable->ref_field_offsets; jint i; /* Trace reference fields. */ for (i = 0; i < count; i++) { _svmt_object_instance **reference = (_svmt_object_instance **) (((char *) obj) + offsets[i]); *reference = _svmf_copy_object (env, *reference, pto_space_tail); } } } #endif /* Skip to next object */ *pto_space_head = (_svmt_word *) (((char *) obj) + _svmf_get_end_offset (obj)); } } /* -------------------------------------------------------------------------------- _svmf_copying_gc_internal -------------------------------------------------------------------------------- */ inline svm_static void _svmf_copying_gc_internal (_svmt_JNIEnv *env, void *to_space) { _svmt_word *to_space_head; void *to_space_tail; _svmf_stop_the_world (env); pthread_mutex_unlock (&env->vm->global_mutex); to_space_head = to_space; to_space_tail = to_space; _svmf_trace_native_references (env, &to_space_tail); _svmf_trace_heap (env, &to_space_head, &to_space_tail, to_space); assert (to_space_head == to_space_tail); env->vm->heap.alloc = to_space_head; pthread_mutex_lock (&env->vm->global_mutex); _svmf_resume_the_world (env); } /* -------------------------------------------------------------------------------- _svmf_collect_garbage -------------------------------------------------------------------------------- */ inline svm_static void _svmf_collect_garbage (_svmt_JNIEnv *env) { _svmt_heap *heap = &(env->vm->heap); void *to_space; size_t to_space_size; to_space_size = ((size_t) (((char *) heap->end) - ((char *) heap->start))) + heap->increment_size; if ((to_space = _svmf_malloc (to_space_size)) == NULL) { return; } heap->size = to_space_size; _svmf_copying_gc_internal (env, to_space); heap->hashed_notmoved = 0; heap->hashcode_base += ((char *) heap->alloc) - ((char *) heap->start); _svmf_free (heap->start); heap->start = to_space; heap->end = ((char *) to_space) + to_space_size; } /* -------------------------------------------------------------------------------- _svmf_gc_request_space -------------------------------------------------------------------------------- */ /* IMPORTANT: The calling thread should hold the lock on vm->global_mutex when calling this function. */ inline svm_static jint _svmf_gc_request_space (_svmt_JNIEnv *env, size_t requested_size) { _svmt_heap *heap = &(env->vm->heap); #if defined (_SABLEVM_GC_STATISTICS) struct timeval start_time; struct timeval end_time; long secs; long usecs; #endif /* requested_size = 0 means gc requested. */ if (requested_size != 0) { if (_svmf_is_free(heap, requested_size)) { return JNI_OK; } } #if defined (_SABLEVM_GC_STATISTICS) gettimeofday (&start_time, NULL); #endif /* do gc call. */ _svmf_collect_garbage (env); #if defined (_SABLEVM_GC_STATISTICS) heap->gc_collections++; gettimeofday (&end_time, NULL); secs = end_time.tv_sec - start_time.tv_sec; usecs = end_time.tv_usec - start_time.tv_usec; if (usecs < 0) { usecs += 1000000; secs -= 1; } heap->gc_total_secs += secs; heap->gc_total_usecs += usecs; if (heap->gc_total_usecs > 999999) { heap->gc_total_usecs -= 1000000; heap->gc_total_secs += 1; } #endif if (_svmf_is_free(heap, requested_size)) { return JNI_OK; } return JNI_ERR; } /* -------------------------------------------------------------------------------- _svmf_gc_new_instance -------------------------------------------------------------------------------- */ svm_static jint _svmf_gc_new_instance (_svmt_JNIEnv *env, size_t instance_size, void **ppinstance) { jint status; _svmm_mutex_lock (env->vm->global_mutex); _svmf_halt_if_requested (env); status = _svmf_gc_request_space (env, instance_size); if (status == JNI_OK) { /* Reset the instance space. */ void **ppspace = &(env->vm->heap.alloc); memset (*ppspace, 0, instance_size); *ppinstance = *ppspace; /* Update the free-space pointer. */ *ppspace = ((char *) *ppspace) + instance_size; #if defined (_SABLEVM_GC_STATISTICS) env->vm->heap.al_bytes_allocated += instance_size; env->vm->heap.al_objects_allocated++; #endif } else { _svmf_error_OutOfMemoryError (env); } _svmm_mutex_unlock (); return status; } #endif /* defined (_SABLEVM_COPYING_GC) */