关于内存:JVM-内存大对象监控和优化实践

作者:vivo 互联网服务器团队 - Liu Zhen、Ye Wenhao服务器内存问题是影响应用程序性能和稳定性的重要因素之一,须要及时排查和优化。本文介绍了某外围服务内存问题排查与解决过程。首先在JVM与大对象优化上进行了无效的实际,其次在故障转移与大对象监控上提出了牢靠的落地计划。最初,总结了内存优化须要思考的其余问题。 一、问题形容音乐业务中,core服务次要提供歌曲、歌手等元数据与用户资产查问。随着元数据与用户资产查问量的增长,一些JVM内存问题也逐步露出,例如GC频繁、耗时长,在高峰期RPC调用超时等问题,导致业务外围性能受损。 图1 业务异样数量变动 二、剖析与解决通过对日志,机器CPU、内存等监控数据分析发现: YGC均匀每分钟次数12次,峰值为24次,均匀每次的耗时在327毫秒。FGC均匀每10分钟0.08次,峰值1次,均匀耗时30秒。能够看到GC问题较为突出。 在问题期间,机器的CPU并没有显著的变动,然而堆内存呈现较大异样。图2,黄色圆圈处,内存应用急速回升,FGC变的频繁,开释的内存越来越少。 图2 老年代内存应用异样 因而,咱们认为业务性能异样是机器的内存问题导致的,须要对服务的内存做一次专项优化。 步骤1  JVM优化以下是默认的JVM参数: -Xms4096M -Xmx4096M -Xmn1024M -XX:MetaspaceSize=256M -Djava.security.egd=file:/dev/./urandom -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/{runuser}/logs/other如果不指定垃圾收集器,那么JDK 8默认采纳的是Parallel Scavenge(新生代) +Parallel Old(老年代),这种组合在多核CPU上充分利用多线程并行的劣势,进步垃圾回收的效率和吞吐量。然而,因为采纳多线程并行形式,会造成肯定的进展工夫,不适宜对响应工夫要求较高的应用程序。然而,core这类的服务特点是对象数量多,生命周期短。在零碎特点上,吞吐量较低,要求时延低。因而,默认的JVM参数并不适宜core服务。 依据业务的特点和屡次对照试验,抉择了如下参数进行JVM优化(4核8G的机器)。该参数将young区设为原来的1.5倍,缩小了进入老年代的对象数量。将垃圾回收器换成ParNew+CMS,能够缩小YGC的次数,升高进展工夫。此外还开启了CMSScavengeBeforeRemark,在CMS的从新标记阶段进行一次YGC,以缩小从新标记的工夫。 -Xms4096M -Xmx4096M -Xmn1536M -XX:MetaspaceSize=256M -XX:+UseConcMarkSweepGC -XX:+CMSScavengeBeforeRemark -Djava.security.egd=file:/dev/./urandom -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/{runuser}/logs/other 图3 JVM优化前后的堆内存比照 优化后成果如图3,堆内存的应用明显降低,然而Dubbo超时依然存在。 咱们推断,在业务高峰期,该节点呈现了大对象降职到了老年代,导致内存应用迅速回升,并且大对象没有被及时回收。那如何找到这个大对象及其产生的起因呢?为了升高问题排查期间业务的损失,提出了长期的故障转移策略,尽量升高异样数量。 步骤2  故障转移策略在api服务调用core服务出现异常时,将出现异常的机器ip上报给监控平台。而后利用监控平台的统计与告警能力,配置相应的告警规定与回调函数。当异样触发告警,通过配置的回调函数将告警ip传递给api服务,此时api服务能够将core服务下的该ip对应的机器视为“故障”,进而通过自定义的故障转移策略(实现Dubbo的AbstractLoadBalance抽象类,并且配置在我的项目),主动将该ip从提供者集群中剔除,从而达到不去调用问题机器。图 4 是整个措施的流程。在该措施上线前,每当有机器内存告警时,将会人工重启该机器。 图4 故障转移策略 步骤3  大对象优化大对象占用了较多的内存,导致内存空间无奈被无效利用,甚至造成OOM(Out Of Memory)异样。在优化过程中,先是查看了异样期间的线程信息,而后对堆内存进行了剖析,最终确定了大对象身份以及产生的接口。 (1) Dump Stack 查看线程 从监控平台上Dump Stack文件,发现肯定数量的如下线程调用。 Thread 5612: (state = IN_JAVA) - org.apache.dubbo.remoting.exchange.codec.ExchangeCodec.encodeResponse(org.apache.dubbo.remoting.Channel, org.apache.dubbo.remoting.buffer.ChannelBuffer, org.apache.dubbo.remoting.exchange.Response) @bci=11, line=282 (Compiled frame; information may be imprecise) - org.apache.dubbo.remoting.exchange.codec.ExchangeCodec.encode(org.apache.dubbo.remoting.Channel, org.apache.dubbo.remoting.buffer.ChannelBuffer, java.lang.Object) @bci=34, line=73 (Compiled frame) - org.apache.dubbo.rpc.protocol.dubbo.DubboCountCodec.encode(org.apache.dubbo.remoting.Channel, org.apache.dubbo.remoting.buffer.ChannelBuffer, java.lang.Object) @bci=7, line=40 (Compiled frame) - org.apache.dubbo.remoting.transport.netty4.NettyCodecAdapter$InternalEncoder.encode(io.netty.channel.ChannelHandlerContext, java.lang.Object, io.netty.buffer.ByteBuf) @bci=51, line=69 (Compiled frame) - io.netty.handler.codec.MessageToByteEncoder.write(io.netty.channel.ChannelHandlerContext, java.lang.Object, io.netty.channel.ChannelPromise) @bci=33, line=107 (Compiled frame) - io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(java.lang.Object, io.netty.channel.ChannelPromise) @bci=10, line=717 (Compiled frame) - io.netty.channel.AbstractChannelHandlerContext.invokeWrite(java.lang.Object, io.netty.channel.ChannelPromise) @bci=10, line=709 (Compiled frame)...state = IN_JAVA 示意Java虚拟机正在执行Java程序。从线程调用信息能够看到,Dubbo正在调用Netty,将输入写入到缓冲区。此时的响应可能是一个大对象,因此在对响应进行编码、写缓冲区时,须要消耗较长的工夫,导致抓取到的此类线程较多。另外耗时长,也即是大对象存活工夫长,导致full gc 开释的内存越来越小,闲暇的堆内存变小,这又会加剧full gc 次数。 ...

August 28, 2023 · 2 min · jiezi

关于内存:深入了解-JavaScript-内存泄漏

作者:京东批发 谢天在任何语言开发的过程中,对于内存的治理都十分重要,JavaScript 也不例外。 然而在前端浏览器中,用户个别不会在一个页面停留很久,即便有一点内存透露,从新加载页面内存也会跟着开释。而且浏览器也有本人的主动回收内存的机制,所以前端并没有特地关注内存透露的问题。 然而如果咱们对内存透露没有什么概念,有时候还是有可能因为内存透露,导致页面卡顿。理解内存透露,如何防止内存透露,都是不可短少的。 什么是内存在硬件级别上,计算机内存由大量触发器组成。每个触发器蕴含几个晶体管,可能存储一个位。单个触发器能够通过惟一标识符寻址,因而咱们能够读取和笼罩它们。因而,从概念上讲,咱们能够把咱们的整个计算机内存看作是一个微小的位数组,咱们能够读和写。这是内存的底层概念,JavaScript 作为一个高级语言,不须要通过二进制进行内存的读写,而是相干的 JavaScript 引擎做了这部分的工作。 内存的生命周期内存也会有生命周期,不论什么程序语言,个别能够依照程序分为三个周期: 调配期:调配所须要的内存使用期:应用调配的内存进行读写开释期:不须要时将其开释和偿还内存调配 -> 内存应用 -\> 内存开释 什么是内存透露在计算机科学中,内存透露指因为忽略或谬误造成程序未能开释曾经不再应用的内存。内存透露并非指内存在物理上的隐没,而是应用程序调配某段内存后,因为设计谬误,导致在开释该段内存之前就失去了对该段内存的管制,从而造成了内存的节约。如果内存不须要时,没有通过生命周期的的开释期,那么就存在内存透露。 内存透露的简略了解:无用的内存还在占用,得不到开释和偿还。比较严重时,无用的内存会继续递增,从而导致整个零碎的卡顿,甚至解体。 JavaScript 内存管理机制像 C 语言这样的底层语言个别都有底层的内存治理接口,然而 JavaScript 是在创立变量时主动进行了内存调配,并且在不应用时主动开释,开释的过程称为“垃圾回收”。然而就是因为主动回收的机制,让咱们谬误的感觉开发者不用关怀内存的治理。 JavaScript 内存管理机制和内存的生命周期是统一的,首先须要分配内存,而后应用内存,最初开释内存。绝大多数状况下不须要手动开释内存,只须要关注对内存的应用(变量、函数、对象等)。 内存调配JavaScript 定义变量就会主动分配内存,咱们只须要理解 JavaScript 的内存是主动调配的就能够了。 let num = 1;const str = "名字";const obj = { a: 1, b: 2}const arr = [1, 2, 3];function func (arg) { ... }内存应用应用值的过程实际上是对调配的内存进行读写的操作,读取和写入的操作可能是写入一个变量或者一个对象的属性值,甚至传递函数的参数。 // 持续上局部// 写入内存num = 2;// 读取内存,写入内存func(num);内存回收垃圾回收被称为GC(Garbage Collection) 内存透露个别都是产生在这一步,JavaScript 的内存回收机制尽管能够回收绝大部分的垃圾内存,然而还是存在回收不了的状况,如果存在这些状况,须要咱们本人手动清理内存。 以前一些老版本的浏览器的 JavaScript 回收机制没有那么欠缺,经常出现一些 bug 的内存透露,不过当初的浏览器个别都没有这个问题了。 这里理解下当初 JavaScript 的垃圾内存的两种回收形式,相熟一下这两种算法能够帮忙咱们了解一些内存透露的场景。 ...

March 21, 2023 · 3 min · jiezi

关于内存:浅析大促备战过程中出现的fullGc我们能做什么

作者:京东科技 白洋 前言:背景:为应答618、双11大促,生产金融侧会依据批发侧大促节奏进行整体零碎备战。对外围流量入口承载的零碎进行加固优化,排除零碎危险,保障大促期间零碎稳固。因为大促期间,生产金融业务承当着直面用户,高并发,零碎危险大概率间接造成资损危险等问题。 在日常压测和大促期间,常常会产生Jvm呈现大量young Gc 和 局部full GC的状况,导致性能降落,可用率升高等状况。之前对Jvm的垃圾回收机制不是很熟,如何防止和如何调优,基本上无所不通,本文也是对本人学到的常识的一个坚固~一、什么是JVM的GC?JVM(Java Virtual Machine)。JVM 是 Java 程序的虚拟机,是一种实现 Java 语言的解释器。 它提供了一种独立于操作系统的运行环境,使得 Java 程序在任何反对 JVM 的计算机上都能够运行。JVM 负责加载、验证、解释、执行和垃圾回收 Java 字节代码,并为 Java 程序提供内存治理、线程治理和安全控制等服务。 JVM 中的 GC(Garbage Collection)是垃圾回收的缩写,是 JVM 的内存管理机制。 Young GC 和 Full GC 是两种不同的 GC 算法。 Young GC:针对新生代对象的回收算法,通常应用的是复制算法或者标记整顿算法。因为新生代中的对象生命周期短,所以 Young GC 速度要比 Full GC 快得多。 Full GC:针对整个堆内存的回收算法,用于回收那些在 Young GC 中没有回收的存活对象。Full GC 速度比较慢,因为它须要扫描整个堆内存,因而对系统的性能影响较大。 所以在设计 Java 利用时,须要尽量减少 Full GC 的次数,以保证系统的性能。常见的办法包含扩充新生代的内存空间,缩小数组长度等。 以上根本是通用的对 Jvm 和 Gc 的解释。然而能够显著看出短少一些细节,对咱们来说还是没什么用,测试同学该如何了解具体的场景呢?? 咱们首先来了解young GC 的诞生过程: 首先, 了解复制算法和标记整顿算法,它们是两种不同的 Young GC 回收算法。 ...

March 1, 2023 · 3 min · jiezi

关于内存:堆内存持续占用高-且-ygc回收效果不佳-排查处理实践

作者:京东批发 王江波 阐明:局部素材来源于网络,数据分析全为实在数据。一、 问题背景自建的两套工具,运行一段时间后均呈现 内存占用高触发报警,频繁young gc且成果不佳。已经尝试屡次解决,因各种起因耽误,最近下定决心解决此问题。 二、 问题形容Q:堆内存1018M,应用达到950M左右触发一次young gc,ygc之后内存占用630M,未产生full gc 三、 容器配置已解决要害信息 •主机名 xxx •实例ID xxx •ip •操作系统名称Linux •操作系统体系结构amd64 •CPU个数2 •JRE版本1.8.0_191 •JVM启动工夫2023-02-18 17:14:10.873 •启动门路/export/App •Full GCPS MarkSweep •Young GCPS Scavenge •过程ID115135 •物理内存大小251.4GB(269888389120Byte) •替换区大小0.0GB(0Byte) •虚拟内存大小12.5GB(13368197120Byte) •利用门路/export/App/lib/tp-center-web.jar!/BOOT-INF/lib/ •JVM启动参数-javaagent:/export/xxx/lib/pfinder-profiler-agent-1.0.8-20210322032622-6f12bda2.jar -Ddeploy.app.name=xxx -Ddeploy.app.id=xxx -Ddeploy.instance.id=0 -Ddeploy.instance.name=server1 -DJDOS_DATACENTER=HT -Dloader.path=./conf -Dspring.profiles.active=pre -Xms1024m -Xmx1024m -Xmn384m -XX:MetaspaceSize=64m -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSInitiatingOccupancyOnly -XX:ParallelGCThreads=2 -XX:CICompilerCount=2 -XX:MaxDirectMemorySize=128m -Duser.timezone=Asia/Shanghai -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=xxx -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=xxx 四、 问题剖析1、 young gc的机会?R:ygc在新生代的eden区满了之后就会触发,采纳复制算法回收新生代的垃圾。 2、 为何young gc后堆内存使用率依然很高?R:新生代过小,大对象间接进入老年代,编码时要尽量减少大对象的应用。 3、 full gc 的机会?R:HotSpot VM里,除了CMS之外,其它能收集老年代的GC都会同时收集整个GC堆,包含新生代 ...

March 1, 2023 · 4 min · jiezi

关于内存:慧销平台ThreadPoolExecutor内存泄漏分析

