记一次 JVM 源码分析(3.内存管理与GC)

48 篇文章 13 订阅
17 篇文章 0 订阅

简介

miniJVM 的内存管理的实现较为简单

  • 内存分配使用了开源的 ltalloc 库
  • GC就是经典的 Mark-Sweep GC

对象分配

对象分配要关注的就两个过程

  • New 一个 Java 对象的过程
  • 内存块在堆上分配的过程

对象在 JVM 堆中的存在形式

首先,每个对象在 JVM 的结构体的头部都有一个叫 MemoryBlock 的头部
这个头部描述了对象的类型,读写锁,以及 GC 标志

//内存块的头部描述,每个可分配的内存块都在头部有此结构体,以描述内存块的类型和状态
typedef struct _MemoryBlock {
    //对象对应的类
    JClass *clazz;
    struct _MemoryBlock *next;
    ThreadLock *volatile thread_lock;
    //类型,是引用对象还是 JClass 还是其他类型
    u8 type;//type of array or object runtime,class
    //GC 标志
    u8 garbage_mark;
    u8 garbage_reg;
    u8 arr_type_index;
} MemoryBlock;

现有的对象只有两种类型

  • JClass,也就是类对象
  • 0bject,也就是普通对象
struct _ClassType {
    //内存块描述头部
    MemoryBlock mb;
    .....
}

struct _InstanceType {
    MemoryBlock mb;
    //类型二选一
    union {
        //成员变量内存开始的地址
        c8 *obj_fields; //object fieldRef body
        c8 *arr_body;//array body
    };
    //数组长度
    s32 arr_length;
};

实例化对象

普通对象实例化

对于普通对象的实例化,流程非常简单

  • 在堆上为对象分配内存,大小为对象内部非静态成员变量的总大小(包括各个父类的)+ Instance 结构体本身的大小
  • 将对象引用注册到 GC 管理中

对象内存结构
在这里插入图片描述

Instance *instance_create(Runtime *runtime, JClass *clazz) {
    //很简单,分配目标类中的成员变量所占内存大小的内存空间 + Instance 结构体本身所占空间 就可以了。
    Instance *ins = jvm_calloc(sizeof(Instance) + clazz->field_instance_len);
    ins->mb.type = MEM_TYPE_INS;
    ins->mb.clazz = clazz;
    //指向成员变量内存开始的地址
    ins->obj_fields = (c8 *) (&ins[1]);//jvm_calloc(clazz->field_instance_len);

    gc_refer_reg(runtime, ins);
    return ins;
}

Class 对象实例化

每个java 类有一个 java.lang.Class 的实例, 用于承载对相关java类的操作
这个类相当于 JClass 结构体在 Java 层中的 mirro,一对一的关系

  • 先检查 JClass 中有没有已经实例化的 Class 对象,有的话直接返回
  • New Class 的 Instance
  • 加入 GC Holder 防止被 GC
  • 调用 Class 对象的默认无参构造函数 及 初始化成员变量值
  • JClass 结构体和 Class 对象互相保存对方的指针
Instance *insOfJavaLangClass_create_get(Runtime *runtime, JClass *clazz) {
    JClass *java_lang_class = classes_load_get_c(STR_CLASS_JAVA_LANG_CLASS, runtime);
    if (java_lang_class) {
        if (clazz->ins_class) {
            return clazz->ins_class;
        } else {
            //New Class 对象
            Instance *ins = instance_create(runtime, java_lang_class);
            //加入 GC_Holder 防止被 GC
            gc_refer_hold(ins);
            //调用默认无参构造函数 及 初始化成员变量值
            instance_init(ins, runtime);
            //在 JClass 结构体中保存 Class 对象的指针
            clazz->ins_class = ins;
            //在 Class 对象中保存 JClass 结构体的指针
            insOfJavaLangClass_set_classHandle(ins, clazz);
            return ins;
        }
    }
    return NULL;
}

void insOfJavaLangClass_set_classHandle(Instance *insOfJavaLangClass, JClass *handle) {
    setFieldLong(getInstanceFieldPtr(insOfJavaLangClass, jvm_runtime_cache.class_classHandle), (s64) (intptr_t) handle);
}

String 对象初始化

