Hotspot源码解析-第二十章-基础类型的数组类型对象的创建与分配

20.2 基础类型的数组类型创建

该函数的入口在init.cpp->init_globals(),然后再调用universe.cpp->universe2_init()函数,实际执行的函数是Universe::genesis,所以从这开始源码的解析。解析前先了解一下Klass的概念,大家思考个问题:我们编写的java类在JVM中是以何种形式存在的呢?

答:其实他就是以Klass类存在的,Klass类就是java类在jvm中存在的形式。下图为Klass类的继承结构

图20-10
在这里插入图片描述

20.2.1 universe.cpp

20.2.1.1 Universe::genesis
void Universe::genesis(TRAPS) {
  ResourceMark rm;

  { FlagSetting fs(_bootstrapping, true);

    { MutexLocker mc(Compile_lock);

      // 计算Object类的虚函数表占用内存大小,实现细节看`章节20.2.1.2`
      compute_base_vtable_size();

      if (!UseSharedSpaces) {
        // 下面创建8个基础类型的数组类型的对象,细节看`章节20.2.2`
        _boolArrayKlassObj      = TypeArrayKlass::create_klass(T_BOOLEAN, sizeof(jboolean), CHECK);
        _charArrayKlassObj      = TypeArrayKlass::create_klass(T_CHAR,    sizeof(jchar),    CHECK);
        _singleArrayKlassObj    = TypeArrayKlass::create_klass(T_FLOAT,   sizeof(jfloat),   CHECK);
        _doubleArrayKlassObj    = TypeArrayKlass::create_klass(T_DOUBLE,  sizeof(jdouble),  CHECK);
        _byteArrayKlassObj      = TypeArrayKlass::create_klass(T_BYTE,    sizeof(jbyte),    CHECK);
        _shortArrayKlassObj     = TypeArrayKlass::create_klass(T_SHORT,   sizeof(jshort),   CHECK);
        _intArrayKlassObj       = TypeArrayKlass::create_klass(T_INT,     sizeof(jint),     CHECK);
        _longArrayKlassObj      = TypeArrayKlass::create_klass(T_LONG,    sizeof(jlong),    CHECK);
        // 将已创建的数组类型对象都存储到_typeArrayKlassObjs数组中,方便后面使用
        _typeArrayKlassObjs[T_BOOLEAN] = _boolArrayKlassObj;
        _typeArrayKlassObjs[T_CHAR]    = _charArrayKlassObj;
        _typeArrayKlassObjs[T_FLOAT]   = _singleArrayKlassObj;
        _typeArrayKlassObjs[T_DOUBLE]  = _doubleArrayKlassObj;
        _typeArrayKlassObjs[T_BYTE]    = _byteArrayKlassObj;
        _typeArrayKlassObjs[T_SHORT]   = _shortArrayKlassObj;
        _typeArrayKlassObjs[T_INT]     = _intArrayKlassObj;
        _typeArrayKlassObjs[T_LONG]    = _longArrayKlassObj;
        // 根加载器的CLD
        ClassLoaderData* null_cld = ClassLoaderData::the_null_class_loader_data();
        // 在null_cld中创建一个Array对象的数组,长度为2,元素默认值都为NULL
        _the_array_interfaces_array = MetadataFactory::new_array<Klass*>(null_cld, 2, NULL, CHECK);
        // 在null_cld中创建一个Array对象的数组,长度为0
        _the_empty_int_array        = MetadataFactory::new_array<int>(null_cld, 0, CHECK);
         // 在null_cld中创建一个Array对象的数组,长度为0
        _the_empty_short_array      = MetadataFactory::new_array<u2>(null_cld, 0, CHECK);
         // 在null_cld中创建一个Array对象的数组,长度为0
        _the_empty_method_array     = MetadataFactory::new_array<Method*>(null_cld, 0, CHECK);
         // 在null_cld中创建一个Array对象的数组,长度为0
        _the_empty_klass_array      = MetadataFactory::new_array<Klass*>(null_cld, 0, CHECK);
      }
    }
  
   // 下面代码逻辑将放在 `章节20.4` 和 `章节20.5` 中讲

   // 将系统类(System/Object/Class/String等等)表示的字符串都以符号symbol的对象存储到虚拟机的符号表中,关于符号表的介绍,可以看`章节19.2`,为什么要创建符号表呢?因为这些系统类是公共的,后面都会用到的,所以统一放符号表中。这块实现看`章节20.4`
    vmSymbols::initialize(CHECK);
    // 各字典(其实就是一个hash table)和基础类的预加载,字典主要用在后续类加载过程中,比如占位符字典表、解析出错表,具体实现细节看`章节20.5`
    SystemDictionary::initialize(CHECK);
    // 取出 Object Klass
    Klass* ok = SystemDictionary::Object_klass();
    // 把 null 和 -2147483648 作为字符串,预先存储到字符串表
    _the_null_string            = StringTable::intern("null", CHECK);
    _the_min_jint_string       = StringTable::intern("-2147483648", CHECK);

    if (UseSharedSpaces) {
      // Verify shared interfaces array.
      assert(_the_array_interfaces_array->at(0) ==
             SystemDictionary::Cloneable_klass(), "u3");
      assert(_the_array_interfaces_array->at(1) ==
             SystemDictionary::Serializable_klass(), "u3");
    } else {
      // 设置共享的接口类数组,0号位是Cloneable klass,1号位是Serializable klass
      _the_array_interfaces_array->at_put(0, SystemDictionary::Cloneable_klass());
      _the_array_interfaces_array->at_put(1, SystemDictionary::Serializable_klass());
    }
    // 设置基础类型的数组类型的超类为Object
    initialize_basic_type_klass(boolArrayKlassObj(), CHECK);
    initialize_basic_type_klass(charArrayKlassObj(), CHECK);
    initialize_basic_type_klass(singleArrayKlassObj(), CHECK);
    initialize_basic_type_klass(doubleArrayKlassObj(), CHECK);
    initialize_basic_type_klass(byteArrayKlassObj(), CHECK);
    initialize_basic_type_klass(shortArrayKlassObj(), CHECK);
    initialize_basic_type_klass(intArrayKlassObj(), CHECK);
    initialize_basic_type_klass(longArrayKlassObj(), CHECK);
  } // end of core bootstrapping

  // Object 数组类型 Klass
  _objectArrayKlassObj = InstanceKlass::
    cast(SystemDictionary::Object_klass())->array_klass(1, CHECK);
  // 将该类型添加到其兄弟类型的列表中
  _objectArrayKlassObj->append_to_sibling_list();

  // 针对 jdk1.3、1.4、1.5或更高版本的特殊几个类的加载
  // Only 1.3 or later has the java.lang.Shutdown class.
  // Only 1.4 or later has the java.lang.CharSequence interface.
  // Only 1.5 or later has the java.lang.management.MemoryUsage class.
  if (JDK_Version::is_partially_initialized()) {
    uint8_t jdk_version;
    Klass* k = SystemDictionary::resolve_or_null(
        vmSymbols::java_lang_management_MemoryUsage(), THREAD);
    CLEAR_PENDING_EXCEPTION; // ignore exceptions
    if (k == NULL) {
      k = SystemDictionary::resolve_or_null(
          vmSymbols::java_lang_CharSequence(), THREAD);
      CLEAR_PENDING_EXCEPTION; // ignore exceptions
      if (k == NULL) {
        k = SystemDictionary::resolve_or_null(
            vmSymbols::java_lang_Shutdown(), THREAD);
        CLEAR_PENDING_EXCEPTION; // ignore exceptions
        if (k == NULL) {
          jdk_version = 2;
        } else {
          jdk_version = 3;
        }
      } else {
        jdk_version = 4;
      }
    } else {
      jdk_version = 5;
    }
    JDK_Version::fully_initialize(jdk_version);
  }

  #ifdef ASSERT
  if (FullGCALot) {  // GC相关,先忽略,后续针对GC专题来讲
    // Allocate an array of dummy objects.
    // We'd like these to be at the bottom of the old generation,
    // so that when we free one and then collect,
    // (almost) the whole heap moves
    // and we find out if we actually update all the oops correctly.
    // But we can't allocate directly in the old generation,
    // so we allocate wherever, and hope that the first collection
    // moves these objects to the bottom of the old generation.
    // We can allocate directly in the permanent generation, so we do.
    int size;
    if (UseConcMarkSweepGC) {
      warning("Using +FullGCALot with concurrent mark sweep gc "
              "will not force all objects to relocate");
      size = FullGCALotDummies;
    } else {
      size = FullGCALotDummies * 2;
    }
    objArrayOop    naked_array = oopFactory::new_objArray(SystemDictionary::Object_klass(), size, CHECK);
    objArrayHandle dummy_array(THREAD, naked_array);
    int i = 0;
    while (i < size) {
        // Allocate dummy in old generation
      oop dummy = InstanceKlass::cast(SystemDictionary::Object_klass())->allocate_instance(CHECK);
      dummy_array->obj_at_put(i++, dummy);
    }
    {
      // Only modify the global variable inside the mutex.
      // If we had a race to here, the other dummy_array instances
      // and their elements just get dropped on the floor, which is fine.
      MutexLocker ml(FullGCALot_lock);
      if (_fullgc_alot_dummy_array == NULL) {
        _fullgc_alot_dummy_array = dummy_array();
      }
    }
    assert(i == _fullgc_alot_dummy_array->length(), "just checking");
  }
  #endif

  // Initialize dependency array for null class loader
  ClassLoaderData::the_null_class_loader_data()->init_dependencies(CHECK);

}