作者:京东批发 冯晓涛 问题背景京东生旅平台慧销零碎,作为平台零碎对接了多条业务线,次要进行各个业务线广告,召回等流动相干内容与能力治理。 最近依据告警发现内存继续升高,每隔2-3天会收到内存超过阈值告警,猜想可能存在内存透露的状况,而后进行排查。依据24小时时间段内存监控能够发现,容器的内存在持续上升: 问题排查初步预计内存透露,查看24小时时间段jvm内存监控,排查jvm内存回收状况: YoungGC和FullGC状况: 通过jvm内存剖析和YoungGC与FullGC执行状况,能够判断可能起因如下: 1、 存在YoungGC然而没有呈现FullGC,可能是对象进入老年代然而没有达到FullGC阈值,所以没有触发FullGC,对象始终存在老年代无奈回收 2、 存在内存透露,尽管执行了YoungGC,然而这部分内存无奈被回收 通过线程数监控,察看以后线程状况,发现以后线程数7427个,并且还在一直回升,根本判断存在内存透露,并且和线程池的不当应用无关: 通过JStack,获取线程堆栈文件并进行剖析,排查为什么会有这么多线程: 发现通过线程池创立的线程数达7000+: 代码剖析剖析代码中ThreadPoolExecutor的应用场景,发现在一个worker公共类中定义了一个线程池,worker执行时会应用线程池进行异步执行。 public class BackgroundWorker { private static ThreadPoolExecutor threadPoolExecutor; static { init(15); } public static void init() { init(15); } public static void init(int poolSize) { threadPoolExecutor = new ThreadPoolExecutor(3, poolSize, 1000, TimeUnit.MINUTES, new LinkedBlockingDeque<>(1000), new ThreadPoolExecutor.CallerRunsPolicy()); } public static void shutdown() { if (threadPoolExecutor != null && !threadPoolExecutor.isShutdown()) { threadPoolExecutor.shutdownNow(); } } public static void submit(final Runnable task) { if (task == null) { return; } threadPoolExecutor.execute(() -> { try { task.run(); } catch (Exception e) { e.printStackTrace(); } }); } }广告缓存刷新worker应用线程池的代码: ...

February 28, 2023 · 3 min · jiezi

关于内存:10分钟读懂虚拟内存-IO-零拷贝

虚拟内存 (一)虚拟内存引入 咱们晓得计算机由CPU、存储器、输出/输出设备三大外围局部组成,如下 CPU运行速度很快,在齐全现实的状态下,存储器应该要同时具备以下三种个性: 速度足够快:这样 CPU 的效率才不会受限于存储器;容量足够大:容量可能存储计算机所需的全副数据;价格足够便宜:价格低廉,所有类型的计算机都能装备;然而,出于老本思考,以后计算机体系中,存储都是采纳分层设计的,常见档次如下: 上图别离为寄存器、高速缓存、主存和磁盘,它们的速度逐级递加、老本逐级递加,在计算机中的容量逐级递增。通常咱们所说的物理内存即上文中的主存,常作为操作系统或其余正在运行中的程序的长期材料存储介质。在嵌入式以及一些老的操作系统中,零碎间接通过物理寻址形式和主存打交道。然而,随着科技倒退,遇到如下困境: 一台机器可能同时运行多台大型应用程序;每个应用程序都须要在主存存储大量长期数据;晚期,单个CPU寻址能力2^32,导致内存最大4G。主存成了计算机系统的瓶颈。此时,科学家提出了一个概念:虚拟内存。 以32位操作系统为例,虚拟内存的引入,使得操作系统能够为每个过程调配大小为 4GB的虚拟内存空间,而实际上物理内存在须要时才会被加载,无效解决了物理内存无限空间带来的瓶颈。在虚拟内存到物理内存转换的过程中,有很重要的一步就是进行地址翻译,上面介绍。 (二)地址翻译 过程在运行期间产生的内存地址都是虚拟地址,如果计算机没有引入虚拟内存这种存储器形象技术的话,则 CPU 会把这些地址间接发送到内存地址总线上,而后拜访和虚拟地址雷同值的物理地址;如果应用虚拟内存技术的话,CPU 则是把这些虚拟地址通过地址总线送到内存治理单元(Memory Management Unit,简称 MMU),MMU 将虚拟地址翻译成物理地址之后再通过内存总线去拜访物理内存: 虚拟地址(比方 16 位地址 8196=0010 000000000100)分为两局部:虚构页号(Virtual Page Number,简称 VPN,这里是高 4 位局部)和偏移量(Virtual Page Offset,简称 VPO,这里是低 12 位局部),虚拟地址转换成物理地址是通过页表(page table)来实现的。页表由多个页表项(Page Table Entry, 简称 PTE)组成,个别页表项中都会存储物理页框号、批改位、拜访位、爱护位和 "在/不在" 位(无效位)等信息。这里咱们基于一个例子来剖析当页面命中时,计算机各个硬件是如何交互的: 第 1 步:处理器生成一个虚拟地址 VA,通过总线发送到 MMU;第 2 步:MMU 通过虚构页号失去页表项的地址 PTEA,通过内存总线从 CPU 高速缓存/主存读取这个页表项 PTE;第 3 步:CPU 高速缓存或者主存通过内存总线向 MMU 返回页表项 PTE;第 4 步:MMU 先把页表项中的物理页框号 PPN 复制到寄存器的高三位中,接着把 12 位的偏移量 VPO 复制到寄存器的末 12 位形成 15 位的物理地址,即能够把该寄存器存储的物理内存地址 PA 发送到内存总线,拜访高速缓存/主存;第 5 步:CPU 高速缓存/主存返回该物理地址对应的数据给处理器。在 MMU 进行地址转换时,如果页表项的无效位是 0,则示意该页面并没有映射到实在的物理页框号 PPN,则会引发一个缺页中断,CPU 陷入操作系统内核,接着操作系统就会通过页面置换算法抉择一个页面将其换出 (swap),以便为行将调入的新页面腾出地位,如果要换出的页面的页表项里的批改位曾经被设置过,也就是被更新过,则这是一个脏页 (Dirty Page),须要写回磁盘更新该页面在磁盘上的正本,如果该页面是"洁净"的,也就是没有被批改过,则间接用调入的新页面笼罩掉被换出的旧页面即可。缺页中断的具体流程如下: ...

November 17, 2022 · 3 min · jiezi

关于内存:Datenlord-内存顺序问题二

上一篇文章介绍了内存模型,并介绍了两种内存程序, memory_order_acquire(Acquire)和memory_order_release(Release)。 集体认为,这两种内存程序是C++定义的六种内存程序中最重要的两种, 只有了解了Acquire和Release的语义,能力更好了解其余四种内存程序的语义。 更进一步,在理论应用场景中,Acquire和Release是最常见的两种内存程序。 如何判断该应用哪种内存程序?这是开发者在应用原子类型和无锁化编程时最常碰到的问题。 本篇Blog用理论的例子来阐明,如何判断该应用哪种内存程序。 此外,为了更深刻了解基于原子操作和基于锁实现的同步关系的本质区别, 本篇Blog还会介绍Happen-Before关系和Synchronize-With关系。 Happen-Before关系线程间的同步关系,是要约定不同线程里产生的事件的先后秩序,互斥关系实质也是一种同步关系。 Happen-Before关系就是用于定义不同事件之间的先后秩序。 Happen-Before关系能够是在代码里动态约定好(基于锁的形式),也能够是在程序运行时动静发现(基于原子操作和内存程序的形式)。 先来看一个简略的例子,这个例子解释了Happen-Before关系: int data = 0;int flag = 0;// thread 1void thread_func1() { data = 42; flag = 1; // 事件1}// thread 2void thread_func2() { if (flag == 1) // 事件2 printf("%d", data);}下面的例子里定义了两个全局变量,线程1设置flag = 1示意实现对data的赋值, 线程2读取flag的值用以判断线程1是否实现对data的赋值,如果flag == 1则输入data的值。 咱们定义两个事件,事件1为thread_func1里对flag赋值示意对data的赋值实现, 事件2为thread_func2里判断flag == 1,如果flag == 1则输入data的值。 因为没有用锁的形式在代码里动态规约事件1和事件2的先后顺序,程序运行时能够有多种后果, 有些后果是正当的,有些后果是不合理的。 其中两种正当的后果是:要么线程2输入data的值42,要么不输入。 也就是说要么事件1 Happen-Before事件2,要么事件2 Happen-Before事件1。 然而,还有些不合理的后果,比方线程2有可能输入data的值为0,为什么呢? 因为编译器或CPU会对程序进行优化,使得指令的执行程序跟代码的逻辑程序不统一。比方编译器可能对thread_func2进行如下优化: // thread 2void thread_func2() { int tmp = data; if (flag == 1) printf("%d", tmp);}这里tmp代表某个寄存器,编译器优化thread_func2导致在判断flag == 1前把data的值先载入寄存器,此时data的值可能为0, 判断完flag == 1之后再输入寄存器的值,此时即使data曾经被thread_func1赋值为1,然而寄存器tmp里的值依然是0。 也就是说,程序运行时产生不合理的后果,是因为没有保障事件1和事件2之间的先后秩序,导致两个事件在运行时有重叠。 因而,为了保障下面的例子运行产生正当的后果,咱们须要确保要么事件1 Happen-Before事件2,要么事件2 Happen-Before事件1。 能够采纳基于锁的信号量机制,在代码里动态约定事件1在事件2之前产生, 也能够采纳原子操作和内存程序在程序运行时动静发现事件1和事件2之间的关系。 ...

July 19, 2022 · 2 min · jiezi

关于内存:Datenlord-内存顺序问题一

内存程序,艰深地讲,是对于代码编译成机器指令后的执行程序问题。内存程序和编译器、硬件架构密切相关。那为什么会产生内存程序问题呢?有两方面起因: 一方面,编译器为了优化程序性能,不会齐全依照开发者写的代码的程序来生成机器指令; 另一方面,在程序运行时,为了进步性能,CPU也不齐全依照程序的指令程序执行,比方体系结构里经典的Tomasulo算法。 对于大部分开发者而言,在写单线程程序,或者基于锁(Mutex)和信号量(Semaphore)之类编程框架提供的同步元语写多线程程序的时候,并不需要关怀内存程序的问题。 这是因为编译器和硬件架构保障了,尽管指令执行程序可能跟开发者写的代码语句的程序不统一,然而执行后的后果是一样的,即语义统一。 换句话讲,编译器和硬件架构提供了一层形象用以屏蔽内存程序问题,保障代码和编译进去的程序执行语义统一。 这样一方面进步程序性能,另一方面让开发者不必关怀底层细节。编译器和硬件架构提供的这一层形象叫作内存模型(Memory Model)。 这种为了便于了解和应用而提出一层形象以屏蔽底层简单细节的做法,在各个学科中亘古未有。 类比经典力学和相对论,在远低于光速的静止中,实用经典力学,在靠近或达到光速的静止中,实用相对论而不实用经典力学; 经典力学和相对论之间,有一层形象,速度远低于光速,形象成立,速度靠近或达到光速,形象被突破。 相似的,编译器和硬件架构提供了内存模型这一层形象用以屏蔽内存程序问题。 对于大部分开发者而言,写单线程程序,或基于编程框架提供的同步元语写多线程程序的时候,内存模型形象成立,无需思考内存程序问题; 当开发者写多线程程序,对于多线程并发拜访(或读或写)共享数据,应用原子操作,而不是基于锁互斥拜访数据,即无锁化编程的时候, 这时内存模型的形象被突破,开发者必须思考内存程序问题。 内存程序问题波及编译器和硬件架构的很多细节,我尝试用对于大部分开发者来说浅显易懂的语言来形容内存程序问题, 尽可能防止编译器和硬件架构的实现细节,以便于大家了解。 上面顺次介绍内存模型、内存程序、原子操作,最初以C++11为例解说开发者如何规约内存程序。 内存模型内存模型是编程语言对程序运行时内存拜访模式的形象,即内存被多个程序(过程和线程)共享,程序对内存的拜访是无奈预知的。 艰深地讲,内存模型指的是CPU并发随机拜访内存,或从内存加载数据(Load)或把数据写入到内存(Store)。 Load和Store是机器指令(或汇编语言)的术语,其实就是读(Read)操作和写(Write)操作。 这里,内存模型屏蔽了很多硬件的细节,比方CPU的寄存器、缓存等等(因为寄存器和缓存属于程序执行上下文,CPU拜访寄存器和缓存不存在并发)。 内存模型比拟好了解,每个开发者或多或少都接触到内存模型。 有了内存模型这一层形象,那么内存程序问题能够等价于读操作和写操作的执行程序问题,因为内存模型里CPU对内存的拜访只有读和写两种操作。 开发者在写代码时,代码语句的先后顺序往往约定了对内存拜访的先后顺序的,即便拜访的不是同一个内存地址。然而这个约定是基于内存模型这层形象成立的前提。 后面提到,内存模型在单线程编程和基于编程框架提供的同步元语实现多线程编程的状况下,对内存程序问题进行屏蔽,怎么了解呢? 上面通过例子阐明单线程程序的内存程序问题: int x, y = 0;x = y + 1;y = 2;这段代码定义了两个整数,x和y,并对y初始化赋值为0,而后给x赋值的时候用到y的值,之后再给y赋值。 看上去,对y的写操作必须在对x的写操作之后,然而改写上述代码片段如下: int x, y = 0;int tmp;tmp = y;y = 2;x = tmp + 1;减少了变量tmp之后,首先把y的值付给tmp,而后就能够先对y赋新值,再给x赋值。对x和y来说,下面两段程序的执行后果是等价的。 变量tmp在这里能够了解为是CPU的寄存器,有了寄存器的帮忙,代码里的读操作和写操作先后顺序可能被扭转。 艰深地讲,编译器对代码语句的程序调整也是相似的原理 (仅供对编译器不熟的读者了解编译器如何对代码语句程序的调整,理论编译器对代码的优化很简单,细节暂不开展)。 上述例子阐明了,单线程状况下,内存模型的形象成立,开发者无需思考内存程序问题。 再思考多线程的状况,把对x的写操作和对y的写操作放在不同的线程里: int x, y = 0;void thread_func1() { x = y + 1;}void thread_func2() { y = 2;}能够看出,x会有多种后果,取决于程序运行时两个线程的执行程序,这就跟之前单线程的执行后果不统一了。 因为这里没有采纳编程框架提供的同步元语来实现线程间同步,内存模型的形象被突破,编译器和硬件架构无奈保障语义统一。 此时,开发者要么采纳编程框架提供的同步元语实现线程间同步以满足内存模型的形象,要么显式规约指令执行程序以保障后果正确。 改写下面的例子,能够采纳编程框架提供的同步元语,规约程序运行时线程的执行程序,这里应用信号量来实现线程间同步: ...

July 18, 2022 · 2 min · jiezi

关于内存:Mac系统占用空间大空间不够查看系统文件大小分布

