K8s 中 Pod OOMKilled 原因

news2024/11/18 4:19:01

目录

Exit Code 137

解决方案

JVM 感知 cgroup 限制

使用 JDK9 的容器感知机制尝试

问题分析

容器内部感知 CGroup 资源限制

在 Java10 中,改进了容器集成

JVM 参数 MaxDirectMemorySize

-XX:MaxDirectMemorySize 的默认值是什么?

其他获取 maxDirectMemory 的值的 API 方法

内存分析问题

-XX:+DisableExplicitGC 与 NIO 的 direct memory


我们日常的工作当中,通常应用都会采用 Kubernetes 进行容器化部署,但是总是会出现一些问题,例如,JVM 堆小于 Docker 容器中设置的内存大小和 Kubernetes 的内存大小,但是还是会被 OOMKilled。在此我们介绍一下 K8s 的 OOMKilled 的 Exit Code 编码。

Exit Code 137

  • 表明容器收到了 SIGKILL 信号,进程被杀掉,对应 kill -9,引发 SIGKILL 的是 docker kill。这可以由用户或由 docker 守护程序来发起,手动执行:docker kill

  • 137 比较常见,如果 pod 中的 limit 资源设置较小,会运行内存不足导致 OOMKilled,此时 state 中的 ”OOMKilled” 值为 true,你可以在系统的 dmesg -T 中看到 OOM 日志。

因为我的 heap 大小肯定是小于 Docker 容器以及 Pod 的大小的,为啥还是会出现 OOMKilled?

这种问题常发生在 JDK8u131 或者 JDK9 版本之后所出现在容器中运行 JVM 的问题:在大多数情况下,JVM 将一般默认会采用宿主机 Node 节点的内存为 Native VM 空间(其中包含了堆空间、直接内存空间以及栈空间),而并非是是容器的空间为标准。

例如我的机器:

$ docker run -m 100MB openjdk:8u121 java -XshowSettings:vm -version
VM settings:
    Max. Heap Size (Estimated): 444.50M
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

以上的信息出现了矛盾,我们在运行的时候将容器内存设置为 100MB,而 -XshowSettings:vm 打印出的 JVM 将最大堆大小为 444M,如果按照这个内存进行分配内存的话很可能会导致节点主机在某个时候杀死我的 JVM。

解决方案

JVM 感知 cgroup 限制

一种方法解决 JVM 内存超限的问题,这种方法可以让 JVM 自动感知 docker 容器的 cgroup  限制,从而动态的调整堆内存大小。JDK8u131 在 JDK9 中有一个很好的特性,即 JVM 能够检测在 Docker 容器中运行时有多少内存可用。为了使 jvm 保留根据容器规范的内存,必须设置标志 -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap

注意:如果将这两个标志与 Xms 和 Xmx 标志一起设置,那么 jvm 的行为将是什么?-Xmx 标志将覆盖-XX:+ UseCGroupMemoryLimitForHeap 标志。

总结一下:

  • 标志 -XX:+UseCGroupMemoryLimitForHeap 使 JVM 可以检测容器中的最大堆大小。

  • -Xmx 标志将最大堆大小设置为固定大小。

  • 除了 JVM 的堆空间,还会对于非堆和 jvm 的东西,还会有一些额外的内存使用情况。

使用 JDK9 的容器感知机制尝试

$ docker run -m 100MB openjdk:8u131 java \
  -XX:+UnlockExperimentalVMOptions \
  -XX:+UseCGroupMemoryLimitForHeap \
  -XshowSettings:vm -version
VM settings:
    Max. Heap Size (Estimated): 44.50M
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

可以看出来通过内存感知之后,JVM 能够检测到容器只有 100MB,并将最大堆设置为 44M。我们调整一下内存大小看看是否可以实现动态化调整和感知内存分配,如下所示。

$ docker run -m 1GB openjdk:8u131 java \
  -XX:+UnlockExperimentalVMOptions \
  -XX:+UseCGroupMemoryLimitForHeap \
  -XshowSettings:vm -version
VM settings:
    Max. Heap Size (Estimated): 228.00M
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

