【JVM故障问题排查心得】「内存诊断系列」JVM内存与Kubernetes中pod的内存、容器的内存不一致所引发的OOMKilled问题总结(下)

news2024/11/26 0:38:38

承接上文

之前文章根据《【JVM故障问题排查心得】「内存诊断系列」JVM内存与Kubernetes中pod的内存、容器的内存不一致所引发的OOMKilled问题总结(上)》我们知道了如何进行设置和控制对应的堆内存和容器内存的之间的关系,所以防止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/K、m/M、g/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的属性。我们可以看出来他转换为了该属性之后,进行设置和初始化直接内存的配置。针对于直接内存的核心类就在http://www.docjar.com/html/api/sun/misc/VM.java.html。大家有兴趣可以看一下对应的视线。在JVM源码里面的目录是:java.base/jdk/internal/misc/VM.java,我们看一下该类关于直接内存的重点部分。

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/50479.html

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

相关文章

[附源码]计算机毕业设计springboot教学辅助系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

[附源码]Python计算机毕业设计Django的云网盘设计

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

12.Java 使用中间件优雅停机方案设计与实现

内核中的信号量开始聊起。 从内核层我们接着会聊到 JVM 层&#xff0c;在 JVM 层一探优雅停机底层的技术玄机。 随后我们会从 JVM 层一路奔袭到 Spring 然后到 Dubbo。在这个过程中&#xff0c;笔者还会带大家一起 Shooting Dubbo 在优雅停机下的一个 Bug&#xff0c;并为大家…

“数智芯生,云端共创”,利尔达精彩亮相第十八届国际物联网展

“数智芯生&#xff0c;云端共创”&#xff0c;11月15日&#xff0c;为期三天的IOTE 2022 第十八届国际物联网展在深圳国际会展中心&#xff08;宝安&#xff09;盛大开幕。来自全球的感知层、传输层以及应用层的400多家企业共聚一堂&#xff0c;分享发展成果&#xff0c;探讨物…

Android App Icon 替换

今天老板提出有一个需求, 说咱家App icon太丑, 老板说随便什么icon都行 1. 为了随便找一个icon, 我去google了一个图 2. ok, 随便找到一个icon, 下载下来保存文件叫做 jay_web_app.png, 长得还行 3. Android icon 替换开始, 首先找到原来icon的位置, 怎么找呢? 有一个简单直接…

每日一题·对原型和原型链的理解(12/1)

目录 1. 原型链为什么要出现&#xff1f; 2. 原型是什么 3. 原型链是什么 4. 一些代码片段 1. 原型链为什么要出现&#xff1f; 声明2个实例&#xff0c;每个实例都会独立创建sayHi方法&#xff0c;非常占用内存。一样的方法&#xff0c;可以放在原型对象上共用。 function…

[附源码]SSM计算机毕业设计疫情背景下社区公共卫生服务系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

[附源码]Python计算机毕业设计SSM辽宁省高考志愿智能辅助填报系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

中间件版本信息泄露:Microsoft-HTTPAPI/2.0

中间件版本信息泄露&#xff1a;Microsoft-HTTPAPI 修复漏洞现象风险描述修复建议漏洞现象 server版本信息泄露 风险描述 修改请求url会导致所使用服务或中间件版本泄露&#xff0c;从而为攻击者提供进一步攻击的机会 修复建议 如果DisableServerHeader不存在&#xff0c…

【数据结构】二叉树的实现

文章目录一、二叉树的概念二、特殊的二叉树三、二叉树的性质四、二叉树的存储结构五、二叉树链式结构实现&#xff08;1&#xff09;创建结构体&#xff08;2&#xff09;具体函数实现及实现1.0 二叉树的构建1.1 二叉树的销毁1.2 二叉树节点个数1.3 二叉树叶子结点个数1.4 二叉…

【Pandas数据处理100例】(九十):Pandas使用period_range()生成固定间隔日期

前言 大家好,我是阿光。 本专栏整理了《Pandas数据分析处理》,内包含了各种常见的数据处理,以及Pandas内置函数的使用方法,帮助我们快速便捷的处理表格数据。 正在更新中~ ✨ 🚨 我的项目环境: 平台:Windows10语言环境:python3.7编译器:PyCharmPandas版本:1.3.5N…

【自然语言处理概述】99乘法表

【自然语言处理&#xff08;NLP&#xff09;】99乘法表 作者简介&#xff1a;在校大学生一枚&#xff0c;华为云享专家&#xff0c;阿里云专家博主&#xff0c;腾云先锋&#xff08;TDP&#xff09;成员&#xff0c;云曦智划项目总负责人&#xff0c;全国高等学校计算机教学与产…

量子计算(十):量子计算原理

文章目录 量子计算原理 一、酉变换 二、矩阵的指数函数 三、单位矩阵 四、单量子比特逻辑门 五、泡利矩阵 六、常见逻辑门 量子计算原理 经典计算中&#xff0c;最基本的单元是比特&#xff0c;而最基本的控制模式是逻辑门&#xff0c;可以通过逻辑门的组合来达到控制电…

[附源码]Python计算机毕业设计SSM课堂考勤(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【C++】vector的认识+模拟实现

目录1️⃣vector的概念2️⃣STL中vector的使用2.1 vector的定义2.2 iterator的使用2.3 vector的空间问题2.4 vector的增删查改2.5 迭代器失效问题2.5.1 什么是迭代器&#xff1f;2.5.2 迭代器失效3️⃣vector的模拟实现3.1 迭代器3.2 构造函数&#x1f50e;memcpy的拷贝异常问题…

【Java面试】为什么重写equals方法必须同时重写HashCode方法?

众所周知再JDK1.8之后&#xff0c;Java修改了String类型的底层源码&#xff0c;因为他们发现其实对于-128~127范围的字符更加常用&#xff0c;因此将底层的数组从char类型修改为了byte类型。 看到上面的方法可以发现&#xff0c;String类型的equals方法会先比较两个字符串的…

Caffeine 源码、架构、原理(史上最全,10W字 超级长文)

文章很长&#xff0c;而且持续更新&#xff0c;建议收藏起来&#xff0c;慢慢读&#xff01;疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 &#xff1a; 免费赠送 :《尼恩Java面试宝典》 持续更新 史上最全 面试必备 2000页 面试必备 大厂必备 涨薪必备 免费赠送 经典…

软件实训-技术学习心得

一、MoOtA 前端主要负责动态界面的编写&#xff0c;主要学习JavaScript&#xff0c;HTML&#xff0c;CSS三件套 1、前端项目文件夹 .idea&#xff1a;无需关注 node_modules&#xff1a;无需关注 public&#xff1a;存放媒体资源&#xff0c;比如图片&#xff08;images&…

CSS网页页面图像灰色滤镜写法示例

阿酷TONY / 原创 2022-12-1 / 长沙 / grayscale()函数是一个内置函数&#xff0c;用于对图像应用滤镜以设置图像的灰度。 用法: grayscale( amount ) 参数&#xff1a;此函数接受包含灰度值的单个参数量。灰度值根据数量和百分比设置。值0&#xff05;表示原始图像&…

Java并发编程—Thread类中的start()方法如何启动一个线程?

一、java线程的介绍&#xff1a; 在java的开发过程中&#xff0c;很多铁子对于java线程肯定不感到陌生&#xff0c;作为java里面重要的组成部分&#xff0c;这里就从如何创建一个线程给大家进行分析&#xff1b; 二、相关知识引入&#xff1a; ​ 之前我了解过&#xff0c;j…