0
点赞
收藏
分享

微信扫一扫

windows系统使用c++实现一个小型jvm(四)------------jvm的gc 以及 jvm是什么


  这篇文章记录一下gc和对虚拟机的理解,其它的相关jvm的内容就暂时先不涉及了,以后有机会再弄。 

1.gc流程

     在前台的文章中,我记录了一个模拟的gc流程,那个里面由于对于根对象 具有很高的抽象程度,因此对java的gc想要去了解一下,这里分块看看gc的源码:

// 0. create a TEMP new-oop-pool:
unordered_map<Oop *, Oop *> new_oop_map; // must be `map/set` instead of list, for getting rid of duplication!!!

    首先,定义一个缓存容器。 

Oop *new_oop;        // global local variable

// 0.5. first migrate all of the basic type mirrors.
for (auto & iter : java_lang_class::get_single_basic_type_mirrors()) {
Oop *mirror = iter.second;
recursive_add_oop_and_its_inner_oops_and_modify_pointers_by_the_way(mirror, new_oop_map);
iter.second = (MirrorOop *)mirror;
}
// 0.7. I don't want to uninstall all StringTable...
unordered_set<Oop *, java_string_hash, java_string_equal_to> new_string_table;
for (auto & iter : java_lang_string::get_string_table()) {
new_oop = iter;
recursive_add_oop_and_its_inner_oops_and_modify_pointers_by_the_way(new_oop, new_oop_map);
new_string_table.insert(new_oop);
}
new_string_table.swap(java_lang_string::get_string_table());

  然后收集全局变量。 

// 1. for all GC-Roots [InstanceKlass]:
for (auto iter : system_classmap) {
klass_inner_oop_gc(iter.second, new_oop_map); // gc the klass
}
for (auto iter : AutomanClassLoader::get_loader().classmap) {
klass_inner_oop_gc(iter.second, new_oop_map); // gc the klass
}
for (auto iter : AutomanClassLoader::get_loader().anonymous_klassmap) {
klass_inner_oop_gc(iter, new_oop_map); // gc the klass
}

    然会回收类实例(这个流程可能是 卸载,与加载类对应);

// 2. for all GC-Roots [vm_threads]:
for (auto & thread : automan_jvm::threads()) {
// std::wcout << "thread: " << thread.tid << ", has " << thread.vm_stack.size() << " frames... "<< std::endl; // delete
// 2.3. for thread.args
for (auto & iter : thread.arg) {
// std::wcout << "arg: " << iter << std::endl; // delete
recursive_add_oop_and_its_inner_oops_and_modify_pointers_by_the_way(iter, new_oop_map);
}
for (auto & frame : thread.vm_stack) {
// 2.5. for vm_stack::StackFrame::LocalVariableTable
for (auto & oop : frame.localVariableTable) {
// std::wcout << "localVariableTable: " << oop; // delete
recursive_add_oop_and_its_inner_oops_and_modify_pointers_by_the_way(oop, new_oop_map);
// std::wcout << " to " << oop; // delete
}
// 2.7. for vm_stack::StackFrame::op_stack
// stack can't use iter. so make it with another vector...
list<Oop *> temp;
while(!frame.op_stack.empty()) {
Oop *oop = frame.op_stack.top(); frame.op_stack.pop();
temp.push_front(oop);
}
for (auto & oop : temp) {
// std::wcout << "op_stack: " << oop; // delete
recursive_add_oop_and_its_inner_oops_and_modify_pointers_by_the_way(oop, new_oop_map);
// std::wcout << " to " << oop; // delete
}
for (auto & oop : temp) {
frame.op_stack.push(oop);
}
}

}

  然会回收线程的资源,包括本地变量表,虚拟机栈,操作栈,等可回收资源。 

// 2.5. for all GC-Roots: ThreadTable
for (auto & iter : ThreadTable::get_thread_table()) {
// std::wcout << "thread: from " << iter.second.second;
Oop *thread = std::get<1>(iter.second);
recursive_add_oop_and_its_inner_oops_and_modify_pointers_by_the_way(thread, new_oop_map);
std::get<1>(iter.second) = (InstanceOop *)thread;
// std::wcout << " to " << iter.second.second;
}

     然后回收线程。

// 3. create a new oop table and exchange with the global Mempool
list<Oop *> new_oop_handler_pool;
for (auto & iter : new_oop_map) {
new_oop_handler_pool.push_back(iter.second);
}
// delete all:
for (auto iter : Mempool::oop_handler_pool()) {
delete iter;
}
// swap.
new_oop_handler_pool.swap(Mempool::oop_handler_pool());
// 4. final: must do this.
gc() = false; // no need to lock.
//todo: 这里gc完成后,通知所有的线程(由 gc线程发出通知)
signal_all_thread();
std::wcout << "gc over!!" << std::endl; // delete

  最后回收资源,并处罚线程通知。 

  从代码看,貌似没有用分代回收。 。  emmmm... ,但是至少知道所谓的根是怎么来的了。 

2.jvm虚拟机是什么?

   这个主要是我用于对这段时间调试的理解。 

   之前总是听说,java是介于解释型与编译型语言之间,即中间字节码的方式运行的,它较纯编译语言的速度有所下降。  从源码来分析,它的速度到底有哪些影响呢? 

    实际上,这个jvm的主题部分就是一个  switch...case...  结构,  其中,ByteCodeEngine在目前功能不完善的情况下,这个控制结构的代码长度为 3000多行。   注意,这是一个方法哦!   比绝大多数类都要大了。 

    在看看  case中,用于判断的是  十六进制的 操作数,  类似于  0x01,0x02... 0xff  这种,数字有个什么好处??   它是天然的索引!!!   简言之,这个switch...case...结构是一颗天然的  B树索引,任意一个操作的检索时间为  O(1)的时间复杂度。  从这个层面讲,它与原生的程序几乎没有区别。 

    那为什么说它的速度有一定影响呢?我觉得主要从以下几个方面考虑:

        1.任意一个java程序,它要启动前,必须启动jvm,启动jvm我们前面已经说了,它会经历那样那样的步骤。  不算轻量级。

        2.启动一个java程序之后,它的内部会至少开辟三个线程,这就涉及到线程调度,而且gc线程调用较为频繁,这是比较耗时的。 

        3.虽然执行流程跟原生的并没有多大区别,但是虚拟机栈,本地变量表,全局常量表 等等都是通过代码维护,而纯编译语言这些东西是由操作系统维护的,它是机器码级别。  所以这里速度会有一些差别。

  jvm的内容暂时就记到这里了,代码地址在: ​​这里​​。  

举报

相关推荐

0 条评论