Hotspot源码解析-第十七章-虚拟机万物创建(三)

17.4 Java堆空间内存分配

分配Java堆内存前,我们先通过两图来了解下C堆、Java堆、内核空间、native本地空间的关系。

1、从图17-1来看,Java堆的分配其实就是从Java进程运行时堆中选中一块内存区域来映射

2、从图17-2,可以看中各内存空间的关系,当然实际的内存区域比这个复杂的多,这里只是概括说明

图17-1
在这里插入图片描述

图17-2
在这里插入图片描述

17.4.1 genCollectedHeap.cpp

17.4.1.1 GenCollectedHeap::initialize
jint GenCollectedHeap::initialize() {
  // 这一步只是对c2编译器开通使用时,做一些参数赋值操作,这里就不展开讲
  CollectedHeap::pre_initialize();

  // 这里获取分代数_n_gens,就是2
  int i;
  _n_gens = gen_policy()->number_of_generations();

  // 保证2个值相等wordSize和HeapWordSize分别是在操作系统和Java堆中代表一个字word占用内存的大小,这两个值必然相同,否则出错
  guarantee(HeapWordSize == wordSize, "HeapWordSize must equal wordSize");

  // Java堆的对齐值,这个在`章节17.2.1.1`中有介绍
  size_t gen_alignment = Generation::GenGrain;
 // 获取分代对象数组,这个在`章节17.2.1.1`中有介绍,数组元素就2个,索引0元素表示年轻代,索引1元素表示老年代
  _gen_specs = gen_policy()->generations();

  // 分别遍历新生代和老年代,并设置各自分代的空间大小(初始值和最大值),同时确保内存对齐
  for (i = 0; i < _n_gens; i++) {
    _gen_specs[i]->align(gen_alignment);
  }

  // 下面才是给Java堆分配空间

  char* heap_address;
  size_t total_reserved = 0;
  int n_covered_regions = 0;
  ReservedSpace heap_rs;
  // 这是最外层Java堆的内存对齐值
  size_t heap_alignment = collector_policy()->heap_alignment();
  // 分配java堆内存,看`章节17.4.1.2`
  heap_address = allocate(heap_alignment, &total_reserved,
                          &n_covered_regions, &heap_rs);

  if (!heap_rs.is_reserved()) {
    vm_shutdown_during_initialization(
      "Could not reserve enough space for object heap");
    return JNI_ENOMEM;
  }
  // 将分配的Java堆内存,用 MemRegion 内存区域对象管理起来
  _reserved = MemRegion((HeapWord*)heap_rs.base(),
                        (HeapWord*)(heap_rs.base() + heap_rs.size()));

  // 参数赋值
  _reserved.set_word_size(0);
  _reserved.set_start((HeapWord*)heap_rs.base()); // Java堆内存的首地址
  size_t actual_heap_size = heap_rs.size(); // Java堆内存大小
    // Java堆内存的限制地址,也就是不能超过这条线
  _reserved.set_end((HeapWord*)(heap_rs.base() + actual_heap_size)); 
  // 接下来就是把Java堆按分代算法,分成4个区域(新生代、S1、S2、老年代)管理起来,这块逻辑,我们放`第十八章`讲
  _rem_set = collector_policy()->create_rem_set(_reserved, n_covered_regions);
  set_barrier_set(rem_set()->bs());

  _gch = this;

  for (i = 0; i < _n_gens; i++) {
    ReservedSpace this_rs = heap_rs.first_part(_gen_specs[i]->max_size(), false, false);
    _gens[i] = _gen_specs[i]->init(this_rs, i, rem_set());
    heap_rs = heap_rs.last_part(_gen_specs[i]->max_size());
  }
  clear_incremental_collection_failed();

#if INCLUDE_ALL_GCS
  // If we are running CMS, create the collector responsible
  // for collecting the CMS generations.
  if (collector_policy()->is_concurrent_mark_sweep_policy()) {
    bool success = create_cms_collector();
    if (!success) return JNI_ENOMEM;
  }
#endif // INCLUDE_ALL_GCS

  return JNI_OK;
}
17.4.1.2 GenCollectedHeap::allocate
char* GenCollectedHeap::allocate(size_t alignment,
                                 size_t* _total_reserved,
                                 int* _n_covered_regions,
                                 ReservedSpace* heap_rs){
  const char overflow_msg[] = "The size of the object heap + VM data exceeds "
    "the maximum representable size";

  // Now figure out the total size.
  size_t total_reserved = 0;
  int n_covered_regions = 0;
  const size_t pageSize = UseLargePages ?
      os::large_page_size() : os::vm_page_size();

  assert(alignment % pageSize == 0, "Must be");
  // 遍历_gen_specs,求得新生代和老年代的分配大小
  for (int i = 0; i < _n_gens; i++) {
    total_reserved += _gen_specs[i]->max_size();
    if (total_reserved < _gen_specs[i]->max_size()) {
      vm_exit_during_initialization(overflow_msg);
    }
    n_covered_regions += _gen_specs[i]->n_covered_regions();  // 最终为2
  }
  assert(total_reserved % alignment == 0,
         err_msg("Gen size; total_reserved=" SIZE_FORMAT ", alignment="
                 SIZE_FORMAT, total_reserved, alignment));

  // Needed until the cardtable is fixed to have the right number
  // of covered regions.
  n_covered_regions += 2;  // 再加2,就是4,也就是把堆最终分成4个区(新生代、S1、S2、老年代)

  *_total_reserved = total_reserved;
  *_n_covered_regions = n_covered_regions;
  // 分配内存,实现细节看`章节17.4.2`
  *heap_rs = Universe::reserve_heap(total_reserved, alignment);
  return heap_rs->base();
}