String 对象除了普通对象的内容以外,还有两个其他的操作

  • .class 文件中的字符串是以 UTF-8 编码,而 Java 在运行时字符串应该是 Unicode 形式,所以这里需要将 UTF-8 转换成 Unicode
  • String 对象中有一个 char[] 数组用来保存字符串,这里需要初始化 char[] 对象并且将其设置到 String 中的 value 成员变量中去
//===============================    实例化字符串  ==================================
Instance *jstring_create(Utf8String *src, Runtime *runtime) {
    if (!src)return NULL;
    Utf8String *clsName = utf8_create_c(STR_CLASS_JAVA_LANG_STRING);
    JClass *jstr_clazz = classes_load_get(clsName, runtime);
    Instance *jstring = instance_create(runtime, jstr_clazz);
    gc_refer_hold(jstring);//hold for no gc

    jstring->mb.clazz = jstr_clazz;
    instance_init(jstring, runtime);

    c8 *ptr = jstring_get_value_ptr(jstring);
    u16 *buf = jvm_calloc(src->length * data_type_bytes[DATATYPE_JCHAR]);
    //UTF-8 -> Unicode
    s32 len = utf8_2_unicode(src, buf);
    if (len >= 0) {//可能解析出错
        //填充 String 中的 char 数组
        Instance *arr = jarray_create_by_type_index(runtime, len, DATATYPE_JCHAR);//u16 type is 5
        setFieldRefer(ptr, (__refer) arr);//设置数组
        memcpy(arr->arr_body, buf, len * data_type_bytes[DATATYPE_JCHAR]);
    }
    jvm_free(buf);
    jstring_set_count(jstring, len);//设置长度
    utf8_destory(clsName);
    gc_refer_release(jstring);
    return jstring;
}

实例化数组

Java 中每种数组也是一个单独的类

  • 数组对象实例化与普通对象的区别在于多了一个计算内存大小的步骤
  • 数组对象在内存中以 Instance 结构体 + 数组体存在
    在这里插入图片描述
    Code
//===============================    实例化数组  ==================================
Instance *jarray_create_by_class(Runtime *runtime, s32 count, JClass *clazz) {
    //类型的 index
    s32 typeIdx = clazz->mb.arr_type_index;
    //取得对应类型所占内存大小
    s32 width = data_type_bytes[typeIdx];
    //为数组对象分配内存,大小为 元素大小 * 长度
    Instance *arr = jvm_calloc(sizeof(Instance) + (width * count));
    //配置 memblock 属性
    arr->mb.type = MEM_TYPE_ARR;
    arr->mb.clazz = clazz;
    arr->mb.arr_type_index = typeIdx;
    arr->arr_length = count;
    //指向数组体
    if (arr->arr_length)arr->arr_body = (c8 *) (&arr[1]);
    gc_refer_reg(runtime, arr);
    return arr;
}

对数组体大小的计算,每种数组元素的大小对应其对象类型的大小,也就是基本数据类型或者引用类型:

s32 data_type_bytes[DATATYPE_COUNT] = {0, 0, 0, 0,
                                       sizeof(c8),
                                       sizeof(u16),
                                       sizeof(f32),
                                       sizeof(f64),
                                       sizeof(c8),
                                       sizeof(s16),
                                       sizeof(s32),
                                       sizeof(s64),
                                       sizeof(__refer),
                                       sizeof(__refer),
};
多维数组

对于多维数组,需要递归遍历的去走创建纬度 - 1 的数组:

Instance *jarray_multi_create(Runtime *runtime, s32 *dim, s32 dim_size, Utf8String *pdesc, s32 deep) {
    s32 len = dim[dim_size - 1 - deep];
    if (len == -1) {
        return NULL;
    }
    JClass *cl = array_class_create_get(runtime, pdesc);

    //获取或者创建普通数组
    Instance *arr = jarray_create_by_class(runtime, len, cl);
    //维度 - 1
    Utf8String *desc = utf8_create_part(pdesc, 1, pdesc->length - 1);
    
    c8 ch = utf8_char_at(desc, 0);
#if _JVM_DEBUG_BYTECODE_DETAIL > 5
    jvm_printf("multi arr deep :%d  type(%c) arr[%x] size:%d\n", deep, ch, arr, len);
#endif
    //如果还有维度,则继续递归
    if (ch == '[') {
        int i;
        s64 val;
        for (i = 0; i < len; i++) {
            Instance *elem = jarray_multi_create(runtime, dim, dim_size, desc, deep + 1);
            val = (intptr_t) elem;
            jarray_set_field(arr, i, val);
        }
    }
    utf8_destory(desc);
    return arr;
}