我们设置了容器有 1GB 内存分配,而 JVM 使用 228M 作为最大堆。因为容器中除了 JVM 之外没有其他进程在运行,所以我们还可以进一步扩大一下对于 Heap 堆的分配?

$ docker run -m 1GB openjdk:8u131 java \
  -XX:+UnlockExperimentalVMOptions \
  -XX:+UseCGroupMemoryLimitForHeap \
  -XX:MaxRAMFraction=1 -XshowSettings:vm -version
VM settings:
    Max. Heap Size (Estimated): 910.50M
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

在较低的版本的时候可以使用 -XX:MaxRAMFraction 参数,它告诉 JVM 使用可用内存 /MaxRAMFract 作为最大堆。使用 -XX:MaxRAMFraction=1,我们将几乎所有可用内存用作最大堆。从上面的结果可以看出来内存分配已经可以达到了 910.50M。

问题分析
  1. 最大堆占用总内存是否仍然会导致你的进程因为内存的其他部分(如“元空间”)而被杀死?

答案:MaxRAMFraction=1 仍将为其他非堆内存留出一些空间。

但如果容器使用堆外内存,这可能会有风险,因为几乎所有的容器内存都分配给了堆。您必须将-XX:MaxRAMFraction=2 设置为堆只使用 50% 的容器内存,或者使用 Xmx。

容器内部感知 CGroup 资源限制

Docker1.7 开始将容器 cgroup 信息挂载到容器中,所以应用可以从  /sys/fs/cgroup/memory/memory.limit_in_bytes 等文件获取内存、  CPU 等设置,在容器的应用启动命令中根据 Cgroup 配置正确的资源设置 -Xmx, -XX:ParallelGCThreads 等参数

在 Java10 中,改进了容器集成
  • Java10+ 废除了 -XX:MaxRAM 参数,因为 JVM 将正确检测该值。在 Java10 中,改进了容器集成。无需添加额外的标志,JVM 将使用 1/4 的容器内存用于堆。

  • java10+ 确实正确地识别了内存的 Docker 限制,但您可以使用新的标志 MaxRAMPercentage(例如:-XX:MaxRAMPercentage=75)而不是旧的 MaxRAMFraction,以便更精确地调整堆的大小,而不是其余的(堆栈、本机…)

  • java10+ 上的 UseContainerSupport 选项,而且是默认启用的,不用设置。同时  UseCGroupMemoryLimitForHeap 这个就弃用了,不建议继续使用,同时还可以通过 -XX:InitialRAMPercentage-XX:MaxRAMPercentage-XX:MinRAMPercentage 这些参数更加细腻的控制 JVM 使用的内存比率。

Java 程序在运行时会调用外部进程、申请 Native Memory 等,所以即使是在容器中运行 Java  程序,也得预留一些内存给系统的。所以 -XX:MaxRAMPercentage  不能配置得太大。当然仍然可以使用 -XX:MaxRAMFraction=1 选项来压缩容器中的所有内存。


通过前面的讲解我们知道了如何设置和控制 Java 应用对应的堆内存和容器内存的之间的关系,进而防止 JVM 的堆内存超过了容器内存,避免容器出现 OOMKilled 的情况。但是在整个 JVM 进程体系而言,不仅仅只包含了 Heap 堆内存,其实还有其他相关的内存存储空间是需要我们考虑的,一边防止这些内存空间会造成我们的容器内存溢出的场景,正如下图所示。

图片

接下来我们需要进行分析出 heap 之外的一部分就是对外内存就是 Off Heap Space,也就是 Direct buffer  memory 堆外内存。主要通过的方式就是采用 Unsafe 方式进行申请内存,大多数场景也会通过 Direct  ByteBuffer 方式进行获取。好废话不多说进入正题。

JVM 参数 MaxDirectMemorySize

我们先研究一下 jvm 的 -XX:MaxDirectMemorySize,该参数指定了 DirectByteBuffer 能分配的空间的限额,如果没有显示指定这个参数启动 jvm,默认值是 xmx 对应的值(低版本是减去幸存区的大小)。

DirectByteBuffer 对象是一种典型的”冰山对象”,在堆中存在少量的泄露的对象,但其下面连接用堆外内存,这种情况容易造成内存的大量使用而得不到释放