20.2.1.2 Universe::compute_base_vtable_size
void Universe::compute_base_vtable_size() {
  // 计算Object类的虚函数表占用内存大小
  _base_vtable_size = ClassLoader::compute_Object_vtable();
}

classLoader.cpp

在Java中所有的类都是默认继承自Object类的,所以这里要计算Object类的初始 vtable的大小,默认JDK_1_2_Object_vtable_size = 5,为什么是5呢?因为Object提供了5个可使用和继承的方法,如下:

public native int hashCode();

public boolean equals(Object obj) {
return (this == obj);
}

protected native Object clone() throws CloneNotSupportedException;

public String toString() {
return getClass().getName() + “@” + Integer.toHexString(hashCode());
}

protected void finalize() throws Throwable { }

int ClassLoader::compute_Object_vtable() {
  int JDK_1_2_Object_vtable_size = 5; // 5个
  // 占用内存为5 * 每个的大小(vtableEntry::size())
  return JDK_1_2_Object_vtable_size * vtableEntry::size();
}

20.2.2 typeArrayKlass.hpp/cpp

20.2.2.1 create_klass
const char* TypeArrayKlass::external_name(BasicType type) {
  switch (type) {
    // 看着下面这些[开头的类型是不是很熟悉,在Java中打印基础类型的数组对象时,就会出现下面符号中的一种,也就是虚拟机层面给各基础类型数组的类型简称
    case T_BOOLEAN: return "[Z";
    case T_CHAR:    return "[C";
    case T_FLOAT:   return "[F";
    case T_DOUBLE:  return "[D";
    case T_BYTE:    return "[B";
    case T_SHORT:   return "[S";
    case T_INT:     return "[I";
    case T_LONG:    return "[J";
    default: ShouldNotReachHere();
  }
  return NULL;
}