内部异常实例化

JVM 在运行时会抛出一些内部异常,例如著名的 StackOverflowException,这些异常都不是在 Java 代码中 New 出来的,那么就需要在 JVM 运行中去直接 New 出来对应内部异常对象抛给 Java 层。
分为两种:

  • StackOverflowException 这种是不会带 String 类型的提示信息的
  • 还有一些是有提示信息 Msg 的
    这里直接看带提示信息的实现:
Instance *exception_create_str(s32 exception_type, Runtime *runtime, c8 *errmsg) {
#if _JVM_DEBUG_BYTECODE_DETAIL > 5
    jvm_printf("create exception : %s\n", STRS_CLASS_EXCEPTION[exception_type]);
#endif
    //New String 对象用作异常提示
    Utf8String *uerrmsg = utf8_create_c(errmsg);
    Instance *jstr = jstring_create(uerrmsg, runtime);
    gc_refer_hold(jstr);
    utf8_destory(uerrmsg);
    //String 入参数栈
    RuntimeStack *para = stack_create(1);
    push_ref(para, jstr);
    gc_refer_release(jstr);
    //New 异常对象
    Utf8String *clsName = utf8_create_c(STRS_CLASS_EXCEPTION[exception_type]);
    JClass *clazz = classes_load_get(clsName, runtime);
    utf8_destory(clsName);
    Instance *ins = instance_create(runtime, clazz);
    gc_refer_hold(ins);
    //调用异常对象的带 String 参数的构造方法,传入 Msg
    instance_init_methodtype(ins, runtime, "(Ljava/lang/String;)V", para);
    gc_refer_release(ins);
    stack_destory(para);
    return ins;
}

lambda 表达式实例化

lambda 表达式相当于一个动态生成的方法对象,那么自然也是需要实例化的
lambda 表达式类型由三个JVM内部类型组成:

  • MethodHandle,它是可对直接执行的方法(或域、构造方法等)的类型的引用
  • MethodType, 它是表示方法签名类型的不可变对象。每个方法句柄都有一个MethodType实例,用来指明方法的返回类型和参数类型。
  • MethodHandles$Lookup,相当于MethodHandle工厂类,通过findxxx方法可以得到相应的MethodHandle,还可以配合反射API创建MethodHandle,对应的方法有unreflect、unreflectSpecial等
//MethodType 包含一个 String 类型的方法描述
Instance *method_type_create(Runtime *runtime, Utf8String *desc) {
    JClass *cl = classes_load_get_c(STR_CLASS_JAVA_LANG_INVOKE_METHODTYPE, runtime);
    if (cl) {
        Instance *mt = instance_create(runtime, cl);
        gc_refer_hold(mt);
        Instance *jstr_desc = jstring_create(desc, runtime);
        RuntimeStack *para = stack_create(1);
        //String 参数入栈
        push_ref(para, jstr_desc);
        //调用构造方法
        instance_init_methodtype(mt, runtime, "(Ljava/lang/String;)V", para);
        stack_destory(para);
        gc_refer_release(mt);
        return mt;
    }
    return NULL;
}
//Lookup 包含 lambda 表达式方法所在的类
Instance *method_handles_lookup_create(Runtime *runtime, JClass *caller) {
    JClass *cl = classes_load_get_c(STR_CLASS_JAVA_LANG_INVOKE_METHODHANDLES_LOOKUP, runtime);
    if (cl) {
        Instance *lookup = instance_create(runtime, cl);
        gc_refer_hold(lookup);
        RuntimeStack *para = stack_create(1);

        push_ref(para, insOfJavaLangClass_create_get(runtime, caller));
        instance_init_methodtype(lookup, runtime, "(Ljava/lang/Class;)V", para);
        stack_destory(para);
        gc_refer_release(lookup);
        return lookup;
    }
    return NULL;
}
//handler 较为复杂,包含了具体的方法信息,有4个参数
Instance *method_handle_create(Runtime *runtime, MethodInfo *mi, s32 kind) {
    JClass *cl = classes_load_get_c(STR_CLASS_JAVA_LANG_INVOKE_METHODHANDLE, runtime);
    if (cl) {
        Instance *mh = instance_create(runtime, cl);
        gc_refer_hold(mh);
        RuntimeStack *para = stack_create(4);
        //类型
        push_int(para, kind);
        //当前类名
        Instance *jstr_clsName = jstring_create(mi->_this_class->name, runtime);
        gc_refer_hold(jstr_clsName);
        push_ref(para, jstr_clsName);
        //lambda 方法名
        Instance *jstr_methodName = jstring_create(mi->name, runtime);
        push_ref(para, jstr_methodName);
        gc_refer_hold(jstr_methodName);
        //方法的描述
        Instance *jstr_methodDesc = jstring_create(mi->descriptor, runtime);
        push_ref(para, jstr_methodDesc);
        gc_refer_hold(jstr_methodDesc);
        instance_init_methodtype(mh, runtime, "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", para);
        stack_destory(para);
        gc_refer_release(mh);
        gc_refer_release(jstr_clsName);
        gc_refer_release(jstr_methodName);
        gc_refer_release(jstr_methodDesc);
        return mh;
    }
    return NULL;
}