背景: 最近老提醒空间不够,很难堪,始终弹零碎提醒 如图,256的空间,就剩下几个G了,其中最大头的零碎占用:160G,占比60%多 失常状况下:咱们能够点击治理,进入到零碎磁盘优化界面: 这种实用于简略的文件占用剖析,个别咱们可能通过 清理文稿 和 回收箱 来解决空间不够的问题。 1、清空回收站。 2、在文稿里,按文件大小排序,删除不须要的文件。 3、对于GarageBand,这个是零碎上的模仿乐器,个别都应用不到。 革除办法: rm -rf /Library/Application\ Support/GarageBandrm -rf /Library/Application\ Support/Logicrm -rf /Library/Audio/Apple\ Loops 不过,对于罪魁祸首,零碎的160G,咱们怎么能力晓得她的外部存储散布呢? 注释: 对于如何查看零碎的文件占用详情。 一、首先关上终端,输出 du -sh *这个命令用来查看根目录下,所有文件的大小散布,如图所示: 其中,咱们很容易能看到每个文件的大小占比,疾速定位到最大占比的文件:Library 二、输出命令,进入到Library文件门路 cd ~/Library 而后,查看Library下的所有文件大小散布。 输出: du -d 1 -h 很容易咱们能够找到最大的文件:/Developer 当然,其余的文件大小,咱们也都能看到,高深莫测。 三、到这里,咱们根本就能晓得上面的套路了,咱们能够持续往下查看 比方,我这里持续进入到Developer文件,再查看他的每个子文件大小: 根本,查看个两三层,就根本能晓得大略的起因了,我这边因为是程序猿,所以Xcode是根本原因,占了零碎160G的一半大小。 到这里为止,如果你也是程序猿,有趣味的,能够持续看;如果没有趣味的能够间接跳第四步。 根本这就到底了,大略的空间占用散布从下面几张图,也根本无数了。 以上是讲如何查看及剖析文件,如果你是iOS程序猿,这边附上几个清理步骤(清理Xcode缓存) 通过测试,我临时发现这几个文件能够适当清理下: 1 ./Archives 这个文件存储的是你所有的历史打包文件,你能够将一些历史的包删掉,最近的如果不释怀能够先留着,不过全副删除也是没问题的,然而,如果你删除了,咱们Xcode打包上传界面就看不到货色了(如下图所示,该界面的货色就没了) 2 ./DerivedData 这个文件大家应该比拟相熟了,存储的是所有我的项目的一些缓存数据、包含编译缓存文件等等,这个文件是能够全副清理的,当然,你也能够保留一些最近的我的项目,先临时清理历史我的项目的缓存。 3 ./iOS DeviceSupport 这个文件外面存储的是xcode对手机各个版本的反对文件,这里如果你的我的项目不再须要反对iOS6、iOS7等,能够先把外面的基于iOS6、iOS7的反对文件革除。 4 附上 完整版Xcode瘦身办法大全 ,有趣味的可对照此文操作。 四、通过第三步的层层剖析,咱们根本能晓得了每个文件的大小散布,也能找到一些不须要用的垃圾文件,其中大多以缓存文件居多,大家能够适当进行清理~ 最初,如果有对命令不感冒的人,也能够通过Finder来进行可视化的查看。 关上Spotlight(Ctrl+空格),输出 ~/Library 能进入到资源库文件门路: 最左边的文件就对应方才命令进去的文件,咱们能够依据命令查找到的占用最大的文件,这里点进去查看,而后再依据本人的需要进行适当清理。 ...

July 2, 2022 · 1 min · jiezi

关于内存:内存不超过5Mdatop-在识别冷热内存及跨-numa-访存有多硬核-龙蜥技术

datop 是一款轻量级实时性内存冷热扫描工具,可能扫描内存冷热以及跨 muma 访存的状况,其运行时开销和存储空间十分小(单 CPU 使用率不到 4% 和 5MB),此外不受硬件平台的限度,在物理机和虚拟机上均能反对。目前曾经在龙蜥社区开源了,内核态的局部代码是基于 OpenAnolis 5.10。 开源地址: https://gitee.com/anolis/data-profile-tools.git 以下是 Cloud Kernel SIG 核心成员分享《datop 轻量级靶向内存热点扫描工具介绍与入门》局部内容(视频回顾和技术PPT获取形式见文末)。 DRAM 内存低容量对系统造成性能影响日益突出,云计算场景中为缓解日益增长的内存需要,业界提出异构内存的应用,采纳两种或更多类型的内存:小型高性能内存以及大容量的低性能内存。此外,古代高端存储设备如固态驱动器和相位扭转内存,为进步内存性能提供了可能, 这些代替计划给人一种谬误的幻觉,认为轻易能通过筛选数据的热度来抉择寄存的设施,的确这些信息在现有的内存治理中,能够通过全盘扫描内存冷热的形式失去, 然而长时间扫描过程中很容易产生极大的开销,造成零碎性能损耗。 以后现有内存冷热扫描机制缺点: 热点跟踪开销成比例增长:监控的内存越多,开销越大跟踪品质升高:监控内存范畴越大,监控品质降落越显著短少 numa 访存统计:跨 numa 访存性能影响不可疏忽针对上述问题,咱们开发了跟踪实时内存热点数据的工具 datop(Data Access Top), 采纳划分内存区域采样的形式,并自适应区域构建技术来获取极低的开销损耗,在此基础上,还减少了 numa 仿真性能,用于收集工作跨 numa 访存状况,为了评估其准确性和低开销能力,咱们选取和测试了多个 benchmark,并与基线进行比拟,结果表明:datop 工具在辨认冷热内存以及跨 numa 访存方面具备优良的体现能力。 具体分享提纲有以下 6 局部: 1、datop 的概述 2、背景:云计算大规模简单场景下内存面临的挑战 3、datop 开发设计和指标 4、热点扫描原理及策略 5、datop 工具测试状况阐明 6、datop 演示与试用及后续布局和思考 规范 datop 演示与应用,利用 datop 工具去监控测试用例 numactl -m 0 -C 30 memhog -r1000000000 100m 热点内存散布以及跨 numa 状况。 ...

May 5, 2022 · 1 min · jiezi

关于内存:硬盘可以分为几类监控专用硬盘和普通硬盘有什么区别

硬盘分为绿盘黑盘蓝屏紫盘红盘。不同色彩的硬盘性能侧重点不一样,能够利用在不同的畛域。这种色彩分级办法是西部数据公司特有的硬盘分级办法。 如何正确抉择一款性价比高的硬盘就会省去很大的前期保护老本,那么专用监控硬盘和一般硬盘区别体现在哪些方面呢? 硬盘按色彩分大抵有:彩色、蓝色、绿色、红色、紫色 、金盘6种 黑盘:彩色硬盘具备杰出的性能和高转速,多实用于电脑服务器以及企业级硬盘,当然价格也是最高的,适宜对性能及稳定性要求极高的用户,然而,黑盘因为转速较高,在应用时应防止激烈触动避免磁头撞击碟片而损坏 蓝盘:是介于绿盘和黑盘之间,通常用的就是蓝盘,性能及容量在几种硬盘中属于中规中矩的,但其性价比十分高,因而广泛应用于生产级PC市场,其毛病就是声音较大(注:当初的绿盘和蓝盘基本上就没区别了) 绿盘:盘属于大容量存储磁盘,功耗较低,侧重于节能,性能绝对个别,但实用于长期保留文件。(注:当初的绿盘和蓝盘基本上就没区别了) 红盘:红盘具备更大的容量、低转速和乐音较小的特点,适宜有搭建网络存储的集体及小型办公用户。 紫盘:紫盘是为监控硬盘录像设施专门设计的,适宜365天×24小时不间断地运行,能够极低的功耗全天候24小时继续读写。 金盘:失常名称是Gold,即金盘,价格昂贵享有五年保固,WDGold 硬盘的工作负载容量远高于个别硬盘,采纳多项技术,可提供更高的可靠性、容量、效用和性能。专为大量数据中心的应用程序而设计,非常适合用于须要高度牢靠存储设备并提供全天候反对的高可用性服务器和存储阵列,包含中小型企业服务器与存储、机架数据中心服务器、存储集群等。 从这个数据来看,一般硬盘并不适宜长时间间断读写,如果长时间不间断读写的话,势必会不堪重负呈现问题;而监控级硬盘能够实现7*24全年无休的继续工作。紫盘反对相当于一般硬盘3倍的工作负载评级,能够更好地满足视频监控零碎的超高需要。因而在稳定性和可靠性方面,监控硬盘无疑胜出一筹。1.硬盘的寿命 监控硬盘是一种比一般硬盘更实用的硬盘。 一般硬盘在上电启动的时候会全速启动,霎时电流可能达到2安,甚至更高。 而监控硬盘启动的时候会迟缓减速,启动电流会管制在2安以下。因为监控零碎中通常会装置多个硬盘,这样在启动的霎时会产生很大的启动电流,如果是一般硬盘的话,电源会难以承受,甚至烧毁。 另外,监控系统对硬盘的传输速度要求个别不高,然而会频繁的小数据量的读写。 所以须要在磁头读写机构上针对监控零碎的读写特点做构造优化设计,以缩短磁头的寿命。 监控硬盘的实践均匀无故障运行工夫比一般电脑硬盘要长得多,稳定性、可靠性要更高。 2.间断工作工夫差异 一般电脑硬盘的设计以8×5为根底。8×5指的是一般一般电脑硬盘每天工作8个小时,每周工作5天(硬盘工作是指硬盘处于读写状态。须要特地留神的是硬盘加电后有两个状态:工作状态和期待状态,其中工作状态是硬盘进行读写工作,期待状态是硬盘没有进行读写工作但处于待命状态。)监控硬盘是按24×7的企业级环境要求进行设计开发的。24×7就是每天工作24个小时,每周工作7天(就是连续不断的进行工作)。所以一般电脑硬盘并不适宜长时间间断读写,如果要求一般电脑硬盘长时间间断读写就会极大的伤害硬盘,使硬盘出现异常声音、读写谬误、工作停止等多种问题并由此导致硬盘最终损坏。硬盘录像机是长年不间断运行的,因而要求硬盘必须能够长时间间断工作,能满足这个要求的就是监控硬盘。 3.启动差异 市面上大多数硬盘都蕴含电机零碎,因而硬盘加电启动的时候与电机加电启动的状况相相似:就是在加电的初始工夫会呈现较大的启动电流以实现硬盘启动。如果加电启动的时候硬盘内部供电系统不能保障足够的电流反对,则硬盘会启动失败,导致硬盘不可用。并且如果屡次呈现这种状况,容易造成硬盘的损坏。因为硬盘电机的供电是由内部电源零碎直流12V满足的,因而在评估硬盘启动电流的时候都以直流12V的电流为规范。 一般电脑硬盘启动电流个别在2.8A~3.2A之间。监控硬盘的启动电流最高为2.0A。数字硬盘录像机常常采纳多硬盘进行数据存储,以装置8片硬盘为例:一般电脑硬盘启动电流2.8×8=22.4A,最低的功耗为22.4A×12V=268.8W。DVR专用硬盘启动电流2.0A×8=16A,最高的功耗为16A×12V=192W。 能够看出,在雷同硬盘数量的状况下监控硬盘对外部电源零碎的要求更低(在同样输出功率的内部电源反对下,能够装置的DVR专用硬盘的数量会更多)。 阐明:在上例的状况中,如果内部电源零碎供电只能保障250W,则采纳一般PC硬盘的时候会呈现某些硬盘能够辨认,某些硬盘不能够辨认,并且没有规律性。 4.运行功耗及散热差异 这里的运行功耗是指硬盘在失常读写状态时的功耗。PC硬盘的运行功耗个别为14.5瓦左右,监控硬盘的运行功耗个别为8瓦左右。能够看出,监控硬盘运行功耗仅相当于一般电脑硬盘功耗的55%,低的运行功耗不仅对电源零碎有重要意义,而且对数字硬盘录像机零碎的散热也有重要的意义(运行功耗中大略75%会转变为热能)。 在数字硬盘录像机中,如果装置8片硬盘,那一般PC硬盘的运行总功耗为14.5W×8=116W,DVR专用硬盘的运行总功耗为8W×8=64W。如果采纳一般PC硬盘,能够想像在数字硬盘录像机这么小的空间内有这么高的发热源,对系统的散热要求是很高的,为了保障硬盘有一个正当的环境温度(0~60℃),必须对硬盘零碎进行无效的散热。而采纳DVR专用硬盘,发热状况就好得多—-升高了零碎散热要求使系统对环境的适应性更强。 5.传输差异 监控硬盘除了采纳传统电脑硬盘的传输模式,还引入了一个更新的传输模式—-不间断传输模式,该传输模式最大为65MB/S。通过引入不间断传输模式,使硬盘对流媒体的反对更加牢靠,充沛保障数字硬盘录像机在录入的同时进行回放的流畅性和稳定性。这是其它硬盘所不具备的个性。 6.价格差异 相比一般电脑硬盘,监控硬盘在价格上与其相差无几,只是在非凡技术上的反对有所不同,前者更重视性能后者则重视稳固读写。只有企业级硬盘才绝对贵一些,毕竟要求更加刻薄一些。 7.硬盘寻道工夫 监控的最大特点是要求硬盘长时间继续写入,反而对数据读取没有太高要求。这一点一般硬盘从设计上就基本不能符合要求。一般硬盘在家用环境下往往是随机小文件读写谋求寻道速度快,而监控硬盘则适宜间断大文件继续写入,具备良好的稳定性。 对于工程商而言,装置监控硬盘稳固了,前期保护服务老本升高,同时客户对你的口碑也会晋升,让客户释怀,利润天然减少。对于客户而言,故障少了,数据失落危险就小,数据安全性进步,让监控起到应有的防护作用,两全其美! 官网:http://www.xmjisujia.cn 公众号:极速佳

November 1, 2021 · 1 min · jiezi

关于内存:物理内存管理连续内存分配

在没有其余技术支持下,调配给一个过程的内存空间必定是间断的,如何分配内存给过程能力使得物理内存利用率更好?因为每个过程执行工夫不同,所以会有一些过程比拟早完结,也有一些过程开始得比拟晚。当给新的过程分配内存空间时,有些开释掉的内存空间比拟小,无奈利用,天然就造成了内存碎片。 1.1 内存碎片间断内存调配是指给过程调配一块不小于指定大小的间断物理内存。如图1所示,一开始先给3个过程别离调配了间断的物理内存,其中过程P1占地址空间0-2,过程P2占地址空间3-8,过程P3占地址空间9-14。而后过程P2被开释掉,此时若须要调配一个内存大小为10的过程,则过程2开释掉的内存空间无奈给新过程,造成了内存碎片,能够看出所谓的内存碎片其实是相对而言的。 <center>图1</center> 内存碎片分类: 内部碎片:已分配内存单元之间的未被应用的内存空间,例如过程P2所开释的内存外部碎片:调配单元外部的未被应用的内存,然而取决于调配单元是否须要取整。例如调配510内存空间,实际上只能调配512这种$2^n$大小的内存空间,这时候就有2个字节无奈利用,造成外部碎片,如图2所示的黄色块。 <center>图 2</center> 1.2 间断内存调配-动态内存调配间断的内存空间如何调配能力更好地进步内存利用率?依据过程指定一个大小可变的分区(块,内存块)称为动态内存调配,因为依据理论状况为过程找一块适合大小的内存块,大一点的过程天然也调配更大的内存块,小的过程调配小的内存块,所以就叫动态内存调配了。 当初假如有5个过程,占用内存状况如图3(左)所示,过程2和过程4完结后内存占用状况如图3(右)所示。 <center>图3</center> 因为要给新过程找个适合的内存块,首先得晓得哪些内存块是闲暇的,哪些是被配置,操作系统须要保护这样的一个数据结构。那又该如何给某个过程找到一个适合的内存块使得总体内存利用率更好呢,有几种策略。别离是最先匹配法,最佳匹配法还有最差匹配法。 最先匹配法:调配n个字节,应用第一个可用的比n大的闲暇块。举个栗子,图4(左)蓝色块示意为按地址排列的闲暇块,如果要分500B内存块给新过程 ,那么1K内存块会是第一个找到的比500B大的闲暇块,将其中的500B内存调配给新过程,新的被调配的内存块会退出被调配的列表中,并注明是哪个过程所应用,剩下的500K闲暇块更新到闲暇列表中去。调配后闲暇块如图4(b)所示。 <center>图4</center> 实现过程: 闲暇分区按地址排序调配过程中,从头往后找,寻找第一个适合的内存块,更新列表开释内存块时,查看是否可与邻近闲暇块合并,更新列表长处: 简略因为后面找到了就不再往后找了,所以高地址可能会保留着较大的内存块,不便前面更大的过程申请更大的内存块。毛病: 因为会把较大内存块分成几个小块,所以会留下内部碎片因为留下内部碎片,所以申请较大内存块时候会比较慢最佳匹配:调配n字节内存块时候,查找并应用大于n的最小内存块。举个栗子,还是如图4(左)所示,蓝色块示意为按地址排列的闲暇块,如果要给新过程调配2k字节的内存块,那么将会找到最初那个3k内存块,并调配2k被新过程,同时并更新被调配和空间内存的列表。 实现过程如下所示: 闲暇分区从小到大排序调配时,查找一个适合的分区,更新列表开释内存块时,查找并且合并邻近的闲暇区域(如果找到,邻近指的是地址邻近不是大小邻近,所以相比最先匹配简单一些),更新列表长处: 可防止的闲暇分区被拆分可缩小内部碎片大小绝对简略毛病: 开释内存块时较慢内部碎片容易产生很多无用的小碎片最差匹配:调配n字节,应用尺寸不小于n的最大闲暇内存块。举个栗子,还是如图4(左)所示,蓝色块示意为按地址排列的闲暇块,如果要给新过程调配2k字节的内存块,那么将会找到两头那个10k内存块,并调配2k被新过程,同时并更新被调配和空间内存的列表。 实现过程如下所示: 闲暇分区按从大到小排序调配选最大内存块开释内存块时,查看是否可与邻近的闲暇块去进行合并,并调整闲暇内存块列表程序长处: 中等大小的内存块调配较多时,成果最好,因为剩下那内存块还能利用起来,相当于剩下的小块比拟少避免出现太多的碎片毛病 : 开释分区较慢内部碎片容易毁坏大的闲暇内存块,后续难以调配大的内存块# 3.物理内存治理-间断内存调配

