diff --git a/_posts/2024-09-10-g1-gc-ter-obj.md b/_posts/2024-09-10-g1-gc-ter-obj.md new file mode 100644 index 0000000..d6bc9bf --- /dev/null +++ b/_posts/2024-09-10-g1-gc-ter-obj.md @@ -0,0 +1,115 @@ +--- +layout: post +title: "JVM 对象遍历" +date: 2024-09-05 11:00:00 +0200 +tags: [GC, G1] +--- + +GC 中,对于存活的对象需要遍历其全引用类型的属性,前面的文章并没有解释对象是如何被遍历的,本文参考 [以 ZGC 为例,谈一谈 JVM 是如何实现 Reference 语义的](https://mp.weixin.qq.com/s?__biz=Mzg2MzU3Mjc3Ng==&mid=2247489586&idx=1&sn=4306549c480f668458ab4df0d4b2ea47&chksm=ce77de75f9005763016605e0d268e1a4393a83bfe2a281c915bbf55de99d25cda529195c2843&scene=178&cur_album_id=2291913023118213124#rd) (后文中称其为引文) 中 "JVM 如何遍历对象的引用关系图" 小节简单介绍对象的遍历。 读者一定要先读引文,再读本文就很简单。 + +## OopMapBlock + +OopMapBlock 是对象中引用类型属性的结构,加快对象的遍历。由于引用类型的字段不都是连续的,所以 OopMapBlock 对于某一个 class 会有多个。如图,图片来自引文。 + + + +## 遍历逻辑 + +在回收阶段,方法 `G1ParScanThreadState::do_copy_to_survivor_space` 拷贝存活对象的时候需要遍历其引用的属性。 + +对于普通对象: + +```cpp +obj->oop_iterate_backwards(&_scanner, klass); +``` + +数组类型的对象: + +```cpp +to_array->oop_iterate_range(&_scanner, 0, checked_cast(step._index)); +``` + +对于 Java 对象,对象头中存储对象的类型。 + +```cpp +class oopDesc { + volatile markWord _mark; + union _metadata { + Klass* _klass; + narrowKlass _compressed_klass; + } _metadata +} +``` + +首先根据 `Klass` 类型拿到对应的函数,然后调用函数。 + +```cpp +//->oopDesc::oop_iterate_backwards +//->OopIteratorClosureDispatch::oop_oop_iterate_backwards +void OopIteratorClosureDispatch::oop_oop_iterate_backwards(OopClosureType* cl, oop obj, Klass* klass) { + OopOopIterateBackwardsDispatch::function(klass)(cl, obj, klass); +} +``` + +`_function` 对于每个 class 类型,都有对应的遍历函数。 + +```cpp +static FunctionType function(Klass* klass) { + return _table._function[klass->kind()]; +} + + +void set_init_function() { + _function[KlassType::Kind] = &init; +} +``` + +调用链是: + +```cpp +// &init -> +// _table.set_resolve_function_and_execute + +//set_resolve_function 绑定函数 +//_function[KlassType::Kind](cl, obj, k); // 函数调用 +//oop_oop_iterate_backwards + +static void oop_oop_iterate_backwards(OopClosureType* cl, oop obj, Klass* k) { + ((KlassType*)k)->KlassType::template oop_oop_iterate_reverse(obj, cl); +} +``` + +`oop_oop_iterate_reverse` 根据 `KlassType` 的类型遍历对象。 + +以 `InstanceKlass` 为例,获取 `nonstatic_oop_maps` 的地址,根据 `OopMapBlock` 的信息遍历对象。 + +```cpp +void InstanceKlass::oop_oop_iterate_reverse(oop obj, OopClosureType* closure) { + oop_oop_iterate_oop_maps_reverse(obj, closure); +} + +ALWAYSINLINE void InstanceKlass::oop_oop_iterate_oop_maps_reverse(oop obj, OopClosureType* closure) { + OopMapBlock* const start_map = start_of_nonstatic_oop_maps(); + OopMapBlock* map = start_map + nonstatic_oop_map_count(); + while (start_map < map) { + --map; + oop_oop_iterate_oop_map_reverse(map, obj, closure); + } +} + +void InstanceKlass::oop_oop_iterate_oop_map_reverse(OopMapBlock* map, oop obj, OopClosureType* closure) { + T* const start = obj->field_addr(map->offset()); + T* p = start + map->count(); + + while (start < p) { + --p; + Devirtualizer::do_oop(closure, p); + } +} +``` + +## 总结 + +本文参考 [以 ZGC 为例,谈一谈 JVM 是如何实现 Reference 语义的](https://mp.weixin.qq.com/s?__biz=Mzg2MzU3Mjc3Ng==&mid=2247489586&idx=1&sn=4306549c480f668458ab4df0d4b2ea47&chksm=ce77de75f9005763016605e0d268e1a4393a83bfe2a281c915bbf55de99d25cda529195c2843&scene=178&cur_album_id=2291913023118213124#rd) 简单的介绍了对象的遍历。 + + diff --git a/_posts/2024-09-10-g1-gc-young-gc-iter-obj.md b/_posts/2024-09-10-g1-gc-young-gc-iter-obj.md deleted file mode 100644 index ad2aba9..0000000 --- a/_posts/2024-09-10-g1-gc-young-gc-iter-obj.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -layout: post -title: "JVM 对象遍历" -date: 2024-09-05 11:00:00 +0200 -tags: [GC, G1] ---- \ No newline at end of file diff --git a/assets/ygc-obj-iter/omb.png b/assets/ygc-obj-iter/omb.png new file mode 100644 index 0000000..3be6f75 Binary files /dev/null and b/assets/ygc-obj-iter/omb.png differ