-XX:MaxDirectMemorySize

-XX:MaxDirectMemorySize=size 用于设置 New I/O (java.nio) direct-buffer allocations 的最大大小,size 的单位可以使用 k/Km/Mg/G;如果没有设置该参数则默认值为  0,意味着 JVM 自己自动给 NIO direct-buffer allocations 选择最大大小。

-XX:MaxDirectMemorySize 的默认值是什么?

在 sun.misc.VM 中,它是 Runtime.getRuntime.maxMemory(),这就是使用-Xmx 配置的内容。而对应的 JVM 参数如何传递给 JVM 底层的呢?主要通过的是 hotspot/share/prims/jvm.cpp。我们来看一下 jvm.cpp 的 JVM 源码来分一下。

  // Convert the -XX:MaxDirectMemorySize= command line flag
  // to the sun.nio.MaxDirectMemorySize property.
  // Do this after setting user properties to prevent people
  // from setting the value with a -D option, as requested.
  // Leave empty if not supplied
  if (!FLAG_IS_DEFAULT(MaxDirectMemorySize)) {
    char as_chars[256];
    jio_snprintf(as_chars, sizeof(as_chars), JULONG_FORMAT, MaxDirectMemorySize);
    Handle key_str = java_lang_String::create_from_platform_dependent_str("sun.nio.MaxDirectMemorySize", CHECK_NULL);
    Handle value_str  = java_lang_String::create_from_platform_dependent_str(as_chars, CHECK_NULL);
    result_h->obj_at_put(ndx * 2,  key_str());
    result_h->obj_at_put(ndx * 2 + 1, value_str());
    ndx++;
  }

jvm.cpp 里头有一段代码用于把 -XX:MaxDirectMemorySize 命令参数转换为 key 为 sun.nio.MaxDirectMemorySize 的属性。我们可以看出来他转换为了该属性之后,进行设置和初始化直接内存的配置。针对于直接内存的核心类就在www.docjar.com/html/api/su…[1]

public class VM {

    // the init level when the VM is fully initialized
    private static final int JAVA_LANG_SYSTEM_INITED     = 1;
    private static final int MODULE_SYSTEM_INITED        = 2;
    private static final int SYSTEM_LOADER_INITIALIZING  = 3;
    private static final int SYSTEM_BOOTED               = 4;
    private static final int SYSTEM_SHUTDOWN             = 5;


    // 0, 1, 2, ...
    private static volatile int initLevel;
    private static final Object lock = new Object();

    //......

    // A user-settable upper limit on the maximum amount of allocatable direct
    // buffer memory.  This value may be changed during VM initialization if
    // "java" is launched with "-XX:MaxDirectMemorySize=<size>".
    //
    // The initial value of this field is arbitrary; during JRE initialization
    // it will be reset to the value specified on the command line, if any,
    // otherwise to Runtime.getRuntime().maxMemory().
    //
    private static long directMemory = 64 * 1024 * 1024;

上面可以看出来 64MB 最初是任意设置的。在 -XX:MaxDirectMemorySize 是用来配置 NIO direct memory 上限用的 VM 参数。可以看一下 JVM 的这行代码。

product(intx, MaxDirectMemorySize, -1,
        "Maximum total size of NIO direct-buffer allocations")

但如果不配置它的话,direct memory 默认最多能申请多少内存呢?这个参数默认值是-1,显然不是一个“有效值”。所以真正的默认值肯定是从别的地方来的。

    // Returns the maximum amount of allocatable direct buffer memory.
    // The directMemory variable is initialized during system initialization
    // in the saveAndRemoveProperties method.
    //
    public static long maxDirectMemory() {
        return directMemory;
    }

    //......