October 26, 2021 · 1 min · jiezi

关于内存:第45问MySQL-的内存突增-该如何诊断

问在 第44问 中, 咱们应用 tcmalloc 提供的工具, 来查看 MySQL 的内存调配 该办法对性能影响不大, 能够在生产环境运行, 但须要将 MySQL 的分配器配置成 tcmalloc 在本次试验中, 咱们介绍另外一种办法, 针对于 MySQL 的内存突增状况进行诊断 试验咱们仍然宽油起一个数据库: 本试验中, 咱们须要模仿MySQL的内存突增的状况. 咱们从 MySQL 的 bug 库里找到一个易于复现的相干 bug: https://bugs.mysql.com/bug.ph... 这个 bug 的形容很清晰, 并提供了一个 SQL 脚本, 间接执行该脚本就能够复现内存激增的状况: 咱们来试一下: 以后 MySQL 的内存占用为181M 执行脚本: 在脚本执行过程中, 咱们会发现 MySQL 应用的内存在一直上涨 当初咱们来诊断 MySQL 对内存的应用. 在脚本执行过程中, 同时执行如下命令进行观测: 小贴士 什么是零碎调用mmap? 简略来说, MySQL不是间接向Linux申请内存, 而是向glibc申请内存. glibc会维持一个内存池, 当glibc发现内存池吃紧时, 会通过零碎调用mmap(或者brk)向Linux申请内存. 所以咱们监听系统调用mmap, 也就监听了MySQL在什么状况下须要大量内存 (即什么时候glibc的内存池吃紧了) ...

September 3, 2021 · 1 min · jiezi

关于内存:第44问MySQL-的内存消耗-有哪些不在-performanceschema-的统计范围

问当 MySQL 内存异样上涨, 咱们能够通过 performance_schema 察看内存的应用, 咱们在试验5中进行过介绍。 但咱们也会发现操作系统统计的 MySQL 内存用量比 performance_schema 统计的 MySQL 内存用量要多。 那么 MySQL 的内存耗费, 有哪些是不在 performance_schema 统计内的呢? 本期咱们设计试验来察看这个问题 试验咱们先装置 google-perftools: 装置后, 能够找到相干的库: 宽油起一个数据库: 记下数据库的启动参数, 并关掉数据库: 咱们在数据库启动命令前, 减少两个环境变量 LD_PRELOAD 和 HEAPPROFILE. 其中 LD_PRELOAD 指向方才咱们装置的 tcmalloc 库, HEAPPROFILE 是输入文件的门路. 启动数据库后, 咱们看到日志中输入了两个 heap 文件. 咱们在数据库中减少一些压力, 还是用咱们相熟的翻倍法: 多做点数据: 察看到输入了更多的 heap 文件: 上面咱们装置 pprof 来解析这些 heap 文件. 先下载 golang , 此步骤须要挂代理: ...

August 27, 2021 · 1 min · jiezi

关于内存:如何避免JS内存泄漏

简介: 很多开发者可能平时并不关怀本人保护的页面是否存在内存透露,起因可能是刚开始简略的页面内存透露的速度很迟缓,在造成重大卡顿之前可能就被用户刷新了,问题也就被暗藏了,然而随着页面越来越简单,尤其当你的页面是 SAP 形式交互时,内存透露的隐患便越来越重大,直到忽然有一天用户反馈说:“操作一会儿页面就卡住不动了,也不晓得为什么,以前不这样的呀”。这篇文章通过一些简略的例子介绍内存透露的考察办法、总结内存透露呈现的起因和常见状况,并针对每种状况总结如何防止内存透露。心愿能对大家有所帮忙。 作者 | 木及起源 | 阿里技术公众号 很多开发者可能平时并不关怀本人保护的页面是否存在内存透露,起因可能是刚开始简略的页面内存透露的速度很迟缓,在造成重大卡顿之前可能就被用户刷新了,问题也就被暗藏了,然而随着页面越来越简单,尤其当你的页面是 SAP 形式交互时,内存透露的隐患便越来越重大,直到忽然有一天用户反馈说:“操作一会儿页面就卡住不动了,也不晓得为什么,以前不这样的呀”。 这篇文章通过一些简略的例子介绍内存透露的考察办法、总结内存透露呈现的起因和常见状况,并针对每种状况总结如何防止内存透露。心愿能对大家有所帮忙。 一 一个简略的例子先看一个简略的例子,上面是这个例子对应的代码: 代码 1 代码 1 的逻辑很简略:点击“add date”按钮时会向 dateAry 数组中 push 3000 个 new Date 对象,点击“clear”按钮时将 dateAry 清空。很显著,“add date”操作会造成内存占用一直增长,如果将这个逻辑用在理论利用中便会造成内存透露(不思考成心将代码逻辑设计成这样的状况),上面咱们看一下如何考察这种内存增长呈现的起因以及如何找出内存透露点。 1 heap snapshot为了防止浏览器插件的烦扰,咱们在 chrome 中新建一个无痕窗口关上上述代码。而后在 chrome 的 devtools 中的 Memory 工具中找到 “Heap Snapshot”工具,点击左上角的录制按钮录制一个 Snapshot,而后点击“add date”按钮,在手动触发 GC(Garbage Collect)之后,再次录制一个 Snapshot,重复执行上述操作若干次,像图 1 中操作的那样,失去一系列的 Snapshot。 图 1 录制 Snapshot 图 2 是咱们刚刚失去的 Snapshot 组,其中的第一个是页面初始加载的时候录制的,不难发现,从第二个开始,每个 Snapshot 相比于上一个其大小都减少了约 200KB,咱们点击抉择 Snapshot 2,在 class filter 输入框中处输出 date,能够失去 Snapshot 2 中所有被 Date 结构器结构进去的 JS 对象,也就是 Date 对象,这里看到的结构器跟浏览器外部的实现无关,不用跟 JS 的对象对应。 ...

August 6, 2021 · 4 min · jiezi

关于内存:为您的设计选择正确的内存解决方案

内存被视为一种必要的技术,然而工厂自动化、主动驾驶汽车、便携式医疗设施、边缘计算和物联网传感器等新利用正在迫使性能要求发生变化。因而用于评估和抉择特定应用程序内存的规范也产生了变动。 NAND、NOR和FRAM在各种利用中都有不同的劣势。因为这些解决方案中的每一个都有针对特定指标应用程序的独特功能集,因而确定哪种类型的内存解决方案可为所需应用程序提供最佳性能和可靠性十分重要。数据中心、计算机和生产设施中的海量存储须要最高的密度和最低的每比特老本,通常由NAND闪存反对。嵌入式计算机存储启动代码和事务数据,通常在NORFlash中可用。数据记录器、传感器和边缘计算机在长时间内捕捉疾速变动的数据应用专门的技术,例如铁电RAM(FRAM)。 让咱们看看哪些内存与这些工作负载相匹配: NAND闪存NAND FLASH常见于数据中心、云服务器和生产设施中。它的高密度使其成为这些工作的现实抉择,并且因为更好的工艺和几何膨胀,技术失去了改良。每GB老本持续降落,读/写性能持续体现良好。然而这种解决方案也存在肯定的局限性:每个编程/擦除周期都会对单元的小尺寸造成压力,从而升高其耐用性,并且尺寸也意味着大概10年的数据保留工夫低于其余内存解决方案。 NOR闪存NOR Flash具备用于保留长数据的大型存储单元和字节可寻址架构,因而非常适合用于疏导代码,其中包含就地执行零碎和事务数据。NOR牢靠且易于应用,其最新改良为故障安全操作提供性能平安和平安启动工具。对于汽车仪表盘和其余性能,NOR能够反对疾速读取和即时启动性能。然而NOR密度低于NAND。 FRAM内存FRAM非常适合须要通过应用较少的功率长时间间断捕捉数据的设施。存储单元交替极性,应用起码的能量来缩短电池寿命并提供近乎有限的耐用性。FRAM还打消了能量或电荷透露问题,因而数据保留至多能够继续100年。与广泛认识相同,FRAM不会被磁场烦扰;然而它的密度无奈反对编码和海量存储需要。 归结为:抉择适合的内存有助于确保产品的胜利。NAND闪存非常适合云计算和集体电子产品中常见的海量存储工作负载。相比之下,NOR Flash和FRAM实用于嵌入式零碎和边缘设施,它们通常位于极其和偏僻的环境中。随着边缘计算的倒退,人工智能的提高正在推动新性能的倒退,并进步对进步故障安全操作规范的需要,这反过来迫使制造商提供具备平安和安保性能的NOR Flash和FRAM。内存存储芯片供应商英尚微电子可提供产品相干技术支持.

July 27, 2021 · 1 min · jiezi

关于内存:OracleHugePage-Size-计算脚本

HugePage计算须要SGA等失效,须要先配置好内存 #!/bin/bash## hugepages_settings.sh## Linux bash script to compute values for the# recommended HugePages/HugeTLB configuration# on Oracle Linux## Note: This script does calculation for all shared memory# segments available when the script is run, no matter it# is an Oracle RDBMS shared memory segment or not.## This script is provided by Doc ID 401749.1 from My Oracle Support# http://support.oracle.com# Check for the kernel versionKERN=`uname -r | awk -F. '{ printf("%d.%d\n",$1,$2); }'`# Find out the HugePage sizeHPG_SZ=`grep Hugepagesize /proc/meminfo | awk '{print $2}'`if [ -z "$HPG_SZ" ];then echo "The hugepages may not be supported in the system where the script is being executed." exit 1fi# Initialize the counterNUM_PG=0# Cumulative number of pages required to handle the running shared memory segmentsfor SEG_BYTES in `ipcs -m | cut -c44-300 | awk '{print $1}' | grep "[0-9][0-9]*"`do MIN_PG=`echo "$SEG_BYTES/($HPG_SZ*1024)" | bc -q` if [ $MIN_PG -gt 0 ]; then NUM_PG=`echo "$NUM_PG+$MIN_PG+1" | bc -q` fidoneRES_BYTES=`echo "$NUM_PG * $HPG_SZ * 1024" | bc -q`# An SGA less than 100MB does not make sense# Bail out if that is the caseif [ $RES_BYTES -lt 100000000 ]; then echo "***********" echo "** ERROR **" echo "***********" echo "Sorry! There are not enough total of shared memory segments allocated forHugePages configuration. HugePages can only be used for shared memory segmentsthat you can list by command: # ipcs -mof a size that can match an Oracle Database SGA. Please make sure that: * Oracle Database instance is up and running * Oracle Database 11g Automatic Memory Management (AMM) is not configured" exit 1fi# Finish with resultscase $KERN in '2.4') HUGETLB_POOL=`echo "$NUM_PG*$HPG_SZ/1024" | bc -q`; echo "vm.hugetlb_pool = $HUGETLB_POOL" ;; '2.6' | '3.8' | '3.10' | '4.1' | '4.14' | '4.18' | '5.4') echo "vm.nr_hugepages = $NUM_PG" ;; *) echo "Kernel version $KERN is not supported by this script (yet). Exiting." ;;esac# End

July 17, 2021 · 2 min · jiezi

关于内存:分布式-Prepare-Statement-协议游标可行性

作者:鲍凤其爱可生 dble 团队开发成员,次要负责 dble 需要开发,故障排查和社区问题解答。少说废话,放码过去。本文起源:原创投稿*爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。背景MySQL JDBC 在执行查问语句时,默认把查问的所有后果全副取回放在内存中,如果遍历很大的表,则可能把内存撑爆。 方法 1查问语句中应用 limit,offset;这样咱们会发现取数据的越来越慢,起因是在设置了 offset 之后,MySQL 须要将读取地位挪动到 offset 的地位,随着 offset 增大,取数据也越来越慢; 方法 2用数据流的形式取数据,能够指定 fetch size,这样每次获取指定数量的数据行,从而防止 OOM。此种形式的应用形式和原理能够参见文章:prepare statement 协定第 2 种形式理论是 MySQL 中的 server-side 游标,server-side 游标是应用 MySQL 外部长期表来实现的。初始的时候,外部长期表是个内存表,当这个表的大小超过 max_heap_table_size and tmp_table_size 两个零碎变量的最小值的时候(两者的最小值),会被转换成 MyISAM 表,即落盘存储。外部长期表的应用限度同样实用于游标的外部长期表。 MySQL 中的两种长期表内部长期表用户通过 CREATE TEMPORARY TABLE 语句显式创立的长期表,这样的长期表称为内部长期表。内部长期表生命周期:创立后,只在以后会话中可见,以后会话完结的时候,该长期表也会被主动敞开。因而,两个会话能够存在同名的长期表,但若有同名的非长期表时,直到长期表被删除,这张表对用户是不可见的。 外部长期表外部长期表是一种非凡轻量级的长期表,用来进行性能优化。这种长期表会被 MySQL 主动创立并用来存储某些操作的两头后果。这些操作可能包含在优化阶段或者执行阶段。这种外部表对用户来说是不可见的,然而通过 EXPLAIN 或者 SHOW STATUS 能够查看 MySQL 是否应用了外部长期表用来帮忙实现某个操作。外部长期表在 SQL 语句的优化过程中扮演着十分重要的角色,MySQL 中的很多操作都要依赖于外部长期表来进行优化。然而应用外部长期表须要创立表以及两头数据的存取代价,所以用户在写 SQL 语句的时候应该尽量的去防止应用长期表。 外部长期表有两种类型1、HEAP 长期表这种长期表的所有数据都会存在内存中,对于这种表的操作不须要 IO 操作。 2、OnDisk 长期表顾名思义,这种长期表会将数据存储在磁盘上。OnDisk 长期表用来解决两头后果比拟大的操作。如果 HEAP 长期表存储的数据大于 MAX_HEAP_TABLE_SIZE(参数参考链接),HEAP 长期表会被主动转换成 OnDisk 长期表。OnDisk 长期表在 5.7 中能够通过 INTERNAL_TMP_DISK_STORAGE_ENGINE 零碎变量抉择应用 MyISAM 引擎或者 InnoDB 引擎。 ...