static inline Klass* create_klass(BasicType type, int scale, TRAPS) {
    // 创建type类型的基础类型的数组类型
    TypeArrayKlass* tak = create_klass(type, external_name(type), CHECK_NULL);
    assert(scale == (1 << tak->log2_element_size()), "scale must check out");
    return tak;
  }

TypeArrayKlass* TypeArrayKlass::create_klass(BasicType type,
                                      const char* name_str, TRAPS) {
  Symbol* sym = NULL;
  if (name_str != NULL) {
    // 把name_str先创建一个符号,并加入到符号表SymbolTable中,看到符号表是不是眼熟,可以回来看`章节19.2`
    sym = SymbolTable::new_permanent_symbol(name_str, CHECK_NULL);
  }
  // 拿到null加载器的CLD,也就是Java中所谓的根加载器
  ClassLoaderData* null_loader_data = ClassLoaderData::the_null_class_loader_data();
  // 给数组类型的Klass分配内存,看`章节20.2.2.2`
  TypeArrayKlass* ak = TypeArrayKlass::allocate(null_loader_data, type, sym, CHECK_NULL);

  // 将当前创建的klass ak,添加到根加载器的CLD的klasses链表中
  null_loader_data->add_class(ak);

  // Call complete_create_array_klass after all instance variables have been initialized.
  /*
  * 通过Java我们知道,每个类都有一个对应的java.lang.Class对象,同理在JVM中也需要有这么一个对象,在JVM中,
  * 该对象用InstanceMirrorKlass的对象来表示,所以这一步就是创建该数组类型的InstanceMirrorKlass对象。
  * 由`图20-10`,得到InstanceMirrorKlass是继承自InstanceKlass的,并且在InstanceMirrorKlass中定义了很多
  * 操作InstanceKlass类对象的函数,所以在Java中,java.lang.Class可以用来做反射处理。
  */
  complete_create_array_klass(ak, ak->super(), CHECK_NULL);

  return ak;
}
20.2.2.2 allocate
TypeArrayKlass* TypeArrayKlass::allocate(ClassLoaderData* loader_data, BasicType type, Symbol* name, TRAPS) {
  assert(TypeArrayKlass::header_size() <= InstanceKlass::header_size(),
      "array klasses must be same size as InstanceKlass");
  // 先计算要分配的内存大小
  int size = ArrayKlass::static_size(TypeArrayKlass::header_size());
  // 在元空间中分配内存,new 操作看`章节20.2.3.1`,TypeArrayKlass 构造函数实现看`章节20.2.2.3`
  return new (loader_data, size, THREAD) TypeArrayKlass(type, name);
}
20.2.2.3 TypeArrayKlass
TypeArrayKlass::TypeArrayKlass(BasicType type, Symbol* name) : ArrayKlass(name) { // 调用父类构造函数
  set_layout_helper(array_layout_helper(type));
  assert(oop_is_array(), "sanity");
  assert(oop_is_typeArray(), "sanity");
  // 设置该数组的最大长度
  set_max_length(arrayOopDesc::max_array_length(type));
  assert(size() >= TypeArrayKlass::header_size(), "bad size");
  // 设置该数组类的CLD为根加载器Data,也就是说基础数组类型的加载都是通过根加载器去加载的
  set_class_loader_data(ClassLoaderData::the_null_class_loader_data());
}