内存分配

内存管理使用的是 ltalloc 的开源库
地址在 http://code.google.com/p/ltalloc
这里不做深入剖析
这里就简单看一下内存分配的外层方法
d_type.jvm_calloc -> ltalloc.ltcalloc -> ltmalloc

CPPCODE(template <bool throw_> static) void *ltmalloc(size_t size)
{
	unsigned int sizeClass = get_size_class(size);
	ThreadCache *tc = &threadCache[sizeClass];
	FreeBlock *fb = tc->freeList;
	if (likely(fb))
	{
		tc->freeList = fb->next;
		tc->counter++;
		return fb;
	}
	else
		return fetch_from_central_cache CPPCODE(<throw_>)(size, tc, sizeClass);
}

这里大致可以查看出来 ltalloc 堆空间可以分为两个部分

  • 每个线程都有一个私有的小堆,这样是避免多线程分派对象的时候,由锁带来的性能问题,Android ART 中也有类似的设计
  • 一个所有线程公有的大堆,当然面对不同线程来的请求,一定是会加锁的

GC

miniJVM 的 GC 使用的是经典的 Mark-Sweep 标记清除算法,仅支持单线程收集,并不支持并发。

GC 流程

概括:

  • 当对象被创建时,注册进垃圾收集器,纳入监管体系(注册的对象包括 Class 类, Instance 对象实例(包括数组对象))
  • 在垃圾回收时,由垃圾收集线程来收集,收集 collector->header 链表中的对象,收集方法为: 当对象未被任一线程引用时,进行标记,直接销毁,释放内存
  • 收集线程会暂停所有正在执行中的java线程 ,回收完之后,恢复线程执行
  • jdwp调试线程中的运行时对象不可回收

流程

  • 暂停所有线程,stop the world
  • 将所有的注册 reg/hold/release 的对象添加到一个临时列表中
  • 拷贝所有运行时的引用
  • 标记所有被线程引用的对象
  • 恢复所有线程运行,resume the world
  • 处理,调用 finalize 方法
  • 释放没有被标记的对象(即不可达对象)
  • 将临时链表恢复为主链表
/**
 * 查找所有实例,如果发现没有被引用时 mb->garbage_mark ,
 * 去除掉此对象对其他对象的引用,并销毁对象
 *
 * @return ret
 */