December 10, 2020 · 1 min · jiezi

关于内存:ram存储器基本概念

目前的铁路和电力及航空航天等多个行业已纷纷推广系统可靠性剖析RAM技术,研发最佳的设施运行保护计划,从而打消设施隐患,防止设施事变产生,升高安装非打算复工次数和设施运行维护费用,促成安装平安长周期运行,具备重要的现实意义。本文次要概括介绍RAM技术的基本概念。 ram为可靠性(Reliability)、可用性(Availability)、可维护性(Maintainability)的简称,是以可靠性实践为根底的延长和倒退.RAM通过对历史数据的回归或专家教训的总结,对故障产生的概率进行计算,从而量化危险,同时针对该危险而履行的监测、检测或检培修不同计划的优劣进行量化比拟,从而辅助管理层作出正确的决策。其相干概念简述如下: (1)可靠性,指某一时间段内设施或零碎间断运行而无故障产生的概率。重要表征参数是均匀无故障工夫(Mean Time Between Failure,简称MTBF)是指相邻两次故障之间的均匀工作工夫,也称均匀故障间隔时间。如图1所示MTBF= ,零碎故障率= 1/MTBF。 图1MTBF与MDT的含意及关系 (2) 可维护性,指随时间推移培修工作未实现的概率。重要表征参数是均匀修复工夫(Mean Time ToRepair,简称MTTR),包含诊断、组件修理与替换及现场调整与测试的工夫。零碎修复率u = 1/MTTR.均匀停机工夫(Mean Down Time,简称MDT)是将MTTR的定义扩大至包含经营及/或保护员工达到现场前的反应时间(Reaction Time,简称RT),是掂量培修效率的尺度,由均匀修理工夫与均匀等待时间组成。即MDT=MTTR +RT。如图1所示,MDT = 。 (3)可用性,指某一段时间内设施或零碎可失常运行的工夫百分比。可用性=运行工夫/(运行工夫+复工工夫)。零碎固有可用性(Inherent availability,简称IA) =MTBF/(MTBF+MTTR)。零碎经营可用性(Operational availability,简称OA)) =MTBF/(MDT+MTTR)。因为运行工夫受可靠性影响、复工工夫受可维护性影响,因而可用性由可靠性及可维护性决定,如图2所示。 图2故障率浴盆曲线

November 24, 2020 · 1 min · jiezi

关于内存:MRAM与其他内存技术相比具有相对优势

MRAM是一种非易失性存储技术,能够在不须要电源的状况下将其内容保留至多10年。它实用于在零碎解体期间须要保留数据的商业利用。基于MRAM的设施能够为“黑匣子”利用提供解决方案,因为它以SRAM的速度写入数据,同时在产生总功耗之前保留数据。Everspin一级代理英尚微电子本文介绍MRAM与其余内存技术的相比拟。 MRAM与内存 内存选项的比拟与其余内存技术选项相比,MRAM具备显著的劣势(下表1)。 表格1 MRAM与其余内存技术相比具备绝对劣势 Flash这项技术利用电荷存储在笼罩在栅极氧化物上的一块浮动多晶硅(浮动栅极)上。对闪存位单元进行编程须要一个高电压场,该场能使电子减速得足够快,以克服硅与浮栅之间的氧化物的能垒。 这导致电子穿透氧化物并为浮置栅极充电,从而扭转了位单元晶体管的阈值电压。电子通过氧化物的重复转移逐步使氧化物资料磨损,在位不再起作用之前,闪存被限度为10K-1M写周期。 间断写入会在10天之内耗尽一些闪存。同时因为不波及充电或放电,MRAM能够接受有限的写入周期。编程过程中会旋转磁极,这是一种无损且无损的操作。 在编程期间,闪光灯须要低压能力使电子穿过氧化物资料。MRAM应用产生磁场的电流来编程自在层。此外,闪存对存储器阵列的大块执行编程器擦除操作。MRAM在单个地址上执行写入。 SRAMSRAM应用放弃CMOS逻辑电平的有源晶体管,须要电源能力保留存储器内容。MRAM存储器的内容放弃在其自在磁性层的极性中。因为该层是磁性的,即便没有电源也能够放弃其状态。 随着技术一直缩小SRAM单元的体积,较小的几何器件往往会透露更多电流。对于单个单元来说,这种透露很小,然而当与存储设备中的数百万个单元相乘时,透露就变得很显著。随着技术的萎缩,这种影响无望放弃。鉴于MRAM的非易失性,能够在零碎中应用掉电技术以实现零电流透露。 电池供电的SRAM它由一个SRAM单元和一个包装在同一包装中的电池组成。该非易失性存储器应用电池电量来保留存储器内容。同时MRAM不须要电池来保留数据,并且以比电池后备SRAM更快的速度执行读/写操作。这样能够进步可靠性并打消与电池解决无关的环境问题。 EEPROM与MRAM相比,该独立存储器的编程速度要慢得多,并且写入循环能力无限。 NVSRAM也称为非易失性SRAM,它联合了SRAM和EEPROM性能。它会在断电时将数据从SRAM存储到EEPROM。然而数据传输十分慢,并且在数据传输期间须要大的内部电容器来放弃NVSRAM的电源。MRAM提供了更快的写入速度,能够在失常的零碎操作期间写入数据。 因而在掉电期间起码的数据传输是必须的。应用MRAM的应用程序也能够受害于平安写入存储器而无需应用大型内部电容器。 FRAM另一个非易失性RAM铁电RAM(FRAM)具备典型的小型阵列大小,范畴从4Kbit到1Mbit。阵列尺寸很小,因为该技术的可扩展性无限,无奈进一步放大位单元的尺寸。 没有这种可伸缩性限度,MRAM能够提供更大的内存阵列。而且MRAM的编程速度比FRAM快。一些FRAM具备无限的循环能力(例如100亿个循环)。他们还须要在读取后刷新存储器,因为该操作会毁坏正在读取的位单元的内容。 DRAM应用此技术,必须常常刷新内存以保留数据。

September 16, 2020 · 1 min · jiezi

一文了解-Redis-内存监控和内存消耗

Redis 是一种内存数据库,将数据保存在内存中,读写效率要比传统的将数据保存在磁盘上的数据库要快很多。所以,监控 Redis 的内存消耗并了解 Redis 内存模型对高效并长期稳定使用 Redis 至关重要。 内存使用统计通过 info memory 命令可以获得 Redis 内存相关的指标。较为重要的指标和解释如下所示: 属性名属性说明used_memoryRedis 分配器分配的内存总量,也就是内部存储的所有数据内存占用量used_memory_human以可读的格式返回 used_memoryused_memory_rss从操作系统的角度显示 Redis 进程占用的物理内存总量used_memory_rss_humanused_memory_rss 的用户宜读格式的显示used_memory_peak内存使用的最大值,表示 used_memory 的峰值used_memory_peak_human以可读的格式返回 used_memory_peak的值used_memory_luaLua 引擎所消耗的内存大小。mem_fragmentation_ratioused_memory_rss / used_memory 的比值,可以代表内存碎片率maxmemoryRedis 能够使用的最大内存上限,0表示没有限制,以字节为单位。maxmemory_policyRedis 使用的内存回收策略,可以是 noeviction、allkeys-lru、volatile-lru、allkeys-random、volatile-random 或者 volatile-ttl。默认是noeviction,也就是不会回收。 当 mem_fragmentation_ratio > 1 时,说明有部分内存并没有用于数据存储,而是被内存碎片所消耗,如果该值很大,说明碎片率严重。当 mem_fragmentation_ratio < 1 时,这种情况一般出现在操作系统把 Redis 内存交换 (swap) 到硬盘导致,出现这种情况要格外关注,由于硬盘速度远远慢于内存,Redis 性能会变得很差,甚至僵死。 当 Redis 内存超出可以获得内存时,操作系统会进行 swap,将旧的页写入硬盘。从硬盘读写大概比从内存读写要慢5个数量级。used_memory 指标可以帮助判断 Redis 是否有被swap的风险或者它已经被swap。 在 Redis Administration 一文 (链接在文末) 建议要设置和内存一样大小的交换区,如果没有交换区,一旦 Redis 突然需要的内存大于当前操作系统可用内存时,Redis 会因为 out of memory 而被 Linix Kernel 的 OOM Killer 直接杀死。虽然当 Redis 的数据被换出 (swap out) 时,Redis的性能会变差,但是总比直接被杀死的好。 ...

October 16, 2019 · 2 min · jiezi

页面CPU和内存占用监控可视化Chrome插件Graph-Process

写这个插件的原因是最近要对比一下页面的 cpu 和内存占用的性能,本来是想找看看有没有什么软件能够去可视化一下当前标签页的cpu和内存占用,但是发现却找不到这种软件,mac 上有个活动监视器,但是当你开很多标签页的话并不很好的监听当前标签页的 cpu 和内存占用,能看到谷歌浏览器的 rendered 进程,但是谷歌浏览器的 rendered 进程很多你并不知道是哪个,而且也没有可视化进行查看平均的 cpu 和内存占用,后来看到谷歌浏览器有个任务管理器可以查看当前标签页的 cpu 占用和内存占用,于是想到有没有人已经写了这种插件,但是遗憾的是并没有,后面仔细搜索了谷歌浏览器插件开发文档确定想要的功能能实现,于是这个插件就诞生了????效果图 主要功能对当前标签页点击插件图标,会开始对当前的标签页也就是页面的CPU和内存进行监控,并生成对应的变化折线图和平均值和表格,平均值如果超过一定范围会有颜色变化。 有人可能会问页面开启扩展后会对当前页面统计造成影响,其实是不会的,谷歌扩展是独立的进程,不会对当前的页面的 cpu 和内存占用造成影响。 安装插件注意,由于使用了谷歌浏览器的实验特性,因此插件需要运行在谷歌浏览器开发者版,由于使用了谷歌浏览器的实验特性,因此插件需要运行在谷歌浏览器开发者版,由于使用了谷歌浏览器的实验特性,因此插件需要运行在谷歌浏览器开发者版,重要的事情说三遍,可以在这里下载开发版谷歌浏览器开发者版插件地址:https://chrome.google.com/web...

October 15, 2019 · 1 min · jiezi

JVM-栈stack溢出案例