    // Save a private copy of the system properties and remove
    // the system properties that are not intended for public access.
    //
    // This method can only be invoked during system initialization.
    public static void saveProperties(Map<String, String> props) {
        if (initLevel() != 0)
            throw new IllegalStateException("Wrong init level");

        // only main thread is running at this time, so savedProps and
        // its content will be correctly published to threads started later
        if (savedProps == null) {
            savedProps = props;
        }

        // Set the maximum amount of direct memory.  This value is controlled
        // by the vm option -XX:MaxDirectMemorySize=<size>.
        // The maximum amount of allocatable direct buffer memory (in bytes)
        // from the system property sun.nio.MaxDirectMemorySize set by the VM.
        // If not set or set to -1, the max memory will be used
        // The system property will be removed.
        String s = props.get("sun.nio.MaxDirectMemorySize");
        if (s == null || s.isEmpty() || s.equals("-1")) {
            // -XX:MaxDirectMemorySize not given, take default
            directMemory = Runtime.getRuntime().maxMemory();
        } else {
            long l = Long.parseLong(s);
            if (l > -1)
                directMemory = l;
        }
        // Check if direct buffers should be page aligned
        s = props.get("sun.nio.PageAlignDirectMemory");
        if ("true".equals(s))
            pageAlignDirectMemory = true;
    }
    //......
}

从上面的源码可以读取 sun.nio.MaxDirectMemorySize 属性,如果为 null 或者是空或者是 - 1,那么则设置为 Runtime.getRuntime().maxMemory();如果有设置 MaxDirectMemorySize 且值大于 -1,那么使用该值作为 directMemory 的值;而 VM 的 maxDirectMemory 方法则返回的是 directMemory 的值。

因为当 MaxDirectMemorySize 参数没被显式设置时它的值就是 -1,在 Java 类库初始化时 maxDirectMemory() 被 java.lang.System 的静态构造器调用,走的路径就是这条:

if (s.equals("-1")) {  
    // -XX:MaxDirectMemorySize not given, take default  
    directMemory = Runtime.getRuntime().maxMemory();  
}

而 Runtime.maxMemory() 在 HotSpot VM 里的实现是:

JVM_ENTRY_NO_ENV(jlong, JVM_MaxMemory(void))  
  JVMWrapper("JVM_MaxMemory");  
  size_t n = Universe::heap()->max_capacity();  
  return convert_size_t_to_jlong(n);  
JVM_END

这个 max_capacity() 实际返回的是 -Xmx 减去一个 survivor space 的预留大小。

结论分析说明

MaxDirectMemorySize 没显式配置的时候,NIO direct memory 可申请的空间的上限就是 -Xmx 减去一个 survivor  space 的预留大小。例如如果您不配置 -XX:MaxDirectMemorySize 并配置 -Xmx5g,则 "默认" MaxDirectMemorySize 也将是 5GB-survivor space 区,并且应用程序的总堆+直接内存使用量可能会增长到 5 + 5 = 10 Gb

其他获取 maxDirectMemory 的值的 API 方法

BufferPoolMXBean 及 JavaNioAccess.BufferPool (通过 SharedSecrets 获取) 的  getMemoryUsed 可以获取 direct memory 的大小;其中 java9 模块化之后,SharedSecrets 从原来的  sun.misc.SharedSecrets 变更到 java.base 模块下的 jdk.internal.access.SharedSecrets;要使用 --add-exports  java.base/jdk.internal.access=ALL-UNNAMED 将其导出到 UNNAMED,这样才可以运行:

public BufferPoolMXBean getDirectBufferPoolMBean(){
        return ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class)
                .stream()
                .filter(e -> e.getName().equals("direct"))
                .findFirst()
                .orElseThrow();
}
public JavaNioAccess.BufferPool getNioBufferPool(){
     return SharedSecrets.getJavaNioAccess().getDirectBufferPool();
}

内存分析问题

-XX:+DisableExplicitGC 与 NIO 的 direct memory
  • 用了 -XX:+DisableExplicitGC 参数后,System.gc() 的调用就会变成一个空调用,完全不会触发任何 GC(但是“函数调用”本身的开销还是存在的哦~)。

  • 做 ygc 的时候会将新生代里的不可达的 DirectByteBuffer 对象及其堆外内存回收了,但是无法对 old 里的 DirectByteBuffer 对象及其堆外内存进行回收,这也是我们通常碰到的最大的问题,如果有大量的 DirectByteBuffer 对象移到了 old,但是又一直没有做 cms gc 或者 full  gc,而只进行 ygc,那么我们的物理内存可能被慢慢耗光,但是我们还不知道发生了什么,因为 heap 明明剩余的内存还很多 (前提是我们禁用了 System.gc)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI先行者第四辑:AI落地铁路的快与慢