父类ArrayKlass构造函数

ArrayKlass::ArrayKlass(Symbol* name) {
  set_name(name);// 设置名字
  // 这里默认设置超类为NULL
  set_super(Universe::is_bootstrapping() ? (Klass*)NULL : SystemDictionary::Object_klass());
  set_layout_helper(Klass::_lh_neutral_value);
  set_dimension(1);
  set_higher_dimension(NULL);
  set_lower_dimension(NULL);
  set_component_mirror(NULL);
  // Object类的vtable大小,这个在`章节20.2.1.2`中有介绍
  int vtable_size = Universe::base_vtable_size();
  set_vtable_length(vtable_size);
  set_is_cloneable(); // All arrays are considered to be cloneable (See JLS 20.1.5)
  JFR_ONLY(INIT_ID(this);)
}

20.2.3 klass.cpp

20.2.3.1 new创建并分配内存
void* Klass::operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw() {
  // 细节看`章节20.2.4.1`
  return Metaspace::allocate(loader_data, word_size, /*read_only*/false,
                             MetaspaceObj::ClassType, THREAD);
}

20.2.4 metaspace.cpp

20.2.4.1 allocate
MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size,
                              bool read_only, MetaspaceObj::Type type, TRAPS) {
  if (HAS_PENDING_EXCEPTION) {
    assert(false, "Should not allocate with exception pending");
    return NULL;  // caller does a CHECK_NULL too
  }

  assert(loader_data != NULL, "Should never pass around a NULL loader_data. "
        "ClassLoaderData::the_null_class_loader_data() should have been used.");

  // DumpSharedSpaces表示多Java进程共享,这里不涉及多Java进程共享,这一步不会走
  if (DumpSharedSpaces) {
    assert(type > MetaspaceObj::UnknownType && type < MetaspaceObj::_number_of_types, "sanity");
    Metaspace* space = read_only ? loader_data->ro_metaspace() : loader_data->rw_metaspace();
    MetaWord* result = space->allocate(word_size, NonClassType);
    if (result == NULL) {
      report_out_of_shared_space(read_only ? SharedReadOnly : SharedReadWrite);
    }
    if (PrintSharedSpaces) {
      space->record_allocation(result, type, space->vsm()->get_raw_word_size(word_size));
    }

    // Zero initialize.
    Copy::fill_to_aligned_words((HeapWord*)result, word_size, 0);

    return result;
  }
  // 确定要分配的内存内容的类型,类的创建都是ClassType
  MetadataType mdtype = (type == MetaspaceObj::ClassType) ? ClassType : NonClassType;

  // 尝试分配元数据。loader_data->metaspace_non_null()函数(实现细节看`章节20.3`)可以拿到Metaspace的指针指向的元空间对象,然后再通过allocate函数在元空间中分配内存,allocate分配看后面描述
  MetaWord* result = loader_data->metaspace_non_null()->allocate(word_size, mdtype);

  if (result == NULL) {
    tracer()->report_metaspace_allocation_failure(loader_data, word_size, type, mdtype);

    // 分配失败,可能是内存不足,所以启动GC
    if (is_init_completed()) {
      // 尝试GC 清空一些内存
      result = Universe::heap()->collector_policy()->satisfy_failed_metadata_allocation(
          loader_data, word_size, mdtype);
    }
  }
  // 启动GC 后还是失败,那就直接报告oom错误
  if (result == NULL) {
    report_metadata_oome(loader_data, word_size, type, mdtype, CHECK_NULL);
  }

  // Zero initialize.
  Copy::fill_to_aligned_words((HeapWord*)result, word_size, 0);
  // 返回分配后的内存首地址
  return result;
}