17.4.2 universe.cpp

17.4.2.1 Universe::reserve_heap
ReservedSpace Universe::reserve_heap(size_t heap_size, size_t alignment) {
  assert(alignment <= Arguments::conservative_max_heap_alignment(),
      err_msg("actual alignment " SIZE_FORMAT " must be within maximum heap alignment " SIZE_FORMAT,
          alignment, Arguments::conservative_max_heap_alignment()));
  // 通过内存对齐,得到要分配的空间大小
  size_t total_reserved = align_size_up(heap_size, alignment);
  assert(!UseCompressedOops || (total_reserved <= (OopEncodingHeapMax - os::vm_page_size())),
      "heap size is too big for compressed oops");
  // 大页时考虑,本系列文章中不考虑大而情况,忽略
  bool use_large_pages = UseLargePages && is_size_aligned(alignment, os::large_page_size());
  assert(!UseLargePages
      || UseParallelGC
      || use_large_pages, "Wrong alignment to use large pages");
  // 取出Java堆的基址base的值,32位机器时,就是0,实现细节看`章节17.4.2.2`
  char* addr = Universe::preferred_heap_base(total_reserved, alignment, Universe::UnscaledNarrowOop);
  // 创建一个ReservedHeapSpace对象,该对象就是用来保留连续内存地址范围空间的数据结构,实现细节看`章节17.4.3`
  ReservedHeapSpace total_rs(total_reserved, alignment, use_large_pages, addr);

  if (UseCompressedOops) {
    if (addr != NULL && !total_rs.is_reserved()) {
      // Failed to reserve at specified address - the requested memory
      // region is taken already, for example, by 'java' launcher.
      // Try again to reserver heap higher.
      addr = Universe::preferred_heap_base(total_reserved, alignment, Universe::ZeroBasedNarrowOop);

      ReservedHeapSpace total_rs0(total_reserved, alignment,
          use_large_pages, addr);

      if (addr != NULL && !total_rs0.is_reserved()) {
        // Failed to reserve at specified address again - give up.
        addr = Universe::preferred_heap_base(total_reserved, alignment, Universe::HeapBasedNarrowOop);
        assert(addr == NULL, "");

        ReservedHeapSpace total_rs1(total_reserved, alignment,
            use_large_pages, addr);
        total_rs = total_rs1;
      } else {
        total_rs = total_rs0;
      }
    }
  }

  if (!total_rs.is_reserved()) {
    vm_exit_during_initialization(err_msg("Could not reserve enough space for " SIZE_FORMAT "KB object heap", total_reserved/K));
    return total_rs;
  }

  if (UseCompressedOops) {
    // Universe::initialize_heap() will reset this to NULL if unscaled
    // or zero-based narrow oops are actually used.
    address base = (address)(total_rs.base() - os::vm_page_size());
    Universe::set_narrow_oop_base(base);
  }
  // 返回total_rs
  return total_rs;
}
17.4.2.2 Universe::preferred_heap_base
char* Universe::preferred_heap_base(size_t heap_size, size_t alignment, NARROW_OOP_MODE mode) {
  assert(is_size_aligned((size_t)OopEncodingHeapMax, alignment), "Must be");
  assert(is_size_aligned((size_t)UnscaledOopHeapMax, alignment), "Must be");
  assert(is_size_aligned(heap_size, alignment), "Must be");

  // HeapBaseMinAddress 是操作系统明确设定的堆内存的最低地址限制,默认设置的是2*G,这里按alignment对齐,把HeapBaseMinAddress的值按alignment对齐后,作为堆内存的最低地址
  uintx heap_base_min_address_aligned = align_size_up(HeapBaseMinAddress, alignment);

  size_t base = 0;
#ifdef _LP64  // 下面是对64位机器及使用压缩指针时的实现,我们只讲32位的,这块逻辑略过
  if (UseCompressedOops) {
    assert(mode == UnscaledNarrowOop  ||
           mode == ZeroBasedNarrowOop ||
           mode == HeapBasedNarrowOop, "mode is invalid");
    const size_t total_size = heap_size + heap_base_min_address_aligned;
    // Return specified base for the first request.
    if (!FLAG_IS_DEFAULT(HeapBaseMinAddress) && (mode == UnscaledNarrowOop)) {
      base = heap_base_min_address_aligned;

    // If the total size is small enough to allow UnscaledNarrowOop then
    // just use UnscaledNarrowOop.
    } else if ((total_size <= OopEncodingHeapMax) && (mode != HeapBasedNarrowOop)) {
      if ((total_size <= UnscaledOopHeapMax) && (mode == UnscaledNarrowOop) &&
          (Universe::narrow_oop_shift() == 0)) {
        // Use 32-bits oops without encoding and
        // place heap's top on the 4Gb boundary
        base = (UnscaledOopHeapMax - heap_size);
      } else {
        // Can't reserve with NarrowOopShift == 0
        Universe::set_narrow_oop_shift(LogMinObjAlignmentInBytes);

        if (mode == UnscaledNarrowOop ||
            mode == ZeroBasedNarrowOop && total_size <= UnscaledOopHeapMax) {

          // Use zero based compressed oops with encoding and
          // place heap's top on the 32Gb boundary in case
          // total_size > 4Gb or failed to reserve below 4Gb.
          uint64_t heap_top = OopEncodingHeapMax;

          // For small heaps, save some space for compressed class pointer
          // space so it can be decoded with no base.
          if (UseCompressedClassPointers && !UseSharedSpaces &&
              OopEncodingHeapMax <= 32*G) {

            uint64_t class_space = align_size_up(CompressedClassSpaceSize, alignment);
            assert(is_size_aligned((size_t)OopEncodingHeapMax-class_space,
                   alignment), "difference must be aligned too");
            uint64_t new_top = OopEncodingHeapMax-class_space;

            if (total_size <= new_top) {
              heap_top = new_top;
            }
          }

          // Align base to the adjusted top of the heap
          base = heap_top - heap_size;
        }
      }
    } else {
      // UnscaledNarrowOop encoding didn't work, and no base was found for ZeroBasedOops or
      // HeapBasedNarrowOop encoding was requested.  So, can't reserve below 32Gb.
      Universe::set_narrow_oop_shift(LogMinObjAlignmentInBytes);
    }

    // Set narrow_oop_base and narrow_oop_use_implicit_null_checks
    // used in ReservedHeapSpace() constructors.
    // The final values will be set in initialize_heap() below.
    if ((base != 0) && ((base + heap_size) <= OopEncodingHeapMax)) {
      // Use zero based compressed oops
      Universe::set_narrow_oop_base(NULL);
      // Don't need guard page for implicit checks in indexed
      // addressing mode with zero based Compressed Oops.
      Universe::set_narrow_oop_use_implicit_null_checks(true);
    } else {
      // Set to a non-NULL value so the ReservedSpace ctor computes
      // the correct no-access prefix.
      // The final value will be set in initialize_heap() below.
      Universe::set_narrow_oop_base((address)UnscaledOopHeapMax);
#if defined(_WIN64) || defined(AIX)
      if (UseLargePages) {
        // Cannot allocate guard pages for implicit checks in indexed
        // addressing mode when large pages are specified on windows.
        Universe::set_narrow_oop_use_implicit_null_checks(false);
      }
#endif //  _WIN64
    }
  }
#endif

  assert(is_ptr_aligned((char*)base, alignment), "Must be");
  // 最终返回base,在32位机器时,虚拟机就是返回0
  return (char*)base; // also return NULL (don't care) for 32-bit VM
}

