OpenJDK ZGC 源码分析(五)对象分配

1. 简介

ZGC的对象分配流程大体与G1类似。分配流程图在JVM G1 源码分析(二)- 对象的堆空间分配内。
主要代码在src/hotspot/share/gc/shared目录下,为多个GC算法的共用代码。
ZGC定制逻辑由gc shared代码调用ZCollectedHeap、ZHeap等ZGC派生类实现。

2. 源码分析

2.1 allocate_new_tlab

当线程的TLAB可用内存不足时,MemAllocator的allocate_inside_tlab_slow会调用ZCollectedHeap的allocate_new_tlab申请新的TLAB。

  • 分配前,先对象字节对齐,默认8字节对齐
  • 调用ZHeap的alloc_tlab进行分配

zCollectedHeap.cpp

HeapWord* ZCollectedHeap::allocate_new_tlab(size_t min_size, size_t requested_size, size_t* actual_size) {
  const size_t size_in_bytes = ZUtils::words_to_bytes(align_object_size(requested_size));
  const uintptr_t addr = _heap.alloc_tlab(size_in_bytes);

  if (addr != 0) {
    *actual_size = requested_size;
  }

  return (HeapWord*)addr;
}

alloc_tlab在校验size合法性后,调用ZObjectAllocator的alloc_object进行分配。

  • TLAB的最大size为常量ZObjectSizeLimitSmall,即2MB

zHeap.inline.hpp

inline uintptr_t ZHeap::alloc_tlab(size_t size) {
  guarantee(size <= max_tlab_size(), "TLAB too large");
  return _object_allocator.alloc_object(size);
}

ZObjectAllocator的alloc_object根据需要分配的size分别调用alloc_small_object、alloc_medium_object、alloc_large_object,我会在mem_allocate章节逐一介绍。

  • 由于TLAB max size是2MB,会调用alloc_small_object进行分配。

zObjectAllocator.cpp

uintptr_t ZObjectAllocator::alloc_object(size_t size, ZAllocationFlags flags) {
  if (size <= ZObjectSizeLimitSmall) {
    // Small
    return alloc_small_object(size, flags);
  } else if (size <= ZObjectSizeLimitMedium) {
    // Medium
    return alloc_medium_object(size, flags);
  } else {
    // Large
    return alloc_large_object(size, flags);
  }
}

uintptr_t ZObjectAllocator::alloc_object(size_t size) {
  assert(ZThread::is_java(), "Must be a Java thread");

  ZAllocationFlags flags;
  flags.set_no_reserve();

  if (!ZStallOnOutOfMemory) {
    flags.set_non_blocking();
  }

  return alloc_object(size, flags);
}

2.2 mem_allocate

当TLAB分配失败时,MemAllocator的allocate_outside_tlab会调用ZCollectedHeap的mem_allocate进行慢分配。

  • size需要对象对齐,默认8字节对齐
  • mem_allocate调用ZHeap的alloc_object,进行对象分配。

zCollectedHeap.cpp

HeapWord* ZCollectedHeap::mem_allocate(size_t size, bool* gc_overhead_limit_was_exceeded) {
  const size_t size_in_bytes = ZUtils::words_to_bytes(align_object_size(size));
  return (HeapWord*)_heap.alloc_object(size_in_bytes);
}
  • 调用ZObjectAllocator的alloc_object进行分配;
  • 如果分配失败,则调用out_of_memory记录。

zHeap.inline.hpp

inline uintptr_t ZHeap::alloc_object(size_t size) {
  uintptr_t addr = _object_allocator.alloc_object(size);
  assert(ZAddress::is_good_or_null(addr), "Bad address");

  if (addr == 0) {
    out_of_memory();
  }

  return addr;
}

根据需要分配的size大小,决定调用哪个函数进行分配。

  • 当对象size小于等于256K时(2M / 8),在small page分配;
  • 当对象size小于等于4M时(32M / 8),在medium page分配;
  • 当对象size大于4M时,在large page分配。

zObjectAllocator.cpp

uintptr_t ZObjectAllocator::alloc_object(size_t size, ZAllocationFlags flags) {
  if (size <= ZObjectSizeLimitSmall) {
    // Small
    return alloc_small_object(size, flags);
  } else if (size <= ZObjectSizeLimitMedium) {
    // Medium
    return alloc_medium_object(size, flags);
  } else {
    // Large
    return alloc_large_object(size, flags);
  }
}

alloc_small_object和alloc_medium_object最后都会调用alloc_object_in_shared_page

  • 调用ZPage alloc_object_atomic在页上分配对象
  • 如果失败,则先分配新页,然后在新页上分配对象

zObjectAllocator.cpp

uintptr_t ZObjectAllocator::alloc_object_in_shared_page(ZPage** shared_page,
                                                        uint8_t page_type,
                                                        size_t page_size,
                                                        size_t size,
                                                        ZAllocationFlags flags) {
  uintptr_t addr = 0;
  ZPage* page = *shared_page;

  if (page != NULL) {
    addr = page->alloc_object_atomic(size);
  }

  if (addr == 0) {
    // Allocate new page
    ZPage* const new_page = alloc_page(page_type, page_size, flags);
    if (new_page != NULL) {
      // Allocate object before installing the new page
      addr = new_page->alloc_object(size);

    retry:
      // Install new page
      ZPage* const prev_page = Atomic::cmpxchg(new_page, shared_page, page);
      if (prev_page != page) {
        if (prev_page == NULL) {
          // Previous page was retired, retry installing the new page
          page = prev_page;
          goto retry;
        }

        // Another page already installed, try allocation there first
        const uintptr_t prev_addr = prev_page->alloc_object_atomic(size);
        if (prev_addr == 0) {
          // Allocation failed, retry installing the new page
          page = prev_page;
          goto retry;
        }

        // Allocation succeeded in already installed page
        addr = prev_addr;

        // Undo new page allocation
        ZHeap::heap()->undo_alloc_page(new_page);
      }
    }
  }

  return addr;
}

alloc_object_atomic没有锁,而是通过自旋+CAS实现并发控制,自旋使用CAS分配对象,直到成功或Page没有足够的空间

zPage.inline.hpp

inline uintptr_t ZPage::alloc_object_atomic(size_t size) {
  assert(is_allocating(), "Invalid state");

  const size_t aligned_size = align_up(size, object_alignment());
  uintptr_t addr = top();

  for (;;) {
    const uintptr_t new_top = addr + aligned_size;
    if (new_top > end()) {
      // Not enough space left
      return 0;
    }

    const uintptr_t prev_top = Atomic::cmpxchg(new_top, &_top, addr);
    if (prev_top == addr) {
      // Success
      return ZAddress::good(addr);
    }

    // Retry
    addr = prev_top;
  }
}

大对象分配略有不同

  • 对象size按2MB对齐
  • 分配large page
  • 在large page上分配对象

zObjectAllocator.cpp

uintptr_t ZObjectAllocator::alloc_large_object(size_t size, ZAllocationFlags flags) {
  assert(ZThread::is_java(), "Should be a Java thread");

  uintptr_t addr = 0;

  // Allocate new large page
  const size_t page_size = align_up(size, ZPageSizeMin);
  ZPage* const page = alloc_page(ZPageTypeLarge, page_size, flags);
  if (page != NULL) {
    // Allocate the object
    addr = page->alloc_object(size);
  }

  return addr;
}

3. 引用

OpenJDK 12 源代码

你可能感兴趣的:(JVM,OpenJDK,12,ZGC源码分析)