allocate 从SpaceManager中分配内存

MetaWord* Metaspace::allocate(size_t word_size, MetadataType mdtype) {
  // class_vsm() 和 vsm() 取出的都是SpaceManager对象,直接往下看
  if (is_class_space_allocation(mdtype)) {
    return  class_vsm()->allocate(word_size);
  } else {
    return  vsm()->allocate(word_size);
  }
}

从SpaceManager中分配

MetaWord* SpaceManager::allocate(size_t word_size) {
  MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag);

  size_t raw_word_size = get_raw_word_size(word_size);
  BlockFreelist* fl =  block_freelists();
  MetaWord* p = NULL;
  // Allocation from the dictionary is expensive in the sense that
  // the dictionary has to be searched for a size.  Don't allocate
  // from the dictionary until it starts to get fat.  Is this
  // a reasonable policy?  Maybe an skinny dictionary is fast enough
  // for allocations.  Do some profiling.  JJJ
  if (fl->total_size() > allocation_from_dictionary_limit) {
    p = fl->get_block(raw_word_size);
  }
  if (p == NULL) {
    // 直接看这一行
    p = allocate_work(raw_word_size);
  }

  return p;
}

正式分配

MetaWord* SpaceManager::allocate_work(size_t word_size) {
  assert_lock_strong(_lock);
#ifdef ASSERT
  if (Metadebug::test_metadata_failure()) {
    return NULL;
  }
#endif
  // Is there space in the current chunk?
  MetaWord* result = NULL;

  // For DumpSharedSpaces, only allocate out of the current chunk which is
  // never null because we gave it the size we wanted.   Caller reports out
  // of memory if this returns null.
  if (DumpSharedSpaces) {
    assert(current_chunk() != NULL, "should never happen");
    inc_used_metrics(word_size);
    return current_chunk()->allocate(word_size); // caller handles null result
  }
  // current_chunk() 函数拿到当前在用的chunk,这个chunk就是`章节20.3.2`中 initialize_first_chunk()函数实现里面设置的,然后再通过allocate分配,怎么分配的呢,继续往下看
  if (current_chunk() != NULL) {
    result = current_chunk()->allocate(word_size);
  }
  // 分配不成功,肯定是容量不够了,这一步就是扩容
  if (result == NULL) {
    result = grow_and_allocate(word_size);
  }

  if (result != NULL) {
    inc_used_metrics(word_size);
    assert(result != (MetaWord*) chunks_in_use(MediumIndex),
           "Head of the list is being allocated");
  }

  return result;
}

从Metachunk中分配

看代码实现,非常简单,就是把_top上移word_size个字,读者只需把它理解成一个带刻度的水缸,往里面加水时,刻度值上升,就这么简单。

MetaWord* Metachunk::allocate(size_t word_size) {
  MetaWord* result = NULL;
  // If available, bump the pointer to allocate.
  if (free_word_size() >= word_size) {
    result = _top;
    _top = _top + word_size;
  }
  return result;
}

20.3 创建和分配Metaspace

为什么要创建Metaspace呢?

答:在元空间分配元数据内存时都需要元空间对象Metaspace实例来操作?在第十八章中虽然有对元空间的创建与分配讲解,但是并没有对元空间实际存储数据逻辑和真正的内存空间分配描述,第十八章只是描述了对元空间实际分配元数据内存前的一些准备工作,比如划出一块大的空间作为元空间、再将大空间划成一块块小的chunk、再通过链表管理起来等等。这一章节就是讲具体的元数据的分配。

20.3.1 classLoaderData.cpp