17.4.3 virtualspace.cpp

17.4.3.1 ReservedHeapSpace::ReservedHeapSpace
ReservedHeapSpace::ReservedHeapSpace(size_t size, size_t alignment,
                                     bool large, char* requested_address) :
  /* 先调用父类构造函数
  */
  ReservedSpace(size, alignment, large,
                requested_address,
                (UseCompressedOops && (Universe::narrow_oop_base() != NULL) &&
                 Universe::narrow_oop_use_implicit_null_checks()) ?
                  lcm(os::vm_page_size(), alignment) : 0) {
  if (base() != NULL) {
    MemTracker::record_virtual_memory_type((address)base(), mtJavaHeap);
  }

  // Only reserved space for the java heap should have a noaccess_prefix
  // if using compressed oops.
  protect_noaccess_prefix(size);
}
17.4.3.2 ReservedSpace::ReservedSpace
ReservedSpace::ReservedSpace(size_t size, size_t alignment,
                             bool large,
                             char* requested_address,
                             const size_t noaccess_prefix) {
  initialize(size+noaccess_prefix, alignment, large, requested_address,
             noaccess_prefix, false);
}
17.4.3.3 ReservedSpace::initialize

入口函数: ReservedHeapSpace total_rs(total_reserved, alignment, use_large_pages, addr);

