public static void createEstimators(Object rootObj, HashMap<Class<?>, ObjectEstimator> byType) {
    // Code initially inspired by Google ObjectExplorer.
    // TODO: roll in the direct-only estimators from fields. Various other optimizations possible.
    Deque<Object> stack = createWorkStack(rootObj, byType);

    while (!stack.isEmpty()) {
      Object obj = stack.pop();
      Class<?> clazz;
      if (obj instanceof Class<?>) {
        clazz = (Class<?>) obj;
        obj = null;
      } else {
        clazz = obj.getClass();
      }
      ObjectEstimator estimator = byType.get(clazz);
      assert estimator != null;
      if (!estimator.isFromClass && obj == null) {
        // The object was added later for the same class; see addToProcessing.
        continue;
      }
      if (estimator.isProcessed())
        continue;
      estimator.init();
      for (Field field : getAllFields(clazz)) {
        Class<?> fieldClass = field.getType();
        if (Modifier.isStatic(field.getModifiers())) {
          continue;
        }
        if (Class.class.isAssignableFrom(fieldClass)) {
          continue;
        }
        if (fieldClass.isPrimitive()) {
          estimator.addPrimitive(fieldClass);
          continue;
        }
        if (Enum.class.isAssignableFrom(fieldClass)) {
          estimator.addEnum();
          continue;
        }
        boolean isArray = fieldClass.isArray();
        if (isArray && fieldClass.getComponentType().isPrimitive()) {
          estimator.addField(FieldType.PRIMITIVE_ARRAY, field);
          continue;
        }
        Object fieldObj = null;
        if (obj != null) {
          fieldObj = extractFieldObj(obj, field);
          fieldClass = determineRealClass(byType, stack, field, fieldClass, fieldObj);
        }
        if (isArray) {
          estimator.addField(FieldType.OBJECT_ARRAY, field);
          addArrayEstimator(byType, stack, field, fieldObj);
        } else if (Collection.class.isAssignableFrom(fieldClass)) {
          estimator.addField(FieldType.COLLECTION, field);
          addCollectionEstimator(byType, stack, field, fieldClass, fieldObj);
        } else if (Map.class.isAssignableFrom(fieldClass)) {
          estimator.addField(FieldType.MAP, field);
          addMapEstimator(byType, stack, field, fieldClass, fieldObj);
        } else {
          estimator.addField(FieldType.OTHER, field);
          addToProcessing(byType, stack, fieldObj, fieldClass);
        }
      }
      estimator.directSize =
          (int) JavaDataModel.alignUp(estimator.directSize, memoryModel.memoryAlign());
    }
  }
Creates size estimators for java objects. The estimators attempt to do most of the reflection
work at initialization time, and also take some shortcuts, to minimize the amount of work done
during the actual estimation.
TODO: clean up
storage-api/src/java/org/apache/hadoop/hive/ql/util/IncrementalObjectSizeEstimator.java