20.3.1.1 metaspace_non_null
Metaspace* ClassLoaderData::metaspace_non_null() {
  assert(!DumpSharedSpaces, "wrong metaspace!");
  // If the metaspace has not been allocated, create a new one.  Might want
  // to create smaller arena for Reflection class loaders also.
  // The reason for the delayed allocation is because some class loaders are
  // simply for delegating with no metadata of their own.
  if (_metaspace == NULL) {
    MutexLockerEx ml(metaspace_lock(),  Mutex::_no_safepoint_check_flag);
    // Check again if metaspace has been allocated while we were getting this lock.
    if (_metaspace != NULL) {
      return _metaspace;
    }
    if (this == the_null_class_loader_data()) {
      assert (class_loader() == NULL, "Must be");
      // 直接看这一行,Metaspace的创建看`章节20.3.2`
      set_metaspace(new Metaspace(_metaspace_lock, Metaspace::BootMetaspaceType));
    } else if (is_anonymous()) {
      if (TraceClassLoaderData && Verbose && class_loader() != NULL) {
        tty->print_cr("is_anonymous: %s", class_loader()->klass()->internal_name());
      }
      set_metaspace(new Metaspace(_metaspace_lock, Metaspace::AnonymousMetaspaceType));
    } else if (class_loader()->is_a(SystemDictionary::reflect_DelegatingClassLoader_klass())) {
      if (TraceClassLoaderData && Verbose && class_loader() != NULL) {
        tty->print_cr("is_reflection: %s", class_loader()->klass()->internal_name());
      }
      set_metaspace(new Metaspace(_metaspace_lock, Metaspace::ReflectionMetaspaceType));
    } else {
      set_metaspace(new Metaspace(_metaspace_lock, Metaspace::StandardMetaspaceType));
    }
  }
  // 把创建后的metaspace对象实例返回
  return _metaspace;
}

20.3.2 metaspace.cpp

20.3.2.1 构造函数和初始始化

构造函数

Metaspace::Metaspace(Mutex* lock, MetaspaceType type) {
 initialize(lock, type);
}

初始化函数

void Metaspace::initialize(Mutex* lock, MetaspaceType type) {
  verify_global_initialization();

  // 创建 SpaceManager 对象,分配元数据(主要是符号、字符串等)需要用到,本章节的后面有对这对象创建的讲解
  _vsm = new SpaceManager(NonClassType, lock);

  if (using_class_space()) {
    // 创建 SpaceManager 对象,分配类时需要用到
    _class_vsm = new SpaceManager(ClassType, lock);
  } else {
    _class_vsm = NULL;
  }

  MutexLockerEx cl(SpaceManager::expand_lock(), Mutex::_no_safepoint_check_flag);

  // 这一步就是从`第十八章`中分配的空闲chunk链表中找出一个适合当前分配大小(前面讲过,MetaChunk块有三种大小,取一个适合自己的就行)的MetaChunk块对象,并把它添加到 SpaceManager 对象(在这里表示 _vsm)来管理
  initialize_first_chunk(type, NonClassType);

  // 同上,这是取出一块MetaChunk来存放类数据,并用 SpaceManager 对象(在这里表示 _class_vsm)管理
  if (using_class_space()) {
    initialize_first_chunk(type, ClassType);
  }

  _alloc_record_head = NULL;
  _alloc_record_tail = NULL;
}

创建SpaceManager对象

SpaceManager对象就是管理元数据分配在哪个chunk,这个对象的创建很简单,就是做一些初始值的赋值

SpaceManager::SpaceManager(Metaspace::MetadataType mdtype,
                           Mutex* lock) :
  _mdtype(mdtype),
  _allocated_blocks_words(0),
  _allocated_chunks_words(0),
  _allocated_chunks_count(0),
  _lock(lock)
{
  initialize();
}

void SpaceManager::initialize() {
  Metadebug::init_allocation_fail_alot_count();
  for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {
    _chunks_in_use[i] = NULL;  //  把所有chunk已用状态设置为null
  }
  _current_chunk = NULL;
  if (TraceMetadataChunkAllocation && Verbose) {
    gclog_or_tty->print_cr("SpaceManager(): " PTR_FORMAT, this);
  }
}

后续将通过抖音视频/直播的形式分享技术,由于前期要做一些准备和规划,预计2024年6月开始,欢迎关注,如有需要或问题咨询,也可直接抖音沟通交流。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

多栖码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值