介绍当启动一个新线程时,JVM就会给这个线程分配一个Java栈(这个栈的内存大小由-Xss参数来设置)。 一个Java栈的基本单位是帧,每一次函数调用就会生成栈帧,占用一定的栈空间。当函数本身需要的内存过大,或者函数调用函数(依赖调用或者递归调用)太深,超过了-Xss设置的内存大小,就会抛出StackOverflowError异常。 -Xss:默认值 1M,控制每个线程占用的内存,这个参数决定了函数调用的最大深度。如果设置的太小可能会很容易出现 StackOverflowError 异常。 JDK 5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。 示例代码public class StackOverflow { private static int deep = 1; /** * 通过无限递归来模拟栈溢出 */ private static void recursion() { deep++; recursion(); } public static void main(String[] args) { try { recursion(); } catch (Throwable e) { // catch 捕获的是 Throwable,而不是 Exception。因为 StackOverflowError 不属于 Exception 的子类。 System.out.println("Stack deep : " + deep); e.printStackTrace(); } // 不让进程结束,便于使用分析工具来查看内存情况 try { Thread.sleep(24 * 60 * 60 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } }}执行结果 ...

July 2, 2019 · 1 min · jiezi

JVM-堆heap溢出案例

一、说明当虚拟机申请不到内存空间的时候,会报堆内存溢出: OutOfMemoryError:java heap space。 常见的原因:http://outofmemory.cn/c/java-...我测试到时候,运行在 16G 内存的机器上。JVM 堆内存 默认为物理内存的1/4,即 16 * 1/4 = 4G JDK 8的 JVM 在 JDK 7 的基础上从堆内存中移除了永久代(Perm Generation),替换为了堆内存之外的元空间(Metaspace),元空间是堆外直接内存,不受堆内存的限制,只受物理内存的限制,可以提供更大的空间。二、原因及解决办法OutOfMemoryError 异常的常见原因: 加载的数据过大。如:加载的文件或者图片过大、一次从数据库取出过多数据代码存在死循环或循环产生过多的对象解决方法 增加jvm的内存大小,使用 -Xmx 和 -Xms 来设置检查代码中是否有死循环或递归调用。检查是否有大循环重复产生新对象实体。检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。检查List、Map等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。三、代码示例/**java堆溢出实例 * 原理:java的堆是用来存放对象实例的,所以我们只要做到以下三点就可以使堆溢出: * 1、限制堆的大小,不可扩展 * 2、不断新建对象 * 3、保持对象存活不被回收 * 对应的,我们需要: * 1、改变JVM的启动参数,将堆的最小值和最大值设成一样,这样就可以避免堆自动扩展(其实不一样也可以) * 2、不断产生对象 * 3、使用一个List来保存对象,保持对象存活 * * JVM配置参数: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError * */public class HeapOom { public static void main(String[] args) { // 此list实例会存放在堆内存中 List<byte[]> list = new ArrayList<>(); int i = 0; boolean flag = true; while (flag) { try { i++; // 每次增加一个1M大小的数组对象 list.add(new byte[1024 * 1024]); } catch (Throwable e) { // catch 捕获的是 Throwable,而不是 Exception。因为 OutOfMemoryError 不属于 Exception 的子类。 e.printStackTrace(); flag = false; // 记录次数 System.out.println("count=" + i); } } // 不让进程结束,便于使用分析工具来查看内存情况 try { Thread.sleep(24 * 60 * 60 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } }}使用的 java 1.8.0_171 版本: ...

July 1, 2019 · 2 min · jiezi

燃烧我的卡路里 ---- Flutter瘦内存瘦包之图片组件

背景在电商类APP里,图片到现在为止仍然是最重要的信息承载媒介,不得不说逛淘宝的过程,其实就是一个看图片的过程。而商品详情页中的图片,通常是页面中内存占用最多的内容,占用了整个页面内存的超过 50%。闲鱼在Flutter化的过程中,选择了商品详情页作为第一个落地的场景。通过多版本的迭代完善,基于Flutter的详情页已经在闲鱼稳定运行。然而正因为详情页的图片量大,导致Flutter里图片相关的问题一直挥之不去。1:内存问题 — 连续push flutter界面内存累积2:安装包问题 — 过渡时期两份重复资源文件。3:寻址缓存问题 — 原有的寻址缓存策略无法复用。4:图片复用问题 — Native和Flutter重复下载相同图片。解决方案—-FXTexImage_V1为了解决这些问题,我们尝试着寻找一种新的思路,一种能够将flutter与native串联起来的思路。而之前做视频播放器的方案给了我们启发。熟悉Flutter的同学应该都知道,Flutter的视频组件是基于一个Flutter提供的一个叫“外接纹理”的技术实现的,关于flutter外接纹理,本人另外有一篇文章有更详细的论述,这里不再赘述。https://mp.weixin.qq.com/s/KkCsBvnRayvpXdI35J3fnw我们将每一张图片假想成一个:静态的视频。图片的内容由一个external_texture来负责显示,而这个external_texture则由native端提供具体的渲染数据。通过这种方案,我们便可以通过external_texture这座桥梁,将flutter作为native端图片的一个最终展示场所。而所有的下载、缓存、裁剪等逻辑都可以复用原来的native图片库。基于这个基本框架,我们形成了我们第一版本的图片渲染组件:FXTexImage—-V1。这个组件很好的解决了Flutter引入的安装包、图片缓存、图片复用等问题。但是图片最大的问题:内存问题,并没有得到解决。内存优化—-FXTexImage_V2为了用户体验,通常会有连续push若干个界面的场景(比如闲鱼的详情页,点击底部的推荐列表,可以一直往下push新的详情页),这种场景下,每一个界面都有大量的图片展示。所以在引入flutter以后,闲鱼在iPhone 6P等机型上通常只能push10个左右详情页就挂了。在考虑到在显示过程中,真正用户可见的页面,其实只有当前栈顶的两个页面,基于这个特征我们就做了优化逻辑:1:在push详情页过程中,我们只保留了当前展示页和当前页的前一页的图片资源,而之前的资源全部都做了释放(只是图片资源的释放,整个页面还有页面中的其他元素还是做了保留)。2:为了做到用户无感知,我们在pop过程中,会预先去加载当前界面下一个界面的图片资源。通过这种方式,理论上我们可以释放掉不可见的资源,从而保证在持续Push界面过程中内存缓慢增长,但是实践过程中发现内存仍然持续增长。经过排查,我们发现flutter 1.0版本以及0.8.2版本里,SurfaceTextureRegistry提供了release方法,这里将会把创建的SurfaceTexture进行释放。然而测试过程中发现,单单对SurfaceTexture释放,并没有完全释放内存,当反复创建对象时仍然会闪退。为此,我们在AndroidExternalTextureGL的析构函数中增加了纹理的释放glDeleteTextures逻辑。然而,AndroidExternalTextureGL的析构是在flutter的GPU线程调用的,而external_texture的release方法通常是在主线程,也就是PlatForm线程调用的。不同线程调用的问题就是会导致一个诡异的问题:推测是不同线程释放的逻辑影响了GL环境,导致文字渲染出了问题。所以,为了解决该问题,我们删除了SurfaceTextureRegistry的release方法里面SufaceTexture的释放逻辑,并且将SurfaceTexture的释放,放到AndroidExternalTextureGL析构阶段,通过Jni调用java方法实现资源释放。AndroidExternalTextureGL::~AndroidExternalTextureGL(){ if (state_ == AttachmentState::attached) { Detach(); if (texture_name_ != 0) { glDeleteTextures(1, &texture_name_); texture_name_ = 0; } } Release(); state_ = AttachmentState::detached;}void AndroidExternalTextureGL::Release() { JNIEnv* env = fml::jni::AttachCurrentThread(); fml::jni::ScopedJavaLocalRef<jobject> surfaceTexture = surface_texture_.get(env); if (!surfaceTexture.is_null()) { SurfaceTextureRelease(env, surfaceTexture.obj()); }}void SurfaceTextureRelease(JNIEnv* env, jobject obj) { env->CallVoidMethod(obj, g_release_method); FML_CHECK(CheckException(env));} g_release_method = env->GetMethodID(g_surface_texture_class->obj(), “release”, “()V”);CPU优化—-FXTexImage_V3通过外界纹理渲染图片+不可见页面资源释放,我们解决了上述提出的一系列问题,但是又引入了新的问题:CPU偏高,滑动帧率偏低。通过测试,在详情页滑动过程中,IOS和Android的CPU都比Flutter原生组件高10%以上,这个显然无法应用。经过排查,发现CPU高的原因是:IOS端: iOS的IOSExternalTextureGL模型是一个拉数据的模型,native端register一个CVPixelBuffer的生产者,当需要绘制时,都会调用一次这个生产者的copyPixelbuffer方法去拉一次数据。然后将拉到的CVPixelBuffer对象转换成GPU Texture。这里每一次转换都换造成CPU较大开销。并且这种拉数据的机制就要求这个生产者的必须一直保留着这个CVPixelBuffer对象(否则界面重刷以后,图片区域就显示白屏)。Android端: android 的数据存储在SurfaceTexture中。每一次external_texture layer需要绘制时候都会从SurfaceTexture中去update 数据到纹理中,由于SurfaceTexture使用基于EGLImage共享内存,所以虽然没有双份内存的问题,但是每一次update 都会带来较大的CPU开销。在之前外接纹理的文章中,我们提出了一种新的基于共享上下文的外接纹理方案。并在我们视频的拍摄和编辑中得到了很好的应用。该方案当初提出来,是为了解决视频数据从CPU -> GPU -> CPU -> GPU 输送的问题而提出来的。但是在图片这个场景下, 新的外接纹理方案下,一张图片在native端加载完成以后,立刻被转换成一个OpenGL的Texture,然后图片的资源马上被释放。当界面刷新时,对于同一张图片的重新渲染,IOSExternalTextureGL不需要再去做将数据(CVPixelBuffer或者SurfaceTexture)转换到Texture的逻辑,而是直接使用之前创建好的Texture。经过这一步优化,我们很好的限制了iOS的CPU和内存,Android的CPU。通过测试对比,V3版本的图片组件,相比于Flutter原生图片组件,在详情页正常滑动操作过程中,平均CPU高出3%左右,虽然仍差于原生组件,单相对是可以接受的。结果内存: 基于新图片组件,我们很好的限制住了连续push 下的内存增长速度,顺利的将iPhone 6P上的详情页push 最大数量从10个增加到了30个以上。在同一线上版本中,我们通过控制ABTest开关,测试新的图片渲染方案和Flutter自带图片组件方案的Abort率,发现新图片组件下闲鱼的Abort率降低20%。安装包: 新组件下,所有的资源组件与原来native资源共用,所有flutter期间新引入的资源出了gif图,全部删除,减少安装包900k+。并且后续可以不用继续新增。寻址策略: 复用native图片组件,基于阿里系自己的图片下载组件,这样可以做到随着集团组件升级版本,兼容版本过程中各种新的寻址方式和图片格式。图片复用:复用native图片组件,当图片地址命中缓存,可直接缓存加载,尺寸不一致时可以预先返回缓存图同时加载大图,这样大大增强详情页大图预览的浏览体验。遗留问题图片组件已经在闲鱼上全量部署,然而还是有一些问题没有得到很好的解决,上文提到过CPU比原生图片组件高3%左右,虽然用户没有感官体验,但是还是有优化空间。还有就是Flutter针对ExternalTexture的纹理渲染时没有开启抗锯齿,导致小图在大区域渲染时比原生组件效果要差。这里还需要继续排查原因。最后,FXTexImage组件还在持续优化中,当解决上述遗留问题以后便会在Github上开源。本文作者:闲鱼技术-炉军阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

April 8, 2019 · 1 min · jiezi

一文掌握 Linux 性能分析之内存篇

本文首发于我的公众号 CloudDeveloper(ID: cloud_dev),专注于干货分享,号内有大量书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫。前面我们已经学习了 CPU 篇,这篇来看下内存篇。01 内存信息同样在分析内存之前,我们得知到怎么查看系统内存信息,有以下几种方法。1.1 /proc/meminfo这个文件记录着比较详细的内存配置信息,使用 cat /proc/meminfo 查看。我们比较关心的是下面几个字段:MemTotal:系统总内存,由于 BIOS、内核等会占用一些内存,所以这里和配置声称的内存会有一些出入,比如我这里配置有 2G,但其实只有 1.95G 可用。MemFree:系统空闲内存。MemAvailable:应用程序可用内存。有人会比较奇怪和 MemFree 的区别,可以从两个层面来区分,MemFree 是系统层面的,而 MemAvailable 是应用程序层面的。系统中有些内存虽然被使用了但是有一部分是可以回收的,比如 Buffers、Cached 及 Slab 这些内存,这部分可以回收的内存加上 MemFree 才是 MemAvailable 的内存值,这是内核通过特定算法算出来的,是一个估算值。Buffers:缓冲区内存Cached:缓存上面信息没有 MemUsed 的值,虽然可以用现有的值大致估算出来,但是我们想一步到位,就用下面的 free 命令。1.2 free这个命令估计用的人就多了(我一般都是用这个命令)。这里存在一个计算公式:MemTotal = used + free + buff/cache(单位 K)几个字段和上面 /proc/meminfo 的字段是对应的。还有个 shared 字段,这个是多进程的共享内存空间,不常用。我们注意到 free 很小,buff/cache 却很大,这是 Linux 的内存设计决定的,Linux 的想法是内存闲着反正也是闲着,不如拿出来做系统缓存和缓冲区,提高数据读写的速率。但是当系统内存不足时,buff/cache 会让出部分来,非常灵活的操作。要看比较直观的值,可以加 -h 参数:1.3 dmidecode同样可以使用这个命令,对于内存,可以使用 dmidecode -t memory 查看:1.4 vmstat这个命令也是非常常用了。但对于内存,显示信息有限。它更多是用于进行系统全局分析和 CPU 分析。详细可以看 CPU 分析一文。02 进程内存使用情况分析最常用的两个命令 ps 和 top,虽然很简单的两个命令,但还是有不少学问的。2.1 top/htoptop 命令运行时默认是按照 CPU 利用率进行排序的,如果要按照内存排序,该怎么操作呢?两种方法,一种直接按 “M”(相应的按 “P” 是 CPU),另外一种是在键入 top 之后,按下 “F”,然后选择要排序的字段,再按下 “s” 确认即可。可以看到,我按照 “%MEM” 排序的结果。这个结果对于查看系统占用内存较多的哪些进程是比较有用的。然后这里我们会重点关注几个地方,上面横排区,和前面几个命令一样可以查看系统内存信息,中间标注的横条部分,和内存相关的有三个字段:VIRT、RES、SHR。VIRT:virtual memory usage,进程占用的虚拟内存大小。RES:resident memory usage,进程常驻内存大小,也就是实际内存占用情况,一般我们看进程占用了多少内存,就是看的这个值。SHR:shared memory,共享内存大小,不常用。2.2 psps 同样可以查看进程占用内存情况,一般常用来查看 Top n 进程占用内存情况,如:ps aux –sort=rss | head -n,表示按 rss 排序,取 Top n。这里也关注三个字段:%MEM:进程使用物理内存所占百分比。VSZ:进程使用虚拟内存大小。RSS:进程使用物理内存大小,我们会重点关注这个值。2.3 pmap这个命令用于查看进程的内存映像信息,能够查看进程在哪些地方用了多少内存。 常用 pmap -x pid 来查看。可以看到该进程内存被哪些库、哪些文件所占用,据此我们定位程序对内存的使用。几个字段介绍一下:Address:占用内存的文件的内存起始地址。Kbytes:占用内存的字节数。RSS:实际占用内存大小。Dirty:脏页大小。Mapping:占用内存的文件,[anon] 为已分配的内存,[stack] 为程序堆栈最后的 total 为统计的总值。我们可以使用 pmap -x pid | tail -1 这样只显示最后一行,循环显示最后一行,达到监控该进程的目的。使用:while true; do pmap -x pid | tail -1; sleep 1; doneOK,以上工具都是 Linux 自带的,当然还有很多高阶的工具,比如 atop、memstat 等等,对于内存泄漏有一个比较常用的检测工具 Valgrind,更多干货可以关注我的公众号。通过以上手段,我们基本上就能定位内存问题所在了,究竟是内存太小,还是进程占用内存太多,有哪些进程占用较多,这些进程又究竟有哪些地方占用较多,这些问题通过以上方法都能解决。最后简单总结下,以上不少工具可能有人会犯选择困难症了。对于我来说,查看系统内存用 free -h,分析进程内存占用用 ps 或者 top(首选 ps),深入分析选择 pmap,就酱。参考:Linux下查看内存使用情况的多种方法 http://stor.51cto.com/art/201…我的公众号 CloudDeveloper(ID: cloud_dev),号内有大量书籍和视频资源,后台回复「1024」即可领取,分享的内容包括但不限于云计算虚拟化、容器、OpenStack、K8S、雾计算、网络、工具、SDN、OVS、DPDK、Linux、Go、Python、C/C++编程技术等内容,欢迎大家关注。 ...

March 18, 2019 · 1 min · jiezi

一文掌握 Linux 性能分析之内存篇

本文首发于我的公众号 CloudDeveloper(ID: cloud_dev),专注于干货分享,号内有大量书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫。前面我们已经学习了 CPU 篇,这篇来看下内存篇。01 内存信息同样在分析内存之前,我们得知到怎么查看系统内存信息,有以下几种方法。1.1 /proc/meminfo这个文件记录着比较详细的内存配置信息,使用 cat /proc/meminfo 查看。我们比较关心的是下面几个字段:MemTotal:系统总内存,由于 BIOS、内核等会占用一些内存,所以这里和配置声称的内存会有一些出入,比如我这里配置有 2G,但其实只有 1.95G 可用。MemFree:系统空闲内存。MemAvailable:应用程序可用内存。有人会比较奇怪和 MemFree 的区别,可以从两个层面来区分,MemFree 是系统层面的,而 MemAvailable 是应用程序层面的。系统中有些内存虽然被使用了但是有一部分是可以回收的,比如 Buffers、Cached 及 Slab 这些内存,这部分可以回收的内存加上 MemFree 才是 MemAvailable 的内存值,这是内核通过特定算法算出来的,是一个估算值。Buffers:缓冲区内存Cached:缓存上面信息没有 MemUsed 的值,虽然可以用现有的值大致估算出来,但是我们想一步到位,就用下面的 free 命令。1.2 free这个命令估计用的人就多了(我一般都是用这个命令)。这里存在一个计算公式:MemTotal = used + free + buff/cache(单位 K)几个字段和上面 /proc/meminfo 的字段是对应的。还有个 shared 字段,这个是多进程的共享内存空间,不常用。我们注意到 free 很小,buff/cache 却很大,这是 Linux 的内存设计决定的,Linux 的想法是内存闲着反正也是闲着,不如拿出来做系统缓存和缓冲区,提高数据读写的速率。但是当系统内存不足时,buff/cache 会让出部分来,非常灵活的操作。要看比较直观的值,可以加 -h 参数:1.3 dmidecode同样可以使用这个命令,对于内存,可以使用 dmidecode -t memory 查看:1.4 vmstat这个命令也是非常常用了。但对于内存,显示信息有限。它更多是用于进行系统全局分析和 CPU 分析。详细可以看 CPU 分析一文。02 进程内存使用情况分析最常用的两个命令 ps 和 top,虽然很简单的两个命令,但还是有不少学问的。2.1 top/htoptop 命令运行时默认是按照 CPU 利用率进行排序的,如果要按照内存排序,该怎么操作呢?两种方法,一种直接按 “M”(相应的按 “P” 是 CPU),另外一种是在键入 top 之后,按下 “F”,然后选择要排序的字段,再按下 “s” 确认即可。可以看到,我按照 “%MEM” 排序的结果。这个结果对于查看系统占用内存较多的哪些进程是比较有用的。然后这里我们会重点关注几个地方,上面横排区,和前面几个命令一样可以查看系统内存信息,中间标注的横条部分,和内存相关的有三个字段:VIRT、RES、SHR。VIRT:virtual memory usage,进程占用的虚拟内存大小。RES:resident memory usage,进程常驻内存大小,也就是实际内存占用情况,一般我们看进程占用了多少内存,就是看的这个值。SHR:shared memory,共享内存大小,不常用。2.2 psps 同样可以查看进程占用内存情况,一般常用来查看 Top n 进程占用内存情况,如:ps aux –sort=rss | head -n,表示按 rss 排序,取 Top n。这里也关注三个字段:%MEM:进程使用物理内存所占百分比。VSZ:进程使用虚拟内存大小。RSS:进程使用物理内存大小,我们会重点关注这个值。2.3 pmap这个命令用于查看进程的内存映像信息,能够查看进程在哪些地方用了多少内存。 常用 pmap -x pid 来查看。可以看到该进程内存被哪些库、哪些文件所占用,据此我们定位程序对内存的使用。几个字段介绍一下:Address:占用内存的文件的内存起始地址。Kbytes:占用内存的字节数。RSS:实际占用内存大小。Dirty:脏页大小。Mapping:占用内存的文件,[anon] 为已分配的内存,[stack] 为程序堆栈最后的 total 为统计的总值。我们可以使用 pmap -x pid | tail -1 这样只显示最后一行,循环显示最后一行,达到监控该进程的目的。使用:while true; do pmap -x pid | tail -1; sleep 1; doneOK,以上工具都是 Linux 自带的,当然还有很多高阶的工具,比如 atop、memstat 等等,对于内存泄漏有一个比较常用的检测工具 Valgrind,更多干货可以关注我的公众号。通过以上手段,我们基本上就能定位内存问题所在了,究竟是内存太小,还是进程占用内存太多,有哪些进程占用较多,这些进程又究竟有哪些地方占用较多,这些问题通过以上方法都能解决。最后简单总结下,以上不少工具可能有人会犯选择困难症了。对于我来说,查看系统内存用 free -h,分析进程内存占用用 ps 或者 top(首选 ps),深入分析选择 pmap,就酱。参考:Linux下查看内存使用情况的多种方法 http://stor.51cto.com/art/201…我的公众号 CloudDeveloper(ID: cloud_dev),号内有大量书籍和视频资源,后台回复「1024」即可领取,分享的内容包括但不限于云计算虚拟化、容器、OpenStack、K8S、雾计算、网络、工具、SDN、OVS、DPDK、Linux、Go、Python、C/C++编程技术等内容,欢迎大家关注。 ...

March 13, 2019 · 1 min · jiezi

mariadb 内存占用优化

本文由云+社区发表作者:工程师小熊摘要:我们在使用mariadb的时候发现有时候不能启动起来,在使用过程中mariadb占用的内存很大,在这里学习下mariadb与内存相关的配置项,对mariadb进行调优。查询最高内存占用使用以下命令可以知道mysql的配置使用多少 RAMSELECT ( @@key_buffer_size+ @@query_cache_size+ @@innodb_buffer_pool_size+ @@innodb_additional_mem_pool_size+ @@innodb_log_buffer_size+ @@max_connections * ( @@read_buffer_size+ @@read_rnd_buffer_size+ @@sort_buffer_size+ @@join_buffer_size+ @@binlog_cache_size+ @@thread_stack+ @@tmp_table_size)) / (1024 * 1024 * 1024) AS MAX_MEMORY_GB;可以使用mysql计算器来计算内存使用下面是理论,可以直接到推荐配置如何调整配置key_buffer_size(MyISAM索引用)指定索引缓冲区的大小,它决定索引处理的速度,尤其是索引读的速度。为了最小化磁盘的 I/O , MyISAM 存储引擎的表使用键高速缓存来缓存索引,这个键高速缓存的大小则通过 key-buffer-size 参数来设置。如果应用系统中使用的表以 MyISAM 存储引擎为主,则应该适当增加该参数的值,以便尽可能的缓存索引,提高访问的速度。怎么设show global status like ‘key_read%’;+————————+————-+| Variable_name | Value |+————————+————-+| Key_read_requests | 27813678764 || Key_reads | 6798830 |——————— key_buffer_size通过检查状态值Key_read_requests和Key_reads,可以知道key_buffer_size设置是否合理。比例key_reads / key_read_requests应该尽可能的低,至少是1:100,1:1000更好。show global status like ‘%created_tmp_disk_tables%’;key_buffer_size只对MyISAM表起作用。即使你不使用MyISAM表,但是内部的临时磁盘表是MyISAM表,也要使用该值。可以使用检查状态值created_tmp_disk_tables得知详情。对于1G内存的机器,如果不使用MyISAM表,推荐值是16M(8-64M)另一个参考如下show global status like ‘key_blocks_u%’;+————————+————-+| Variable_name | Value |+————————+————-+| Key_blocks_unused | 0 || Key_blocks_used | 413543 |+————————+————-+Key_blocks_unused表示未使用的缓存簇(blocks)数,Key_blocks_used表示曾经用到的最大的blocks数,比如这台服务器,所有的缓存都用到了,要么增加key_buffer_size,要么就是过渡索引了,把缓存占满了。比较理想的设置:可以根据此工式来动态的调整Key_blocks_used / (Key_blocks_unused + Key_blocks_used) * 100% ≈ 80%show engines;查询存储引擎innodb_buffer_pool_size (innodb索引用)这个参数和MyISAM的key_buffer_size有相似之处,但也是有差别的。这个参数主要缓存innodb表的索引,数据,插入数据时的缓冲。为Innodb加速优化首要参数。 该参数分配内存的原则:这个参数默认分配只有8M,可以说是非常小的一个值。如果是专用的DB服务器,且以InnoDB引擎为主的场景,通常可设置物理内存的50%,这个参数不能动态更改,所以分配需多考虑。分配过大,会使Swap占用过多,致使Mysql的查询特慢。如果是非专用DB服务器,可以先尝试设置成内存的1/4,如果有问题再调整query_cache_size(查询缓存)缓存机制简单的说就是缓存sql文本及查询结果,如果运行相同的sql,服务器直接从缓存中取到结果,而不需要再去解析和执行sql。如果表更改了,那么使用这个表的所有缓冲查询将不再有效,查询缓存值的相关条目被清空。更改指的是表中任何数据或是结构的改变,包括INSERT、UPDATE、DELETE、TRUNCATE、ALTER TABLE、DROP TABLE或DROP DATABASE等,也包括那些映射到改变了的表的使用MERGE表的查询。显然,这对于频繁更新的表,查询缓存是不适合的,而对于一些不常改变数据且有大量相同sql查询的表,查询缓存会节约很大的性能。注意:如果你查询的表更新比较频繁,而且很少有相同的查询,最好不要使用查询缓存。因为这样会消耗很大的系统性能还没有任何的效果要不要打开?先设置成这样跑一段时间query_cache_size=128M query_cache_type=1 看看命中结果来进行进一步的判断mysql> show status like ‘%Qcache%’;+————————-+———–+| Variable_name | Value |+————————-+———–+| Qcache_free_blocks | 669 || Qcache_free_memory | 132519160 || Qcache_hits | 1158 || Qcache_inserts | 284824 || Qcache_lowmem_prunes | 2741 || Qcache_not_cached | 1755767 || Qcache_queries_in_cache | 579 || Qcache_total_blocks | 1853 |+————————-+———–+8 rows in set (0.00 sec)Qcache_free_blocks:表示查询缓存中目前还有多少剩余的blocks,如果该值显示较大,则说明查询缓存中的内存碎片过多了,可能在一定的时间进行整理。Qcache_free_memory:查询缓存的内存大小,通过这个参数可以很清晰的知道当前系统的查询内存是否够用,是多了,还是不够用,DBA可以根据实际情况做出调整。Qcache_hits:表示有多少次命中缓存。我们主要可以通过该值来验证我们的查询缓存的效果。数字越大,缓存效果越理想。Qcache_inserts: 表示多少次未命中然后插入,意思是新来的SQL请求在缓存中未找到,不得不执行查询处理,执行查询处理后把结果insert到查询缓存中。这样的情况的次数,次数越多,表示查询缓存应用到的比较少,效果也就不理想。当然系统刚启动后,查询缓存是空的,这很正常。Qcache_lowmem_prunes:该参数记录有多少条查询因为内存不足而被移除出查询缓存。通过这个值,用户可以适当的调整缓存大小。Qcache_not_cached: 表示因为query_cache_type的设置而没有被缓存的查询数量。Qcache_queries_in_cache:当前缓存中缓存的查询数量。Qcache_total_blocks:当前缓存的block数量。我们可以看到现网命中1158,未缓存的有1755767次,说明我们这个系统命中的太少了,表变动比较多,不什么开启这个功能涉及参数query_cache_limit:允许 Cache 的单条 Query 结果集的最大容量,默认是1MB,超过此参数设置的 Query 结果集将不会被 Cachequery_cache_min_res_unit:设置 Query Cache 中每次分配内存的最小空间大小,也就是每个 Query 的 Cache 最小占用的内存空间大小query_cache_size:设置 Query Cache 所使用的内存大小,默认值为0,大小必须是1024的整数倍,如果不是整数倍,MySQL 会自动调整降低最小量以达到1024的倍数query_cache_type:控制 Query Cache 功能的开关,可以设置为0(OFF),1(ON)和2(DEMAND)三种,意义分别如下: 0(OFF):关闭 Query Cache 功能,任何情况下都不会使用 Query Cache 1(ON):开启 Query Cache 功能,但是当 SELECT 语句中使用的 SQL_NO_CACHE 提示后,将不使用Query Cache 2(DEMAND):开启 Query Cache 功能,但是只有当 SELECT 语句中使用了 SQL_CACHE 提示后,才使用 Query Cachequery_cache_wlock_invalidate:控制当有写锁定发生在表上的时刻是否先失效该表相关的 Query Cache,如果设置为 1(TRUE),则在写锁定的同时将失效该表相关的所有 Query Cache,如果设置为0(FALSE)则在锁定时刻仍然允许读取该表相关的 Query Cache。innodb_additional_mem_pool_size(InnoDB内部目录大小)InnoDB 字典信息缓存主要用来存放 InnoDB 存储引擎的字典信息以及一些 internal 的共享数据结构信息,也就是存放Innodb的内部目录,所以其大小也与系统中所使用的 InnoDB 存储引擎表的数量有较大关系。这个值不用分配太大,通常设置16M够用了,默认8M,如果设置的内存大小不够,InnoDB 会自动申请更多的内存,并在 MySQL 的 Error Log 中记录警告信息。innodb_log_buffer_size (日志缓冲)表示InnoDB写入到磁盘上的日志文件时使用的缓冲区的字节数,默认值为16M。一个大的日志缓冲区允许大量的事务在提交之前不用写日志到磁盘,所以如果有更新,插入或删除许多行的事务,则使日志缓冲区更大一些可以节省磁盘IO通常最大设为64M足够max_connections (最大并发连接)MySQL的max_connections参数用来设置最大连接(用户)数。每个连接MySQL的用户均算作一个连接,max_connections的默认值为100。这个参数实际起作用的最大值(实际最大可连接数)为16384,即该参数最大值不能超过16384,即使超过也以16384为准;增加max_connections参数的值,不会占用太多系统资源。系统资源(CPU、内存)的占用主要取决于查询的密度、效率等;该参数设置过小的最明显特征是出现”Too many connections”错误mysql> show variables like ‘%max_connect%’;+———————–+——-+| Variable_name | Value |+———————–+——-+| extra_max_connections | 1 || max_connect_errors | 100 || max_connections | 2048 |+———————–+——-+3 rows in set (0.00 sec)mysql> show status like ‘Threads%’;+——————-+———+| Variable_name | Value |+——————-+———+| Threads_cached | 0 || Threads_connected | 1 || Threads_created | 9626717 || Threads_running | 1 |+——————-+———+4 rows in set (0.00 sec)可以看到此时的并发数也就是Threads_connected=1,还远远达不到2048mysql> show variables like ‘open_files_limit’;+——————+——-+| Variable_name | Value |+——————+——-+| open_files_limit | 65535 |+——————+——-+1 row in set (0.00 sec)max_connections 还取决于操作系统对单进程允许打开最大文件数的限制也就是说如果操作系统限制单个进程最大可以打开100个文件那么 max_connections 设置为200也没什么用MySQL 的 open_files_limit 参数值是在MySQL启动时记录的操作系统对单进程打开最大文件数限制的值可以使用 show variables like ‘open_files_limit’; 查看 open_files_limit 值ulimit -n65535或者直接在 Linux 下通过ulimit -n命令查看操作系统对单进程打开最大文件数限制 ( 默认为1024 )connection级内存参数(线程独享)connection级参数,是在每个connection第一次需要使用这个buffer的时候,一次性分配设置的内存。排序性能mysql对于排序,使用了两个变量来控制sort_buffer_size和 max_length_for_sort_data, 不象oracle使用SGA控制. 这种方式的缺点是要单独控制,容易出现排序性能问题.mysql> SHOW GLOBAL STATUS like ‘%sort%’;+—————————+——–+| Variable_name | Value |+—————————+——–+| Sort_merge_passes | 0 || Sort_priority_queue_sorts | 1409 || Sort_range | 0 || Sort_rows | 843479 || Sort_scan | 13053 |+—————————+——–+5 rows in set (0.00 sec)如果发现Sort_merge_passes的值比较大,你可以考虑增加sort_buffer_size 来加速ORDER BY 或者GROUP BY 操作,不能通过查询或者索引优化的。我们这为0,那就没必要设置那么大。读取缓存read_buffer_size = 128K(默认128K)为需要全表扫描的MYISAM数据表线程指定缓存read_rnd_buffer_size = 4M:(默认256K)首先,该变量可以被任何存储引擎使用,当从一个已经排序的键值表中读取行时,会先从该缓冲区中获取而不再从磁盘上获取。大事务binlogmysql> show global status like ‘binlog_cache%’;+———————–+———-+| Variable_name | Value |+———————–+———-+| Binlog_cache_disk_use | 220840 || Binlog_cache_use | 67604667 |+———————–+———-+2 rows in set (0.00 sec)Binlog_cache_disk_use表示因为我们binlog_cache_size设计的内存不足导致缓存二进制日志用到了临时文件的次数Binlog_cache_use 表示 用binlog_cache_size缓存的次数当对应的Binlog_cache_disk_use 值比较大的时候 我们可以考虑适当的调高 binlog_cache_size 对应的值如上图,现网是32K,我们加到64Kjoin语句内存影响如果应用中,很少出现join语句,则可以不用太在乎join_buffer_size参数的设置大小。如果join语句不是很少的话,个人建议可以适当增大join_buffer_size到1MB左右,如果内存充足可以设置为2MB。线程内存影响Thread_stack:每个连接线程被创建时,MySQL给它分配的内存大小。当MySQL创建一个新的连接线程时,需要给它分配一定大小的内存堆栈空间,以便存放客户端的请求的Query及自身的各种状态和处理信息。mysql> show status like ‘%threads%’;+————————-+———+| Variable_name | Value |+————————-+———+| Delayed_insert_threads | 0 || Slow_launch_threads | 0 || Threadpool_idle_threads | 0 || Threadpool_threads | 0 || Threads_cached | 0 || Threads_connected | 1 || Threads_created | 9649301 || Threads_running | 1 |+————————-+———+8 rows in set (0.00 sec)mysql> show status like ‘connections’;+—————+———+| Variable_name | Value |+—————+———+| Connections | 9649311 |+—————+———+1 row in set (0.00 sec)如上:系统启动到现在共接受到客户端的连接9649311次,共创建了9649301个连接线程,当前有1个连接线程处于和客户端连接的状态。而在Thread Cache池中共缓存了0个连接线程(Threads_cached)。Thread Cache 命中率:Thread_Cache_Hit = (Connections - Threads_created) / Connections * 100%;一般在系统稳定运行一段时间后,Thread Cache命中率应该保持在90%左右才算正常。内存临时表tmp_table_size 控制内存临时表的最大值,超过限值后就往硬盘写,写的位置由变量 tmpdir 决定 max_heap_table_size 用户可以创建的内存表(memory table)的大小.这个值用来计算内存表的最大行数值。Order By 或者Group By操作多的话,加大这两个值,默认16Mmysql> show status like ‘Created_tmp_%’;+————————-+——-+| Variable_name | Value |+————————-+——-+| Created_tmp_disk_tables | 0 || Created_tmp_files | 626 || Created_tmp_tables | 3 |+————————-+——-+3 rows in set (0.00 sec)如上图,写入硬盘的为0,3次中间表,说明我们的默认值足够用了mariadb 推荐配置注意这里只推荐innodb引擎内存配置只关注有注释的行[mysqld]datadir=/var/lib/mysqlsocket=/var/lib/mysql/mysql.sockdefault-storage-engine=INNODBcharacter-set-server=utf8collation-server=utf8_general_ciuser=mysqlsymbolic-links=0# global settingstable_cache=65535table_definition_cache=65535max_allowed_packet=4Mnet_buffer_length=1Mbulk_insert_buffer_size=16Mquery_cache_type=0 #是否使用查询缓冲,0关闭query_cache_size=0 #0关闭,因为改表操作多,命中低,开启消耗cpu# sharedkey_buffer_size=8M #保持8M MyISAM索引用innodb_buffer_pool_size=4G #DB专用mem50%,非DB专用mem15%到25%myisam_sort_buffer_size=32Mmax_heap_table_size=16M #最大中间表大小tmp_table_size=16M #中间表大小# per-threadsort_buffer_size=256K #加速排序缓存大小read_buffer_size=128k #为需要全表扫描的MYISAM数据表线程指定缓存read_rnd_buffer_size=4M #已排序的表读取时缓存,如果比较大内存就到6Mjoin_buffer_size=1M #join语句多时加大,1-2Mthread_stack=256k #线程空间,256K or 512Kbinlog_cache_size=64K #大事务binlog# big-tablesinnodb_file_per_table = 1skip-external-lockingmax_connections=2048 #最大连接数skip-name-resolve# slow_query_logslow_query_log_file = /var/log/mysql-slow.loglong_query_time = 30group_concat_max_len=65536# according to tuning-primer.shthread_cache_size = 8thread_concurrency = 16# set variablesconcurrent_insert=2运行时修改使用以下命令来修改变量set global {要改的key} = {值}; (立即生效重启后失效)set @@{要改的key} = {值}; (立即生效重启后失效)set @@global.{要改的key} = {值}; (立即生效重启后失效)试验mysql> set @@global.innodb_buffer_pool_size=4294967296;ERROR 1238 (HY000): Variable ‘innodb_buffer_pool_size’ is a read only variablemysql> set @@global.thread_stack=262144;ERROR 1238 (HY000): Variable ’thread_stack’ is a read only variablemysql> set @@global.binlog_cache_size=65536;Query OK, 0 rows affected (0.00 sec)mysql> set @@join_buffer_size=1048576;Query OK, 0 rows affected (0.00 sec)mysql> set @@read_rnd_buffer_size=4194304;Query OK, 0 rows affected (0.00 sec)mysql> set @@sort_buffer_size=262144;Query OK, 0 rows affected (0.00 sec)mysql> set @@read_buffer_size=131072;Query OK, 0 rows affected (0.00 sec)mysql> set global key_buffer_size=8388608;Query OK, 0 rows affected (0.39 sec)我们可以看到innodb_buffer_pool_size和thread_stack报错了,他们只能改配置文件,在运行时是只读的。 以下直接复制使用set @@global.binlog_cache_size=65536;set @@join_buffer_size=1048576;set @@read_rnd_buffer_size=4194304;set @@sort_buffer_size=262144;set @@read_buffer_size=131072;set global key_buffer_size=8388608;引用记一次Mysql占用内存过高的优化过程mysql 优化技巧心得一(key_buffer_size设置)mysql内存计算mysql计算器mariadb官网此文已由腾讯云+社区在各渠道发布获取更多新鲜技术干货,可以关注我们腾讯云技术社区-云加社区官方号及知乎机构号 ...

January 23, 2019 · 3 min · jiezi

内存性能的正确解读

一台服务器,不管是物理机还是虚拟机,必不可少的就是内存,内存的性能又是如何来衡量呢。1. 内存与缓存现在比较新的CPU一般都有三级缓存,L1 Cache(32KB-256KB),L2 Cache(128KB-2MB),L3 Cache(1M-32M)。缓存逐渐变大,CPU在取数据的时候,优先从缓存去取数据,取不到才去内存取数据。2. 内存与时延显然,越靠近CPU,取数据的速度越块,通过LMBench进行了读数延迟的测试。从上图可以看出:Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz 这款CPU的L1D Cache,L1I Cache为32KB,而L2 Cache为1M,L3为32M;在对应的Cache中,时延是稳定的;不同缓存的时延呈现指数级增长;所以我们在写业务代码的时候,如果想要更快地提高效率,那么使得计算更加贴近CPU则可以获取更好的性能。但是从上图也可以看出,内存的时延都是纳秒为单位,而实际业务中都是毫秒为单位,优化的重点应该是那些以毫秒为单位的运算,而内存时延优化这块则是长尾部分。3. 内存带宽内存时延与缓存其实可谓是紧密相关,不理解透彻了,则可能测的是缓存时延。同样测试内存带宽,如果不是正确的测试,则测的是缓存带宽了。为了了解内存带宽,有必要去了解下内存与CPU的架构,早期的CPU与内存的架构还需要经过北桥总线,现在CPU与内存直接已经不需要北桥,直接通过CPU的内存控制器(IMC)进行内存读取操作:那对应的内存带宽是怎样的呢?测试内存带宽有很多很多工具,linux下一般通过stream进行测试。简单介绍下stream的算法:stream算法的原理从上图可以看出非常简单:某个内存块之间的数据读取出来,经过简单的运算放入另一个内存块。那所谓的内存带宽:内存带宽=搬运的内存大小/耗时。通过整机合理的测试,可以测出来内存控制器的带宽。下图是某云产品的内存带宽数据:————————————————————-Function Best Rate MB/s Avg time Min time Max timeCopy: 128728.5 0.134157 0.133458 0.136076Scale: 128656.4 0.134349 0.133533 0.137638Add: 144763.0 0.178851 0.178014 0.181158Triad: 144779.8 0.178717 0.177993 0.180214————————————————————-内存带宽的重要性自然不言而喻,这意味着操作内存的最大数据吞吐量。但是正确合理的测试非常重要,有几个注意事项需要关注:内存数组大小的设置,必须要远大于L3 Cache的大小,否则就是测试缓存的吞吐性能;CPU数目很有关系,一般来说,一两个核的计算能力,是远远到不了内存带宽的,整机的CPU全部运行起来,才可以有效地测试内存带宽。当然跑单核的stream测试也有意义,可以测试内存的延时。4. 其他内存与NUMA的关系:开启NUMA,可以有效地提供内存的吞吐性能,降低内存时延。stream算法的编译方法选择:通过icc编译,可以有效地提供内存带宽性能分。原因是Intel优化了CPU的指令,通过指令向量化和指令Prefetch操作,加速了数据的读写操作以及指令操作。当然其他C代码都可以通过icc编译的方法,提供指令的效率。本文作者:ecs西邪阅读原文本文为云栖社区原创内容,未经允许不得转载。

December 13, 2018 · 1 min · jiezi

内存池原理大揭秘

欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~本文由[amc](https://cloud.tencent.com/dev…云+社区专栏在 C 语言的动态申请内存技术中,相比起 alloc/free 系统调用,内存池(memory pool)是与现在系统中请求一大片连续的内存空间,然后在运行时根据实际需要分配出去的技术。使用内存池的优点有:速度远比 malloc/free 快,因为减少了系统调用的次数,特别是频繁申请/释放内存块的情况避免了频繁申请/释放内存之后,系统的大量内存碎片节省空间分类根据分配出去的内存大小,内存池可以分为两类:Fixed-size Allocation每次分配出去的内存单元(称为 unit 或者 cell)的大小为程序预先定义的值。释放内存块时,则只需要简单地挂回内存池链表中即可。又称为 “固定尺寸缓冲池”。常规的做法是:将不同 unit size 的内存池整合在一起,以满足不同内存块大小的使用需求Variable-size allocation不分配固定长度,内存的分配只是在一大块空闲的内存上滑动。优点是分配效率很高,缺点是成批地回收内存,因为释放的内存无法直接重复利用。使用这种需要合理规划每块内存的管理区域,所以又叫做 “基于区域的” 内存管理。使用这种做法的分配器,举例有 Apache Portable Runtime 中的 apr_pool 工具。本文不讨论这种内存池。原理和结构概念和数据结构定长内存池有一些基本和必要的概念,需要定义在内存池的结构数据中。以下命名方式使用变体的匈牙利命名法,比如 nNext,n表示变量类型为整形。类似地,p表示指针。Memory Unit每次程序调用 MemPool_Alloc 获取一个内存区域后,会获得一块连续的内存区域。管理一个这样的内存区域的单元就成为内存单元 unit,有时也称作 chunk。每个 unit 需要包含以下数据:nNext:整型数据,表示下一个可供分配的 unit 的标识号。功能请参见后问pData[]:实际的内存区域,其大小在创建时由调用方指定Memory Block一个内存块,内存块中保存着一系列的内存单元。这个数据结构需要包含以下基本信息:nSize:整型数据,表示该 block 在内存中的大小nFree:整型,表示剩下有几个 unit 未被分配nFirst:整型,表示下一个可供分配的 unit 的标识号pNext:指针,指向下一个 memory blockMemory Pool一个内存池总的管理数据结构,换句话说,是一个内存池对象。pBlock:指针,指向第一个 memory blocknUnitSize:整型,表示每个 unit 的尺寸nInitSize:整型,表示第一个 block 的 unit 个数nGrowSize:整型,表示在第一个 block 之外再继续增加的每个 block 的 unit 个数函数接口作为一个内存池,需要实现以下一些基本的函数接口,或者说可以是对象方法:memPoolCreate()创建一个 memory pool,必须的参数为 unit size,可选参数为上文 memory pool 的 nInitSize 和 nGrowSize。memPoolDestroy()销毁整个 memory pool 并交还给操作系统。memPoolAlloc()从 memory pool 中分配一个 unit,其尺寸是预先定义的 unit size。memPoolFree()释放一个指定的 unit。工作过程现在我们用一个 unit size 为 1024、init size 为 4(每一个 block 有 4 个 units)的 memory pool 为例,解释一下内存池的工作原理。下文假设整型的宽度为 4 个字节。创建 memory pool程序开始,调用并创建一个 memory pool。此时调用的函数为 memPoolCreate(),程序会创建一个数据结构,相应的结构体成员及其取值如下:memory pool alloc当调用者第一次请求 memPoolAlloc() 时,内存池发现 block 链表为空,于是想系统申请内存,创建 memory block,并初始化如下(其中地址值为假设值):其中 nSize = 4112 = sizeof(memPool) + nInitSize * sizeof(memUnit)。每一个 nNext 依次加一,各指代着跟着自己的下一个 unit。最后一个 unit 的 nNext 值无意义,因此不说明其取值。然后返回需要的 unit 中的内存。返回内存的逻辑如下:内存池在 block 中查询 nFree 成员由于 nFree > 0,表示有未分配的 unit,因此继续在该 block 中查看 nFirst 成员nFirst 等于 0,表示该 block 中位置为 0 的 unit 可用。因此内存池可以将这个 unit 中的 pData 地址返回给调用方。 pData 的地址值计算方式为:pBlock + sizeof(memBlock) + nFirst * (sizeof(memUnit)) + sizeof(nNext) = 0x10010nFree 减一修改 nFirst 的值,标记下一个可用的 unit。注意这里的 nFirst 切切不能简单地加一,而是取返回给调用方的 unit 所对应的 nNext 的值,也就是下图(2)处原来的值 1将 pData 的地址值返回。为便于说明,这块区域我们标记为 CA操作后各数据结构的状态如下:第二次调用 alloc 的情况类似。调用后各数据结构的状态如下:memory pool free我们先看看结果:首先程序会检查 CA 的地址值,很快就会发现,地址 0x10010 位于上述第一个 block 的范围之内(0x10000 <= 0x10010 <= (0x10000 + 4112))。再计算偏移值可以很快得出其对应的 nNext 标号,也就是上图中的(2)位置。回收 unit,此时需要标记相应的成员值以标示 unit 的回收状态。首先查看 nFirst 的值,参见上前幅图,nFirst 的值为 3,表示位置(3)处的 unit 是可用的。因此我们首先把 (2) 处的 nNext 值设置为 3,将其加回到可用 unit 的链表中将 nFirst 的值修改为 0,也就是代表刚刚回收回来的 unit 的标号,而(2)处的值赋值为 2,表示b(3)的 unit其实可以看到,上面就是一个简单的链表操作。根据上面的过程,如果 CB 也释放了的话,那么 memory pool 的状态则会变成这样:到这个时候,由于整个 block 已经完全回收了(nFree == nInitSize),那么根据不同的策略,可以考虑将整个 block 从内存中释放掉。block 满我们回到 alloc 的逻辑中,可以看到内存池最开始会检查 block 的 nFree 成员。如果 nFree == 0 的时候,那么就会在该 block 的 pNext 中去找到下一个 block,再去检查 nFree。如果发现 block 链表已经结束了,那就意味着当前所有的 block 已满,必须创建新的 block。在实际设计中,我们需要考虑选取合适的 init size 和 grow size 值。从上面的算法中可以看到,如果 alloc/free 调用非常频繁时,第一个 block 的使用效率是非常高的。变体或改进有些简化的版本中,可以不使用 pNext 来维护链表,也就是只有一个 block,并且内存的使用有一个明确且受控的上限值。这经常用在没有 malloc 系统调用的 RTOS 或者是一些对内存非常敏感的嵌入式系统中。如果要用于多线程环境中,那么 memory pool 结构体需要加上锁参考资料《C++应用程序性能优化》 - 内存池 章节Memory Pool Basic Concepts相关阅读【每日课程推荐】机器学习实战!快速入门在线广告业务及CTR相应知识 ...

November 14, 2018 · 2 min · jiezi