s64 garbage_collect() {
    collector->isgc = 1;
    s64 mem_total = 0, mem_free = 0;
    s64 del = 0;
    s64 time, start;

    start = time = currentTimeMillis();
    //prepar gc resource ,
    //GC 同步锁
    garbage_thread_lock();

    //Stop the world 暂停所有线程
    if (_garbage_pause_the_world() != 0) {
        _garbage_resume_the_world();
        return -1;
    }
//    jvm_printf("garbage_pause_the_world %lld\n", (currentTimeMillis() - time));
//    time = currentTimeMillis();
    if (collector->tmp_header) {
        collector->tmp_tailer->next = collector->header;//接起来
        collector->header = collector->tmp_header;
        collector->tmp_header = NULL;
        collector->tmp_tailer = NULL;
    }
//    jvm_printf("garbage_move_cache %lld\n", (currentTimeMillis() - time));
//    time = currentTimeMillis();
    //拷贝所有引用到 GC 链表中
    _garbage_copy_refer();
    //
//    jvm_printf("garbage_copy_refer %lld\n", (currentTimeMillis() - time));
//    time = currentTimeMillis();
    //real GC start
    //开始 GC,设置 FLAG
    _garbage_change_flag();
    //开始递归搜索已经拷贝的根引用
    _garbage_big_search();
    //
//    jvm_printf("garbage_big_search %lld\n", (currentTimeMillis() - time));
//    time = currentTimeMillis();

    //java 线程恢复运行,这时候已经标记了那些对象需要回收了,所以不再需要暂停线程
    _garbage_resume_the_world();
    garbage_thread_unlock();

//    jvm_printf("garbage_resume_the_world %lld\n", (currentTimeMillis() - time));

    s64 time_stopWorld = currentTimeMillis() - start;
    time = currentTimeMillis();
    //

    //处理 finalize 方法
    MemoryBlock *nextmb = collector->header;
    MemoryBlock *curmb, *prevmb = NULL;
    //finalize
    if (collector->_garbage_thread_status == GARBAGE_THREAD_NORMAL) {
        while (nextmb) {
            curmb = nextmb;
            nextmb = curmb->next;
            if (curmb->clazz->finalizeMethod) {// there is a method called finalize
                if (curmb->type == MEM_TYPE_INS && curmb->garbage_mark != collector->flag_refer) {
                    //这里回去调用每个对象的 finalize 方法
                    instance_finalize((Instance *) curmb, collector->runtime);
                }
            }
        }
    }

    //在 finalize 创建的对象,是在 gc 线程创建的对象。。。也需要被注册
    gc_move_refer_thread_2_gc(collector->runtime);// maybe someone new object in finalize...

//    jvm_printf("garbage_finalize %lld\n", (currentTimeMillis() - time));
//    time = currentTimeMillis();
    //clear
    //开始释放垃圾对象的内存
    nextmb = collector->header;
    prevmb = NULL;
    s64 iter = 0;
    while (nextmb) {
        iter++;
        curmb = nextmb;
        nextmb = curmb->next;
        s32 size = _getMBSize(curmb);
        mem_total += size;
        if (curmb->garbage_mark != collector->flag_refer) {
            mem_free += size;
            //释放对象内存
            _garbage_destory_memobj(curmb);
            if (prevmb)prevmb->next = nextmb;
            else collector->header = nextmb;
            del++;
        } else {
            prevmb = curmb;
        }
    }

    //更新堆大小
    spin_lock(&collector->lock);
    collector->obj_count = iter - del;
    heap_size = mem_total - mem_free;
    spin_unlock(&collector->lock);

    s64 time_gc = currentTimeMillis() - time;
//    jvm_printf("[INFO]gc obj: %lld->%lld   heap : %lld -> %lld  stop_world: %lld  gc:%lld\n",
//               iter, collector->obj_count, mem_total, heap_size, time_stopWorld, time_gc);

#ifdef MEM_ALLOC_LTALLOC
    jvm_squeeze(0);
#endif
    collector->isgc = 0;
    return del;
}

创建 GC 收集器

s32 garbage_collector_create() {
    collector = jvm_calloc(sizeof(GcCollector));
    //创建 holder,在 holder 中的对象不会被 gc
    collector->objs_holder = hashset_create();
    //创建引用拷贝的容器
    collector->runtime_refer_copy = arraylist_create(256);
    collector->runtime = runtime_create(NULL);
    collector->_garbage_thread_status = GARBAGE_THREAD_PAUSE;
    thread_lock_init(&collector->garbagelock);
    //开始 GC 线程
    s32 rc = thrd_create(&collector->_garbage_thread, _collect_thread_run, NULL);
    if (rc != thrd_success) {
        jvm_printf("ERROR: garbage thread can't create is %d\n", rc);
    }
    return 0;
}

GC 线程

  • 10 ms 检查一次堆状态,符合条件就准备 GC
  • 每次 GC 间隔至少是 1S
  • 当堆占用超过 80% 才做 GC
