K8s 中 Pod OOMKilled 原因
目录
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.50MErgonomics Machine Class: serverUsing 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.50MErgonomics Machine Class: serverUsing 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.00MErgonomics Machine Class: serverUsing 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.50MErgonomics Machine Class: serverUsing VM: OpenJDK 64-Bit Server VM
在较低的版本的时候可以使用 -XX:MaxRAMFraction 参数,它告诉 JVM 使用可用内存 /MaxRAMFract 作为最大堆。使用 -XX:MaxRAMFraction=1,我们将几乎所有可用内存用作最大堆。从上面的结果可以看出来内存分配已经可以达到了 910.50M。
问题分析
-
最大堆占用总内存是否仍然会导致你的进程因为内存的其他部分(如“元空间”)而被杀死?
答案: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/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 suppliedif (!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 initializedprivate 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 laterif (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 defaultdirectMemory = Runtime.getRuntime().maxMemory();} else {long l = Long.parseLong(s);if (l > -1)directMemory = l;}// Check if direct buffers should be page aligneds = 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)
相关文章:
K8s 中 Pod OOMKilled 原因
目录 Exit Code 137 解决方案 JVM 感知 cgroup 限制 使用 JDK9 的容器感知机制尝试 问题分析 容器内部感知 CGroup 资源限制 在 Java10 中,改进了容器集成 JVM 参数 MaxDirectMemorySize -XX:MaxDirectMemorySize 的默认值是什么? 其他获取 ma…...
为什么程序员最应该学习的是运营与销售,而不是技术?
大概几个月前,我加入了某副业交流群。这里人才很多,不光是传统意义上的程序员,也有公司老板、偏门大佬、产品经理等。 群里的聊天主题就是搞钱俩字,大家讨论着如何搞钱,分享每日收益情况,以及自己做的产品等…...
MySql数据库常用指令(五)多表连接
MySql数据库常用指令(五)多表连接 一、内连接,或等值连接二、左连接三、右连接 实际应用中,我们常常要连接几个不同的MySQL表,因此在 SELECT, UPDATE 和 DELETE 语句中使用 Mysql 的 JOIN 来联合多表查询 INNER JOIN(内…...
Centos7使用rpm安装mysql 5.7.43
Centos7使用rpm安装mysql 5.7.43 1、下载rpm包 wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.7.43-1.el7.x86_64.rpm-bundle.tar2、解压并安装 tar xf mysql-5.7.43-1.el7.x86_64.rpm-bundle.tar yum -y install mysql-*3、按需修改mysql配置 #注意&a…...
补充:如何提高selenium的运行速度?
已经通读该专栏文章的同学,或许对UI自动化测试有了一定的掌握,细心的同学肯定会发现一个问题,当用例量达到一定程度时,对于整体用例的执行速度肯定不会很满意。除了应用多线程运行用例的方式加快速度,有没有其他的方法呢? 今天告诉大家,方法是有的!也是本人新学的。即…...
使用Python+Redis实现文章投票网站后端功能
1.实现投票功能,2.创建文章数据,3.对文章进行排序。 实现投票功能 实现投票功能,要注重文章的时效性与投票的公平性,所以需要给投票功能加上一些约束条件: 文章发布满一个星期后&…...
SpringBoot 环境使用 Redis + AOP + 自定义注解实现接口幂等性
目录 一、前言二、主流实现方案介绍2.1、前端按钮做加载状态限制(必备)2.2、客户端使用唯一标识符2.3、服务端通过检测请求参数进行幂等校验(本文使用) 三、代码实现3.1、POM3.2、application.yml3.3、Redis配置类3.4、自定义注解…...
Leetcode—18.四数之和【中等】
2023每日刷题(四十一) Leetcode—18.四数之和 实现代码 class Solution { public:vector<vector<int>> fourSum(vector<int>& nums, int target) {vector<vector<int>> ans;sort(nums.begin(), nums.end());int n …...
springsecurity6配置二
一、springsecurity6自定义认证异常处理器 1.1 AuthenticationEntryPointImpl.java package com.school.information.core.security.handler;import com.alibaba.fastjson.JSON; import com.school.information.enums.result.ResultStatusEnum; import com.school.informatio…...
php如何对比浮点数大小(bccomp函数)
第一部分,常规例子: 例1:左边比右边小,结果:-1 //示例,左边比右边小返回值:-1 $price1 2.14; $price2 3.14; $result bccomp($price1, $price2, 2); echo 对比结果:.$result;//…...
服务号和订阅号哪个好
服务号和订阅号有什么区别?服务号转为订阅号有哪些作用?在推送频率上来看,服务号每月能推送四条消息,而订阅号可以每天(24小时)推送一条消息。如果企业开通公众号的目的是提供服务,例如售前资讯…...
面试问题--智能指针
什么是智能指针? 当你在编写程序时,可能需要在运行时动态分配内存来存储数据。在传统的C中,你可能会使用 new 和 delete 操作符来手动管理内存。但是这样容易出现一些问题,比如忘记释放内存导致内存泄漏,或者释放了之…...
向量机SVM原理理解和实战
目录 概念场景导入 点到超平面的距离公式 最大间隔的优化模型 硬间隔、软间隔和非线性 SVM 用 SVM 如何解决多分类问题 1. 一对多法 2. 一对一法 SVM主要原理和特点 原理 优点 缺点 支持向量机模型分类 SVM实战如何进行乳腺癌检测 数据集 字段含义 代码实现 参…...
什么是 Node.js?
在 Node.js 出现之前,最常见的 JavaScript 运行时环境是浏览器,也叫做 JavaScript 的宿主环境。浏览器为 JavaScript 提供了 DOM API,能够让 JavaScript 操作浏览器环境(JS 环境)。 2009 年初 Node.js 出现了…...
系列九、声明式事务(xml方式)
一、概述 声明式事务(declarative transaction management)是Spring提供的对程序事务管理的一种方式,Spring的声明式事务顾名思义就是采用声明的方式来处理事务。这里所说的声明,是指在配置文件中声明,用在Spring配置文件中声明式的处理事务来…...
c盘清理——常用方法和工具整理
背景 最近c盘满了,只剩下1-2G,周末有空清理一下。对这块不太熟悉,下面只是把今天网上看到的比较好用的工具整理一下。 使用工具 磁盘大小查看工具——TreeSize(收费) 之前都是右键一个个看每个文件的大小࿰…...
【React】打包体积分析 source-map-explorer
通过分析打包体积,才能知道项目中的哪部分内容体积过大,方便知道哪些包需要进一步优化。 使用步骤 安装分析打包体积的包:npm i source-map-explorer在 package.json 中的 scripts 标签中,添加分析打包体积的命令对项目打包&…...
Zookeeper(一):在WSL单机搭建Zookeeper伪集群
目录 Zookeeper1 启动单个Zookeeper实例1.1 下载Zookeeper安装包并解压1.2 添加环境变量1.3 修改默认配置1.4 新建数据存储目录和日志目录1.5 启动Zookeeper1.6 停止Zookeeper 2 搭建Zookeeper集群2.1 新建集群目录2.2 配置环境变量2.3 创建节点目录2.4 修改配置2.5 创建节点ID…...
Go语法的特殊之处
上文我们讲了GO模块引入指令Go Mod,本文讲述Go语法的特殊之处 : 单变量 : hello:“hello” Go 语言中新增了一个特殊的运算符:,这个运算符可以使变量在不声明的情况下直接被赋值使用。其使用方法和带值声明变量类似,只是少了var关键字&…...
浏览器v8垃圾回收机制和内存泄漏分析-初级
借鉴:一文搞懂V8引擎的垃圾回收 - 掘金 (juejin.cn) 聊聊V8引擎的垃圾回收 - 掘金 (juejin.cn) 内存泄漏方向: 1、全局变量 未手动清除 2、定时器 未手动清除 3、闭包中使用了匿名函数 未手动清除 4、dom被赋值使用后 未手动清除 其他解决方式 1、…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
【Go语言基础【12】】指针:声明、取地址、解引用
文章目录 零、概述:指针 vs. 引用(类比其他语言)一、指针基础概念二、指针声明与初始化三、指针操作符1. &:取地址(拿到内存地址)2. *:解引用(拿到值) 四、空指针&am…...
代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...
Java后端检查空条件查询
通过抛出运行异常:throw new RuntimeException("请输入查询条件!");BranchWarehouseServiceImpl.java // 查询试剂交易(入库/出库)记录Overridepublic List<BranchWarehouseTransactions> queryForReagent(Branch…...
Yii2项目自动向GitLab上报Bug
Yii2 项目自动上报Bug 原理 yii2在程序报错时, 会执行指定action, 通过重写ErrorAction, 实现Bug自动提交至GitLab的issue 步骤 配置SiteController中的actions方法 public function actions(){return [error > [class > app\helpers\web\ErrorAction,],];}重写Error…...
【多线程初阶】单例模式 指令重排序问题
文章目录 1.单例模式1)饿汉模式2)懒汉模式①.单线程版本②.多线程版本 2.分析单例模式里的线程安全问题1)饿汉模式2)懒汉模式懒汉模式是如何出现线程安全问题的 3.解决问题进一步优化加锁导致的执行效率优化预防内存可见性问题 4.解决指令重排序问题 1.单例模式 单例模式确保某…...