人工智能的模型不能解决所有的问题&#xff0c;它可能只能做到80%&#xff0c;剩下10%、20%都是靠你对项目的理解&#xff0c;一点点做针对性的优化。 本篇作者顾子晨&#xff0c;中国铁道科学研究院集团有限公司基础设施检测 研究所 高级工程师&#xff5c;AICA首席AI架构师培…

[蓝桥杯训练]———高精度乘法、除法

高精度乘法、除法 一、高精度乘法⭐1.1 初步理解1.1.1 高精度的定义1.1.2 为什么会有高精度1.1.3 高精度乘法的复杂度 1.2 思想讲解1.3 代码实现1.3.1 声明1.3.2 实现高精度乘法1.3.3 整体实现1.3.4 代码测试 二、高精度除法⭐2.1 初步理解2.2 思想讲解2.3 代码实现2.3.1 声明2…

ubuntu20.04配置OpenCV的C++环境

ubuntu20.04配置OpenCV的C环境 这里以opencv-3.4.16为例 复现https://github.com/raulmur/ORB_SLAM2此项目&#xff0c;需安装opencv及其他依赖&#xff0c;可见README.md详情 1.下载opencv源代码 https://opencv.org/releases/ 2.下载OpenCV的扩展包opencv_contrib&#x…

免杀原理(php)

免杀原理 0x01 前言 何为免杀&#xff0c;免杀就是一种逃脱杀毒软件查杀的方法&#xff0c;免杀的目的就是绕过“墙”&#xff0c;去执行危险的操作。那么如何绕过这堵“墙”&#xff0c;就是免杀的本质。有句俗话说得好“知己知彼&#xff0c;百战不殆”&#xff0c;想要用好…

【Vulnhub靶机】Jarbas--Jenkins

文章目录 信息收集主机发现端口扫描目录爆破 漏洞探测whatwebhash-identifierwhatweb 文档说明&#xff1a;https://www.vulnhub.com/entry/jarbas-1,232/ 靶机下载&#xff1a;Download (Mirror): 信息收集 主机发现 扫描C段 sudo nmap -sn 10.9.75.0/24端口扫描 sudo nma…

通过视频文件地址截取图像生成图片保存为封面图

安装 RPM Fusion 软件库 FFmpeg并不包含在 CentOS 官方软件库中&#xff0c;需要使用第三方软件库安装。可以使用 RPM Fusion 软件库来获取 FFmpeg。 首先&#xff0c;使用以下命令安装 RPM Fusion 软件库&#xff1a; sudo yum install epel-release -y sudo rpm -Uvh https…

BTC 复兴:Ordinals 带来创新活力,BitVM 与 BitStream 相继问世

除了备受瞩目的 ETF&#xff0c;今年 Bitcoin 生态迎来全新的发展活力和机遇。Ordinals 协议的横空出世&#xff0c;以此为基础诞生的 BRC20 协议给整个比特币生态带去了一波新的能量&#xff0c;迎来铭文热度高涨。而诸如 BitVM、BitStream 等新技术甫一问世&#xff0c;便引发…

vulnhub6

靶机地址&#xff1a;https://download.vulnhub.com/evilbox/EvilBox---One.ova 准备工作 可以先安装 kali 的字典: sudo apt install seclists ​ 或者直接输入 seclists​&#xff0c;系统会问你是否安装&#xff0c;输入 y 即可自动安装 733 x 3751414 x 723 ​ 默认路…

引用、动态内存分配、函数、结构体

引用 定义和初始化 **数据类型 &引用名 目标名;**引用和目标共用同一片空间&#xff08;相当于对一片空间取别名&#xff09;。 引用的底层实现&#xff1a;数据类型 * const p&#xff1b; ------> 常指针 int const *p; -----> 修饰 *p const int *p; ----->…

解决github无法访问的办法

