/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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) /* ---------------------------------------------------------------------- _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_free_space ---------------------------------------------------------------------- */ inline svm_static jlong _svmf_get_free_space (_svmt_heap *heap) { return ((char *) heap->end) - ((char *) heap->alloc); } /* ---------------------------------------------------------------------- _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 ---------------------------------------------------------------------- */ svm_static void _svmf_heap_init_defaults (_svmt_heap *heap) { heap->min_size = 25165824; /*SVM_HEAP_DEFAULT_MIN_SIZE; */ heap->max_size = SVM_HEAP_DEFAULT_MAX_SIZE; heap->allocation_increment = 0; /*SVM_HEAP_DEFAULT_ALLOCATION_INCREMENT; */ } /* ---------------------------------------------------------------------- _svmf_heap_init ---------------------------------------------------------------------- */ svm_static jint _svmf_heap_init (_svmt_JNIEnv *env) { _svmt_JavaVM *vm = env->vm; if (_svmm_validate_min_max_increment (vm->heap.min_size, vm->heap.max_size, vm->heap.allocation_increment) != 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 (vm->heap.allocation_increment == 0 && (2 * vm->heap.min_size) < vm->heap.min_size) { return JNI_ERR; } if (vm->heap.allocation_increment == 0) { if (vm->verbose_gc) { _svmf_printf (env, stderr, "[verbose gc: allocating fixed size heap (2 * %d bytes)]\n", vm->heap.min_size); } vm->heap.start = _svmf_malloc (2 * vm->heap.min_size); if (vm->heap.start == NULL) { return JNI_ERR; } vm->heap.end = ((char *) vm->heap.start) + vm->heap.min_size; vm->heap.alloc = vm->heap.start; vm->heap.hashcode_base = 0 - (size_t) vm->heap.start; vm->heap.next_heap = vm->heap.end; } else { if (vm->verbose_gc) { _svmf_printf (env, stderr, "[verbose gc: allocating initial heap (%d bytes)]\n", vm->heap.min_size); } vm->heap.start = _svmf_malloc (vm->heap.min_size); if (vm->heap.start == NULL) { return JNI_ERR; } vm->heap.end = ((char *) vm->heap.start) + vm->heap.min_size; vm->heap.alloc = vm->heap.start; vm->heap.hashcode_base = 0 - (size_t) vm->heap.start; vm->heap.suggested_next_heap_size = vm->heap.min_size; } return JNI_OK; } /* ---------------------------------------------------------------------- _svmf_do_copy_object ---------------------------------------------------------------------- */ inline svm_static _svmt_object_instance * _svmf_do_copy_object (_svmt_JNIEnv *env, _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 (_svmf_lockword_is_array (obj->lockword)) { return _svmf_do_copy_object (env, 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 (env, 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 (env, 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 ---------------------------------------------------------------------- */ 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 ---------------------------------------------------------------------- */ 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_copy_gc_internal ---------------------------------------------------------------------- */ /* IMPORTANT: The world must be stopped prior to calling this function. */ svm_static jint _svmf_copy_gc_internal (_svmt_JNIEnv *env, size_t requested_size) { _svmt_JavaVM *vm = env->vm; size_t increment = vm->heap.allocation_increment; void *to_space; size_t to_space_size; _svmt_word *to_space_head; void *to_space_tail; /***********************************************/ /***********************************************/ /***********************************************/ /***********************************************/ struct timeval start_time; struct timeval end_time; long secs; long usecs; /***********************************************/ /***********************************************/ /***********************************************/ /***********************************************/ struct timeval starttime; struct timeval endtime; if (increment == 0) { to_space = vm->heap.next_heap; to_space_size = vm->heap.min_size; } else { size_t current_heap_size = ((char *) vm->heap.alloc) - ((char *) vm->heap.start); size_t hash_code_size = vm->heap.hashed_notmoved * SVM_ALIGNMENT; size_t strict_min_size = _svmf_aligned_to_increment (current_heap_size + hash_code_size, increment); size_t min_size = _svmf_aligned_to_increment (current_heap_size + hash_code_size + requested_size, increment); size_t suggested_size = vm->heap.suggested_next_heap_size; size_t ideal_size = (suggested_size > min_size) ? suggested_size : min_size; if ((to_space = _svmf_malloc (ideal_size)) != NULL) { to_space_size = ideal_size; } else if ((to_space = _svmf_malloc (min_size)) != NULL) { to_space_size = min_size; } else if ((to_space = _svmf_malloc (strict_min_size)) != NULL) { to_space_size = strict_min_size; } else { return JNI_ERR; } } to_space_head = to_space; to_space_tail = to_space; if (vm->verbose_gc) { _svmf_printf (env, stderr, "[verbose gc: "); gettimeofday (&starttime, NULL); } /***********************************************/ /***********************************************/ /***********************************************/ /***********************************************/ gettimeofday (&start_time, NULL); /***********************************************/ /***********************************************/ /***********************************************/ /***********************************************/ /* trace stack & native references */ _svmf_trace_native_references (env, &to_space_tail); /* trace heap */ _svmf_trace_heap (env, &to_space_head, &to_space_tail, to_space); /***********************************************/ /***********************************************/ /***********************************************/ /***********************************************/ 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; } vm->heap.total_gc_secs += secs; vm->heap.total_gc_usecs += usecs; if (vm->heap.total_gc_usecs > 999999) { vm->heap.total_gc_usecs -= 1000000; vm->heap.total_gc_secs += 1; } _svmf_printf (env, stderr, "gc time = %ld sec %ld usec, total time = %ld sec %ld usec\n", secs, usecs, vm->heap.total_gc_secs, vm->heap.total_gc_usecs); /***********************************************/ /***********************************************/ /***********************************************/ /***********************************************/ if (vm->verbose_gc) { gettimeofday (&endtime, NULL); _svmf_printf (env, stderr, "previously allocated %d bytes, surviving %d bytes, ", ((char *) vm->heap.alloc) - ((char *) vm->heap.start), ((char *) to_space_tail) - ((char *) to_space)); } assert (to_space_head == to_space_tail); vm->heap.hashed_notmoved = 0; vm->heap.hashcode_base += ((char *) vm->heap.alloc) - ((char *) vm->heap.start); if (increment != 0) { size_t suggested_size; _svmf_free (vm->heap.start); vm->heap.start = to_space; vm->heap.end = ((char *) to_space) + to_space_size; vm->heap.alloc = to_space_head; assert (to_space_tail < vm->heap.end); /* attempt to keep the gc heap approximately 1/3 full */ suggested_size = _svmf_aligned_to_increment (3 * ((((char *) vm->heap.alloc) - ((char *) vm->heap.start)) + requested_size), increment); if (suggested_size < vm->heap.min_size) { suggested_size = vm->heap.min_size; } if (vm->heap.max_size != 0 && suggested_size > vm->heap.max_size) { suggested_size = vm->heap.max_size; } vm->heap.suggested_next_heap_size = suggested_size; /* reduce new heap size if it's bigger than next suggested size */ { char *suggestion = ((char *) vm->heap.start) + suggested_size; if ((suggestion >= (((char *) vm->heap.alloc) + requested_size)) && (suggestion < (char *) vm->heap.end)) { vm->heap.end = suggestion; } } } else { vm->heap.next_heap = vm->heap.start; vm->heap.start = to_space; vm->heap.end = ((char *) to_space) + vm->heap.min_size; vm->heap.alloc = to_space_head; assert (to_space_tail < vm->heap.end); } if (vm->verbose_gc) { long secs = endtime.tv_sec - starttime.tv_sec; long usecs = endtime.tv_usec - starttime.tv_usec; if (usecs < 0) { usecs += 1000000; secs -= 1; } _svmf_printf (env, stderr, "new heap is %d bytes, gc time = %ld sec %ld usec]\n", ((char *) vm->heap.end) - ((char *) vm->heap.start), secs, usecs); vm->heap.total_gc_secs += secs; vm->heap.total_gc_usecs += usecs; if (vm->heap.total_gc_usecs > 999999) { vm->heap.total_gc_usecs -= 1000000; vm->heap.total_gc_secs += 1; } } if (requested_size <= (size_t) (((char *) vm->heap.end) - ((char *) vm->heap.alloc))) { return JNI_OK; } return JNI_ERR; } /* ---------------------------------------------------------------------- _svmf_copy_gc ---------------------------------------------------------------------- */ /* IMPORTANT: The calling thread should hold the lock on vm->global_mutex when calling this function. */ svm_static jint _svmf_gc_no_exception (_svmt_JNIEnv *env, size_t requested_size) { _svmt_JavaVM *vm = env->vm; jint status; _svmf_stop_the_world (env); pthread_mutex_unlock (&vm->global_mutex); status = _svmf_copy_gc_internal (env, requested_size); pthread_mutex_lock (&vm->global_mutex); _svmf_resume_the_world (env); return status; } /* ---------------------------------------------------------------------- _svmf_gc_new_instance ---------------------------------------------------------------------- */ svm_static jint _svmf_gc_new_instance (_svmt_JNIEnv *env, size_t instance_size, void **pinstance) { _svmt_JavaVM *vm = env->vm; void *result = NULL; jint status = JNI_OK; _svmm_mutex_lock (vm->global_mutex); _svmf_halt_if_requested (env); if ((instance_size <= (size_t) (((char *) vm->heap.end) - ((char *) vm->heap.alloc))) || ((status = _svmf_gc_no_exception (env, instance_size)) == JNI_OK)) { result = vm->heap.alloc; vm->heap.alloc = ((char *) vm->heap.alloc) + instance_size; } _svmm_mutex_unlock (); if (status != JNI_OK) { _svmf_error_OutOfMemoryError (env); return JNI_ERR; } memset (result, 0, instance_size); *pinstance = result; return JNI_OK; } #endif /* defined (_SABLEVM_COPYING_GC) */