// GC 线程
s32 _collect_thread_run(void *para) {
    s64 lastgc = currentTimeMillis();
    while (1) {
        //10ms 检查一次
        threadSleep(10);
        if (collector->_garbage_thread_status == GARBAGE_THREAD_STOP) {
            break;
        }
        if (collector->_garbage_thread_status == GARBAGE_THREAD_PAUSE) {
            continue;
        }
        //至少间隔 1S 才做一次 GC
        if (currentTimeMillis() - lastgc < 1000) {// less than custom sec no gc
            continue;
        }
//        if (java_debug && jdwpserver.clients->length) {// less than 3 sec no gc
//            continue;
//        }
        //当堆占用超过 80% 才做 GC
        if (currentTimeMillis() - lastgc > GARBAGE_PERIOD_MS || heap_size > MAX_HEAP_SIZE * .8f) {
            //真正 GC
            garbage_collect();
            lastgc = currentTimeMillis();
        }

    }
    collector->_garbage_thread_status = GARBAGE_THREAD_DEAD;
    return 0;
}

GC Root

标记对象的第一步就是明确 GC Root,GC 应该从哪里开始递归标记
那么 miniJVM 定义的 GC Root 如下:

  • 1.线程运行栈,类似寄存器所持有的引用,每个线程各持有一个,可能持有的是某个 Field 的引用
  • 2.该线程引用的其他实例的引用,用作在 JNI 层对 Java 对象的引用
  • 3.方法运行栈帧中的本地变量持有的引用
  • 4.类的静态成员变量持有的引用

拷贝所有引用

在 miniJVM 中,每运行到一个方法,都会生成一个 Runtime 结构体
首先取得每个线程的根栈,然后对每一个根栈的栈桢进行递归拷贝

void _garbage_copy_refer() {
    arraylist_clear(collector->runtime_refer_copy);
    s32 i;
    //jvm_printf("thread set size:%d\n", thread_list->length);
    // 遍历所有线程的 Runtime,取出其中的引用
    for (i = 0; i < thread_list->length; i++) {
        // 根 Runtime,相当于根栈帧
        Runtime *runtime = threadlist_get(i);
        // 开始拷贝引用
        _garbage_copy_refer_thread(runtime);
    }
//    arraylist_iter_safe(thread_list, _list_iter_iter_copy, NULL);
    //调试线程
    if (java_debug) {
        Runtime *runtime = jdwpserver.runtime;
        if (runtime) {
            _garbage_copy_refer_thread(runtime);
        }
    }
}

拷贝单个线程的引用

  • Runtime 中存储了方法的本地变量
  • 还有运行栈中的引用,相当于各个指令的操作数所引用的对象
  • 除此之外,线程的根 Runtime 还保存了 ThreadInfo 结构体,这里面存储了线程持有的在 JNI 中持有的对象引用
s32 _garbage_copy_refer_thread(Runtime *pruntime) {
    arraylist_push_back(collector->runtime_refer_copy, pruntime->threadInfo->jthread);

    s32 i, imax;
    StackEntry entry;
    Runtime *runtime = pruntime;
    //该 JVM 是基于栈的,非基于寄存器,所以每个线程会持有一个运行栈来模拟 CPU CORE 的寄存器
    //取出根运行栈
    RuntimeStack *stack = runtime->stack;
    //遍历拷贝运行栈中的所有的引用,即寄存器的引用
    for (i = 0; i < stack->size; i++) {
        peek_entry(stack, &entry, i);
        if (is_ref(&entry)) {
            __refer ref = entry_2_refer(&entry);
            if (ref) {
                arraylist_push_back(collector->runtime_refer_copy, ref);
            }
        }
    }
    //遍历拷贝该线程在 JNI 中引用的所有实例
    ArrayList *holder = runtime->threadInfo->instance_holder;
    for (i = 0, imax = holder->length; i < imax; i++) {
        __refer ref = arraylist_get_value(holder, i);
        arraylist_push_back(collector->runtime_refer_copy, ref);
    }
    // 遍历拷贝每个栈帧中的本地变量,即本地变量对实例的引用
    while (runtime) {
        for (i = 0; i < runtime->localvar_count; i++) {
            LocalVarItem *item = &runtime->localvar[i];
            if (item->type & STACK_ENTRY_REF) {
                __refer ref = item->rvalue;
                arraylist_push_back(collector->runtime_refer_copy, ref);
            }
        }
        runtime = runtime->son;
    }
    //jvm_printf("[%llx] notified\n", (s64) (intptr_t) pruntime->threadInfo->jthread);
    return 0;
}

递归标记

这里标记的内容有两个

  • 一是刚刚所拷贝的所有线程对对象引用
  • 二是 GC Holder 中的所有常量,被常量引用的对象也是不需要释放的