方法/步骤 1.问题描述&#xff1a;能联网但不能访问github.com 2.找到hosts文件。地址&#xff1a;C:\Windows\System32\drivers\etc &#xff08;一般是在这的&#xff09; 3.不要直接在这修改hosts文件&#xff0c;需要将hosts文件复制粘贴到桌面&#xff08;或其它地方自…

Oracle 中的操作符

1.union:对两个结果集进行并集操作&#xff0c;不包括重复行&#xff0c;同时进行默认规则的排序&#xff1b; SELECT * FROM emp WHERE sal < 1500 UNION SELECT * FROM emp WHERE sal BETWEEN 1000 AND 2000 order by 1 2.union All&#xff1a;对两个结果集进行并集操…

Innux(特殊位与权限)

特殊位与权限 目录&#xff1a; 1. SUID 2. SGID 3. SBIT 4. 文件系统属性chattr权限 5. 管理员权限sudo 1. SUID 1.1 什么是SUID SUID只对二进制可执行文件才有效&#xff08;文件必须具备x权限&#xff09; 执行者对该程序有 x 权限 当前程序拥有SUID时&#xff0…

nova组件简介

目录 组件关系图 controller节点 openstack-nova-api.service: openstack-nova-conductor.service: openstack-nova-consoleauth.service: openstack-nova-novncproxy.service: openstack-nova-scheduler.service: openstack-nova-conductor.service详解 作用和功能&…

4.25每日一题(通过被积函数和积分区域(不等式)选正确的坐标系求二重积分)

一、正确画出积分区域&#xff1b;通过积分区域和被积函数选择方法 二、如何根据被积函数和积分区域正确选择通过极坐标还是根据直角坐标方程计算&#xff1a; &#xff08;1&#xff09;适合极坐标的积分区域&#xff1a;圆或者部分圆 &#xff08;2&#xff09;适合极坐标的…

续新的SSL证书

一、重新申请证书并下载证书&#xff1a; https://www.ename.net/ 二、IIS中在【服务器证书】中删除旧的证书&#xff0c;导入新的证书&#xff1b; 三、在站点的绑定选择新证书&#xff1b;

万字解析设计模式之责任链模式、状态模式

目录 一、责任链模式 1.1概述 1.2结构 1.3实现 1.4 优缺点 1.5应用场景 1.6源码解析 二、状态模式 2.1概述 2.2结构 2.3实现 2.4优缺点 2.5应用场景 三、责任链模式实验 任务描述 实现方式 编程要求 测试说明 四、状态模式实验 任务描述 实现方式 编程要…

2001-2022年上市公-供应链话语权测算数据(原始数据+处理代码Stata do文档+结果)

2001-2022年上市公-供应链话语权测算数据&#xff08;原始数据处理代码Stata do文档结果&#xff09; 1、时间&#xff1a;2001-2022年 2、指标&#xff1a;企业代码、股票代码、年份、股票简称、上市公司前五大供应商的采购额之和占企业当年总采购额的比例、上市公司前五大客…

林业无人机如何提升巡山护林效率?

在郁郁森林之上&#xff0c;一架无人机正盘旋在上空时刻观察着林区的情况。凭借复亚智能的全自动巡检系统&#xff0c;无人机巡山护林的巡视范围和反馈实时性得到了显著提升。 一、林业无人机&#xff1a;科技赋能森林防火 秋季林区时常发生火灾&#xff0c;林业无人机在森林防…

关于自动化测试的误区,你屡清楚了吗?

自动化测试作为软件测试的一种技术手段&#xff0c;时常被大家讨论。本人在自动化技术方面有过略有小成&#xff0c;今天聊一聊关于自动化的一些误区&#xff0c;以帮助新手能正确的了解和认识自动化一些概念。 测试的行为本质是什么&#xff1f; 为什么先从这个概念开始谈起&…

Codesys 数据结构:1.2.4 扩展数据类型之联合体(UNION) 类型详解

Codesys代码代写&#xff0c;程序开发&#xff0c;软件定制&#xff0c;bug修改&#xff0c;问题咨询&#xff1a; T宝搜索店铺【林磊教育】 定义&声明 联合体就是不同类型的变量占用同一个内存的数据结构&#xff1b; 其声明语法如下 TYPE <联合体名字>:UNION<…