参数:

total_reserved 对应 size:空间大小

alignment 对应 alignment:内存对齐值

use_large_pages 对应 large:这里不考虑大页,就设置为false

addr 对应 requested_address:32位时,addr为0

noaccess_prefix 为 0

executable 为 false

void ReservedSpace::initialize(size_t size, size_t alignment, bool large,
                               char* requested_address,
                               const size_t noaccess_prefix,
                               bool executable) {
  // 看源码得知,这里就是取page size(页大小),没什么逻辑
  const size_t granularity = os::vm_allocation_granularity();
  // 断言检验
  assert((size & (granularity - 1)) == 0,
         "size not aligned to os::vm_allocation_granularity()");
  assert((alignment & (granularity - 1)) == 0,
         "alignment not aligned to os::vm_allocation_granularity()");
  assert(alignment == 0 || is_power_of_2((intptr_t)alignment),
         "not a power of 2");
  // 取二者最大值对齐
  alignment = MAX2(alignment, (size_t)os::vm_page_size());

  // Assert that if noaccess_prefix is used, it is the same as alignment.
  assert(noaccess_prefix == 0 ||
         noaccess_prefix == alignment, "noaccess prefix wrong");

  _base = NULL;
  _size = 0;
  _special = false;
  _executable = executable;
  _alignment = 0;
  _noaccess_prefix = 0;
  if (size == 0) {
    return;
  }

  // 不存在大页,special 为 false
  bool special = large && !os::can_commit_large_page_memory();
  char* base = NULL;
  // 32位机器时 requested_address == 0,这条线也不会走
  if (requested_address != 0) {
    requested_address -= noaccess_prefix; // adjust requested address
    assert(requested_address != NULL, "huge noaccess prefix?");
  }
  // special为false,这个if不会走
  if (special) {

    base = os::reserve_memory_special(size, alignment, requested_address, executable);

    if (base != NULL) {
      if (failed_to_reserve_as_requested(base, requested_address, size, true)) {
        // OS ignored requested address. Try different address.
        return;
      }
      // Check alignment constraints.
      assert((uintptr_t) base % alignment == 0,
             err_msg("Large pages returned a non-aligned address, base: "
                 PTR_FORMAT " alignment: " PTR_FORMAT,
                 base, (void*)(uintptr_t)alignment));
      _special = true;
    } else {
      // failed; try to reserve regular memory below
      if (UseLargePages && (!FLAG_IS_DEFAULT(UseLargePages) ||
                            !FLAG_IS_DEFAULT(LargePageSizeInBytes))) {
        if (PrintCompressedOopsMode) {
          tty->cr();
          tty->print_cr("Reserve regular memory without large pages.");
        }
      }
    }
  }

  if (base == NULL) {
    if (requested_address != 0) {
      base = os::attempt_reserve_memory_at(size, requested_address);
      if (failed_to_reserve_as_requested(base, requested_address, size, false)) {
        // OS ignored requested address. Try different address.
        base = NULL;
      }
    } else {
      // 这一步就是通过系统调用mmap映射一块size大小的内存,Java堆内存就是mmap映射出来的
      base = os::reserve_memory(size, NULL, alignment);
    }
    // 映射失败,直接退出函数,分配Java堆内存失败
    if (base == NULL) return;

    // 验证对齐,为啥要验证呢,因为base是mmap映射后返回的内存首地址,这个地址是os自己的规则选取的一个地址,不一定能按照alignment对齐,所以这一定要验证
    if ((((size_t)base + noaccess_prefix) & (alignment - 1)) != 0) {
      // base没有对齐,只能释放刚才mmap映射的内存,然后重试
      if (!os::release_memory(base, size)) fatal("os::release_memory failed");
      // 确保对齐
      size = align_size_up(size, alignment);
      // 再次mmap映射内存,返回的base同样有上面一样的不对齐问题,所以这个函数中包含了手动对齐操作,细节看`章节17.4.3.4`
      base = os::reserve_memory_aligned(size, alignment);

      if (requested_address != 0 &&
          failed_to_reserve_as_requested(base, requested_address, size, false)) {
        // As a result of the alignment constraints, the allocated base differs
        // from the requested address. Return back to the caller who can
        // take remedial action (like try again without a requested address).
        assert(_base == NULL, "should be");
        return;
      }
    }
  }
  // Done
  _base = base;  // 最终拿到了Java堆的首地址
  _size = size;  // 最终拿到了Java堆的大小
  _alignment = alignment;  // 对齐值
  _noaccess_prefix = noaccess_prefix;  // 0

  // 断言判断
  assert(noaccess_prefix == 0 ||
         noaccess_prefix == _alignment, "noaccess prefix wrong");

  assert(markOopDesc::encode_pointer_as_mark(_base)->decode_pointer() == _base,
         "area must be distinguisable from marks for mark-sweep");
  assert(markOopDesc::encode_pointer_as_mark(&_base[size])->decode_pointer() == &_base[size],
         "area must be distinguisable from marks for mark-sweep");
}
17.4.3.4 os_posix.cpp->os::reserve_memory_aligned
char* os::reserve_memory_aligned(size_t size, size_t alignment) {
  assert((alignment & (os::vm_allocation_granularity() - 1)) == 0,
      "Alignment must be a multiple of allocation granularity (page size)");
  assert((size & (alignment -1)) == 0, "size must be 'alignment' aligned");

  size_t extra_size = size + alignment;
  assert(extra_size >= size, "overflow, size is too large to allow alignment");
  // mmap映射一块内存区域,返回首地址
  char* extra_base = os::reserve_memory(extra_size, NULL, alignment);

  if (extra_base == NULL) {
    return NULL;
  }

  // 手动对齐
  char* aligned_base = (char*) align_size_up((uintptr_t) extra_base, alignment);

  // [  |                                       |  ]
  // ^ extra_base
  //    ^ extra_base + begin_offset == aligned_base
  //     extra_base + begin_offset + size       ^
  //                       extra_base + extra_size ^
  // |<>| == begin_offset
  //                              end_offset == |<>|
  // 用对齐后的地址-mmap的首地址,得出与首地址的偏移值
  size_t begin_offset = aligned_base - extra_base;
  // 结束地址对齐后的偏移
  size_t end_offset = (extra_base + extra_size) - (aligned_base + size);
  // begin_offset > 0,表示确实有偏移,那就把extra_base到偏移的这部分释放掉,因为有新的首地址了
  if (begin_offset > 0) {
      os::release_memory(extra_base, begin_offset);
  }
  // end_offset > 0,表示确实有偏移,那就把end_offset偏移的这部分释放掉,因为有新的限制地址了
  if (end_offset > 0) {
      os::release_memory(extra_base + begin_offset + size, end_offset);
  }
  // 返回首地址
  return aligned_base;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/307996.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Springboot3(一、lambda、::的应用)

文章目录 一、使用lambda简化实例创建1.语法&#xff1a;2.示例&#xff1a;3.Function包3.1 有入参&#xff0c;有返回值【多功能函数】3.2 有入参&#xff0c;无返回值【消费者】3.3 无入参&#xff0c;有返回值【提供者】3.4 无入参&#xff0c;无返回值 二、类::方法的使用…

基于ssm运动会管理系统的设计与实现 【附源码】

基于ssm运动会管理系统的设计与实现 【附源码】 &#x1f345; 作者主页 央顺技术团队 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 文末获取源码联系方式 &#x1f4dd; 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuil…

C2-3.3.2 机器学习/深度学习——数据增强

C2-3.3.2 数据增强 参考链接 1、为什么要使用数据增强&#xff1f; ※总结最经典的一句话&#xff1a;希望模型学习的更稳健 当数据量不足时候&#xff1a; 人工智能三要素之一为数据&#xff0c;但获取大量数据成本高&#xff0c;但数据又是提高模型精度和泛化效果的重要因…

工业智能网关:HiWoo Box远程采集设备数据

工业智能网关&#xff1a;HiWoo Box远程采集设备数据 在工业4.0和智能制造的浪潮下&#xff0c;工业互联网已成为推动产业升级、提升生产效率的关键。而在这其中&#xff0c;工业智能网关扮演着至关重要的角色。今天&#xff0c;我们就来深入探讨一下工业智能网关。 一、什么…

Apache JMeter 5.5: 新手指南

如何获取并运行 JMeter 首先&#xff0c;要使用 JMeter&#xff0c;你需要从官网获取软件包。前往 Apache JMeter 的官方页面&#xff0c;然后下载所 需的压缩文件。 配置和启动 JMeter 获取了 JMeter 后&#xff0c;由于它是无需安装即可使用的工具&#xff0c;直接解压下载…

申请企业通配符SSL证书流程

通配符SSL证书&#xff0c;又叫泛域名SSL证书&#xff0c;可以用一张SSL证书同时保护主域名以及主域名下的所有子域名。按照验证方式可以将通配符SSL证书分为DV通配符SSL证书和OV通配符SSL证书。其中OV通配符SSL证书只支持企事业单位申请&#xff0c;又称之为OV企业型通配符SSL…

贝蒂详解<string.h>(下)

✨✨欢迎大家来到贝蒂大讲堂✨✨ ​​​​&#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C语言学习 贝蒂的主页&#xff1a;Betty‘s blog 目录 1. 简介 2. memset()函数 2.1用法 2.2实例 2.3 实现me…

快速了解云计算与云原生

快速了解云计算与云原生 云计算云原生DevOps容器持续交付微服务 云计算 在讲云原生之前&#xff0c;先来讲讲云计算 其中云原生属于技术架构理念&#xff0c;而云计算提供应用所需的基础资源&#xff0c;云计算是云原生的基础&#xff0c;两者是相辅相成的 云计算简单来说&a…

竞赛保研 基于深度学习的水果识别 设计 开题 技术

1 前言 Hi&#xff0c;大家好&#xff0c;这里是丹成学长&#xff0c;今天做一个 基于深度学习的水果识别demo 这是一个较为新颖的竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-senior/pos…

JVM基础(3)——JVM垃圾回收机制

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 学习必须往深处挖&…

复合机器人作为一种新型的智能制造装备高效、精准和灵活的生产方式

随着汽车制造业的快速发展&#xff0c;对于高效、精准和灵活的生产方式需求日益增强。复合机器人作为一种新型的智能制造装备&#xff0c;以其独特的优势在汽车制造中发挥着越来越重要的作用。因此&#xff0c;富唯智能顺应时代的发展趋势&#xff0c;研发出了ICR系列的复合机器…

计算机毕业设计 | SpringBoot航空订票 机票预定购买系统(附源码)

1&#xff0c; 概述 1.1 选题目的 目前&#xff0c;国内航空公司的数量和规模都在扩大&#xff0c;国外航空公司也纷纷着陆中国&#xff0c;这些航空公司之间的竞争可谓日益激烈。配备一个安全、高效、灵活、可靠的客户服务中心系统对于航空公司加强客户服务质量&#xff0c;…

使用Android Compose实现网格列表滑到底部的提示信息展示

文章目录 概述1 效果对比1.1 使用添加Item的办法&#xff1a;1.2 使用自定义的方法 2. 效果实现2.1 列表为空时的提示页面实现2.2 添加Item的方式代码实现2.3 使用自定义的方式实现 3. UI工具类 概述 目前大多数的APP都会使用列表的方式来呈现内容&#xff0c;例如淘宝&#x…

解决Echarts y轴文本超出容器问题

解决Echarts y轴文本超出容器问题 一开始好好的 数据变多之后就被挤出去了 解决方法&#xff1a; // echarts的grid属性 主要就是containLabel这个属性的配置 不设置的话他默认是false, 主要是包含是否包含刻度标签grid: {left: "5%",right: "10%",botto…

linux 里面在docker 里面安装pg 数据库(亲测有效)

目录 1 上传 1 上传 上传之后tar 包&#xff0c;将他变成镜像 输入docker images,发现目前是没有镜像的&#xff0c;现在将tar 包变成镜像 docker load -i postgresql.tar以上就将tar 包变成镜像了 现在在宿主机找一个地方&#xff0c;存放数据库的数据 /home/softinstall/…

全网独家:基于openEuler-20.03-LTS-SP4底包构建opengaussV5.0.1LTS的单机极简版数据库容器

本文尝试基于openEuler-20.03-LTS-SP4底包构建opengaussV5.0.1LTS的单机版极简版数据库容器。 一、软件包源 1、openEuler-20.03-LTS容器底包 openEuler-20.03-LTS-SP4 下载链接 sha256:24d8f51c1f3a79eb975c4e498cadd9055bfd708d66c15935ec46664d0f975a7b openEuler-dock…

@DependsOn:解析 Spring 中的依赖关系之艺术

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 DependsOn&#xff1a;解析 Spring 中的依赖关系之艺术 前言简介基础用法高级用法在 XML 配置中使用 DependsOn通过 Java Config 配置实现依赖管理 生命周期与初始化顺序Bean 生命周期的关键阶段&…

高照数量关系(一)—— 倍数特性、方程问题、周期问题

倍数特性 整除型 &#xff08;1&#xff09;口诀法&#xff1a;&#xff08;常用于3、4、5、9&#xff09;3/9看各个位数字之和&#xff0c;5看末位&#xff0c;4看末两位。 3/9 -> 看各位数字之和能否被3/9整除&#xff0c;例&#xff1a;124345 2/5 ->看数字末一位能…

【Linux】进程

----------------| 本文目录 |---------------- 1. 进程1.1 基本概念1.2 描述进程 - PCB1.2.1 task_struct - PCB的一种1.2.2 task_struct 内容分类 1.3 组织进程1.4 查看进程1.5 通过系统调用获取进程标示符1.6 通过系统调用创建进程 - fork初识 2. 进程状态2.1 看看Linux内核…

美创科技第59号安全实验室最新力作!《内网渗透实战攻略》出版发行

总结先进攻防实战经验&#xff0c;基于创新入侵生命周期模型&#xff0c;为提升渗透实战能力提供系统操作教程&#xff01;近期&#xff0c;美创科技创始人&CEO柳遵梁&#xff0c;美创第59号安全实验室&#xff08;王月兵、覃锦端、毛菲、刘聪等&#xff09;撰写的新书《内…
最新文章