s32 _garbage_big_search() {
    //遍历递归标记刚刚拷贝线程中的根引用
    s32 i, len;
    for (i = 0, len = collector->runtime_refer_copy->length; i < len; i++) {
        __refer r = arraylist_get_value(collector->runtime_refer_copy, i);
        _garbage_mark_object(r);
    }
    //遍历标记常量池
    HashsetIterator hi;
    hashset_iterate(collector->objs_holder, &hi);
    while (hashset_iter_has_more(&hi)) {
        HashsetKey k = hashset_iter_next_key(&hi);
        _garbage_mark_object(k);
    }
    return 0;
}

递归标记不同类型的引用

首先引用分为三种

  • 1.普通对象
  • 2.数组对象
  • 3.类对象
void _garbage_mark_object(__refer ref) {
    if (ref) {
        MemoryBlock *mb = (MemoryBlock *) ref;
        if (collector->flag_refer != mb->garbage_mark) {
            mb->garbage_mark = collector->flag_refer;
            switch (mb->type) {
                case MEM_TYPE_INS:
                    //普通对象
                    _instance_mark_refer((Instance *) mb);
                    break;
                case MEM_TYPE_ARR:
                    //数组对象
                    _jarray_mark_refer((Instance *) mb);
                    break;
                case MEM_TYPE_CLASS:
                    //类对象
                    _class_mark_refer((JClass *) mb);
                    break;
            }
        }
    }
}

标记普通对象引用

首先要注意到该方法是 inline 方法,因为对象引用关系复杂,层级可能非常深,不使用 inline 则可能导致栈溢出。

static inline void _instance_mark_refer(Instance *ins) {
    s32 i, len;
    JClass *clazz = ins->mb.clazz;
    //遍历该对象和所有父类型,找出所有 Field
    while (clazz) {
        FieldPool *fp = &clazz->fieldPool;
        ArrayList *fiList = clazz->insFieldPtrIndex;
        for (i = 0, len = fiList->length; i < len; i++) {
            FieldInfo *fi = &fp->field[(s32) (intptr_t) arraylist_get_value_unsafe(fiList, i)];
            c8 *ptr = getInstanceFieldPtr(ins, fi);
            //如果该 Field 不为空
            if (ptr) {
                //取出该 Field 的引用
                __refer ref = getFieldRefer(ptr);
                //如果引用存在,则标记该引用可达,不会在接下来被回收
                if (ref)_garbage_mark_object(ref);
            }
        }
        clazz = getSuperClass(clazz);
    }
}

标记数组对象

数组对象和普通对象的差别就是需要遍历标记其中的每一个元素

static inline void _jarray_mark_refer(Instance *arr) {
    if (arr && arr->mb.type == MEM_TYPE_ARR) {
//        if (utf8_equals_c(arr->mb.clazz->name, "[[D")) {
//            jvm_printf("check %llx\n", (s64) (intptr_t) arr);
//        }
        if (isDataReferByIndex(arr->mb.arr_type_index)) {
            s32 i;
            //遍历标记数组的每一个元素
            for (i = 0; i < arr->arr_length; i++) {//把所有引用去除,否则不会垃圾回收
                s64 val = jarray_get_field(arr, i);
                if (val)_garbage_mark_object((__refer) (intptr_t) val);
            }
        }
    }
    return;
}

标记类对象

类对象的静态成员变量在类可达的时候一定是可达的,所以类对象需要标记所有的静态成员变量

static inline void _class_mark_refer(JClass *clazz) {
    s32 i, len;
    //类对象需要一同标记类中的静态变量,即常量池
    if (clazz->field_static) {
        FieldPool *fp = &clazz->fieldPool;
        ArrayList *fiList = clazz->staticFieldPtrIndex;
        for (i = 0, len = fiList->length; i < len; i++) {
            FieldInfo *fi = &fp->field[(s32) (intptr_t) arraylist_get_value_unsafe(fiList, i)];
            c8 *ptr = getStaticFieldPtr(fi);
            if (ptr) {
                __refer ref = getFieldRefer(ptr);
                _garbage_mark_object(ref);
            }
        }
    }
}

最后

标记完成后:

  • 恢复线程运行,因为此时哪些对象是不可达的已经确认,后面的内存释放不会影响线程的运行了
  • 处理调用垃圾对象的 finalize 方法
  • 释放所有没有标记到的内存
  • 刷新堆大小
    整个 GC 就完成了
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值