haddler处理队列 netty_Netty堆外内存泄漏排查,这一篇全讲清楚了

上篇文章介绍了Netty内存模型原理,由于Netty在使用不当会导致堆外内存泄漏,网上关于这方面的资料比较少,所以写下这篇文章,专门介绍排查Netty堆外内存相关的知识点,诊断工具,以及排查思路提供参考

现象

堆外内存泄漏的现象主要是,进程占用的内存较高(Linux下可以用top命令查看),但Java堆内存占用并不高(jmap命令查看),常见的使用堆外内存除了Netty,还有基于java.nio下相关接口申请堆外内存,JNI调用等,下面侧重介绍Netty堆外内存泄漏问题排查

堆外内存释放底层实现

1 java.nio堆外内存释放

Netty堆外内存是基于原生java.nio的DirectByteBuffer对象的基础上实现的,所以有必要先了解下它的释放原理

java.nio提供的DirectByteBuffer提供了sun.misc.Cleaner类的clean()方法,进行系统调用释放堆外内存,触发clean()方法的情况有2种

(1) 应用程序主动调用

ByteBuffer buf = ByteBuffer.allocateDirect(1);

((DirectBuffer) byteBuffer).cleaner().clean();

(2) 基于GC回收

Cleaner类继承了java.lang.ref.Reference,GC线程会通过设置Reference的内部变量(pending变量为链表头部节点,discovered变量为下一个链表节点),将可被回收的不可达的Reference对象以链表的方式组织起来

Reference的内部守护线程从链表的头部(head)消费数据,如果消费到的Reference对象同时也是Cleaner类型,线程会调用clean()方法(Reference#tryHandlePending())

2 Netty noCleaner策略

介绍noCleaner策略之前,需要先理解带有Cleaner对象的DirectByteBuffer在初始化时做了哪些事情:

只有在DirectByteBuffer(int cap)构造方法中才会初始化Cleaner对象,方法中检查当前内存是否超过允许的最大堆外内存(可由-XX:MaxDirectMemorySize配置)

如果超出,则会先尝试将不可达的Reference对象加入Reference链表中,依赖Reference的内部守护线程触发可以被回收DirectByteBuffer关联的Cleaner的run()方法

如果内存还是不足, 则执行 System.gc(),触发full gc,来回收堆内存中的DirectByteBuffer对象来触发堆外内存回收,如果还是超过限制,则抛出java.lang.OutOfMemoryError(代码位于java.nio.Bits#reserveMemory()方法)

而Netty在4.1引入可以noCleaner策略:创建不带Cleaner的DirectByteBuffer对象,这样做的好处是绕开带Cleaner的DirectByteBuffer执行构造方法和执行Cleaner的clean()方法中一些额外开销,当堆外内存不够的时候,不会触发System.gc(),提高性能

hasCleaner的DirectByteBuffer和noCleaner的DirectByteBuffer主要区别如下:

构造器方式不同:

noCleaner对象:由反射调用 private DirectByteBuffer(long addr, int cap)创建

hasCleaner对象:由 new DirectByteBuffer(int cap)创建

释放内存的方式不同

noCleaner对象:使用 UnSafe.freeMemory(address);

hasCleaner对象:使用 DirectByteBuffer 的 Cleaner 的 clean() 方法

note:Unsafe是位于sun.misc包下的一个类,可以提供内存操作、对象操作、线程调度等本地方法,这些方法在提升Java运行效率、增强Java语言底层资源操作能力方面起到了很大的作用,但不正确使用Unsafe类会使得程序出错的概率变大,程序不再“安全”,因此官方不推荐使用,并可能在未来的jdk版本移除

Netty在启动时需要判断检查当前环境、环境配置参数是否允许noCleaner策略(具体逻辑位于PlatformDependent的static代码块),例如运行在Android下时,是没有Unsafe类的,不允许使用noCleaner策略,如果不允许,则使用hasCleaner策略

note:可以调用PlatformDependent.useDirectBufferNoCleaner()方法查看当前Netty程序是否使用noCleaner策略

读到这里,也许有读者会问,如果Netty基于hasCleaner策略,通过GC触发Cleaner.clean(),自动回收堆外内存,是不是就可以不用考虑ByteBuf.release()方法的调用,不会内存泄漏?

当然不是,一方面原因是自动触发不实时:需要ByteBuffer对象被GC线程回收才会触发,如果ByteBuffer对象进入老年代后才变得可回收,则需要等到发送频率较低老年代GC才会触发

另一方面,Netty需要基于ByteBuf.release()方法执行其他操作,例如池化内存释放回内存池,否则该对象会被内存池一直标记为已使用

ByteBuf.release()触发机制

业界有一种误解认为 Netty 框架分配的 ByteBuf,框架会自动释放,业务不需要释放;业务创建的 ByteBuf 则需要自己释放,Netty 框架不会释放

产生这种误解是有原因的,Netty框架是会在一些场景调用ByteBuf.release()方法:

1 入站消息处理

当处理入站消息时,Netty会创建ByteBuf读取channel上的消息,并触发调用pipeline上的ChannelHandler处理,应用程序定义的使用ByteBuf的ChannelHandler需要负责release()

public void channelRead(ChannelHandlerContext ctx, Object msg) {

ByteBuf buf = (ByteBuf) msg;

try {

...

} finally {

buf.release();

}

}

如果该ByteBuf不由当前ChannelHandler处理,则传递给pipeline上下一个handler:

public void channelRead(ChannelHandlerContext ctx, Object msg) {

ByteBuf buf = (ByteBuf) msg;

...

ctx.fireChannelRead(buf);

}

常用的我们会通过继承ChannelInboundHandlerAdapter定义入站消息处理的handler,这种情况下如果所有程序的hanler都没有调用release()方法,该入站消息Netty最后并不会release(),会导致内存泄漏;

当在pipeline的handler处理中抛出异常之后,最后Netty框架是会捕捉该异常进行ByteBuf.release()的;

完整流程位于AbstractNioByteChannel.NioByteUnsafe#read(),下面抽取关键片段:

try {

do {

byteBuf = allocHandle.allocate(allocator);

allocHandle.lastBytesRead(doReadBytes(byteBuf));

// 入站消息已读完

if (allocHandle.lastBytesRead() <= 0) {

// ...

break;

}

// 触发pipline上handler进行处理

pipeline.fireChannelRead(byteBuf);

byteBuf = null;

} while (allocHandle.continueReading());

// ...

} catch (Throwable t) {

// 异常处理中包括调用 byteBuf.release()

handleReadException(pipeline, byteBuf, t, close, allocHandle);

}

不过,常用的还有通过继承SimpleChannelInboundHandler定义入站消息处理,在该类会保证消息最终被release:

@Override

public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

boolean release = true;

try {

// 该消息由当前handler处理

if (acceptInboundMessage(msg)) {

I imsg = (I) msg;

channelRead0(ctx, imsg);

} else {

// 不由当前handler处理,传递给pipeline上下一个handler

release = false;

ctx.fireChannelRead(msg);

}

} finally {

// 触发release

if (autoRelease && release) {

ReferenceCountUtil.release(msg);

}

}

}

2 出站消息处理

不同于入站消息是由Netty框架自动创建的,出站消息通常由应用程序创建,然后调用基于channel的write()方法或writeAndFlush()方法,这些方法内部会负责调用传入的byteBuf的release()方法

note: write()方法在netty-4.0.0.CR2前的版本存在问题,不会调用ByteBuf.release()

3 release()注意事项

(1) 引用计数

还有一种常见的误解就是,只要调用了ByteBuf的release()方法,或者ReferenceCountUtil.release()方法,对象的内存就保证释放了,其实不是

因为Netty的ByteBuf引用计数来管理ByteBuf对象的生命周期,ByteBuf继承了ReferenceCounted接口,对外提供retain()和release()方法,用于增加或减少引用计数值,当调用release()方法时,内部计数值被减为0才会触发内存回收动作

(2) derived ByteBuf

derived,派生的意思,在ByteBuf.duplicate(), ByteBuf.slice() 和 ByteBuf.order(ByteOrder) 等方法会创建出derived ByteBuf,创建出来的ByteBuf与原有ByteBuf是共享引用计数的,原有ByteBuf的release()方法调用,也会导致这些对象内存回收

相反ByteBuf.copy() 和 ByteBuf.readBytes(int)方法创建出来的对象并不是derived ByteBuf,这些对象与原有ByteBuf不是共享引用计数的,原有ByteBuf的release()方法调用不会导致这些对象内存回收

堆外内存大小控制参数

配置堆外内存大小的参数有-XX:MaxDirectMemorySize和-Dio.netty.maxDirectMemory,这2个参数有什么区别?

-XX:MaxDirectMemorySize

用于限制Netty中hasCleaner策略的DirectByteBuffer堆外内存的大小,默认值是JVM能从操作系统申请的最大内存,如果内存本身没限制,则值为Long.MAX_VALUE个字节(默认值由Runtime.getRuntime().maxMemory()返回),代码位于java.nio.Bits#reserveMemory()方法中

note:-XX:MaxDirectMemorySize无法限制Netty中noCleaner策略的DirectByteBuffer堆外内存的大小

-Dio.netty.maxDirectMemory

用于限制noCleaner策略下Netty的DirectByteBuffer分配的最大堆外内存的大小,如果该值为0,则使用hasCleaner策略,代码位于PlatformDependent#incrementMemoryCounter()方法中

堆外内存监控

如何获取堆外内存的使用情况?

1 代码工具

(1) hasCleaner的DirectByteBuffer监控

对于hasCleaner策略的DirectByteBuffer,java.nio.Bits类是有记录堆外内存的使用情况,但是该类是包级别的访问权限,不能直接获取,可以通过MXBean来获取

note:MXBean,Java提供的一系列用于监控统计的特殊Bean,通过不同类型的MXBean可以获取JVM进程的内存,线程、类加载信息等监控指标

List bufferPoolMXBeans = ManagementFactoryHelper.getBufferPoolMXBeans();

BufferPoolMXBean directBufferMXBean = bufferPoolMXBeans.get(0);

// hasCleaner的DirectBuffer的数量

long count = directBufferMXBean.getCount();

// hasCleaner的DirectBuffer的堆外内存占用大小,单位字节

long memoryUsed = directBufferMXBean.getMemoryUsed();

note: MappedByteBuffer:是基于FileChannelImpl.map进行进行mmap内存映射(零拷贝的一种实现)得到的另外一种堆外内存的ByteBuffer,可以通过ManagementFactoryHelper.getBufferPoolMXBeans().get(1)获取到该堆外内存的监控指标

(2) noCleaner的DirectByteBuffer监控

Netty中noCleaner的DirectByteBuffer的监控比较简单,直接通过PlatformDependent.usedDirectMemory()访问即可

2 Netty自带内存泄漏检测工具

Netty也自带了内存泄漏检测工具,可用于检测出ByteBuf对象被GC回收,但ByteBuf管理的内存没有释放的情况,但不适用ByteBuf对象还没被GC回收内存泄漏的情况,例如任务队列积压

为了便于用户发现内存泄露,Netty提供4个检测级别:

disabled 完全关闭内存泄露检测

simple 以约1%的抽样率检测是否泄露,默认级别

advanced 抽样率同simple,但显示详细的泄露报告

paranoid 抽样率为100%,显示报告信息同advanced

使用方法是在命令行参数设置:

-Dio.netty.leakDetectionLevel=[检测级别]

示例程序如下,设置检测级别为paranoid :

// -Dio.netty.leakDetectionLevel=paranoid

public static void main(String[] args) {

for (int i = 0; i < 500000; ++i) {

ByteBuf byteBuf = UnpooledByteBufAllocator.DEFAULT.buffer(1024);

byteBuf = null;

}

System.gc();

}

可以看到控制台输出泄漏报告:

十二月 27, 2019 8:37:04 上午 io.netty.util.ResourceLeakDetector reportTracedLeak

严重: LEAK: ByteBuf.release() was not called before it's garbage-collected. See https://netty.io/wiki/reference-counted-objects.html for more information.

Recent access records:

Created at:

io.netty.buffer.UnpooledByteBufAllocator.newDirectBuffer(UnpooledByteBufAllocator.java:96)

io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:187)

io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:178)

io.netty.buffer.AbstractByteBufAllocator.buffer(AbstractByteBufAllocator.java:115)

org.caison.netty.demo.memory.BufferLeaksDemo.main(BufferLeaksDemo.java:15)

内存泄漏的原理是利用弱引用,弱引用(WeakReference)创建时需要指定引用队列(refQueue),通过将ByteBuf对象用弱引用包装起来(代码入口位于AbstractByteBufAllocator#toLeakAwareBuffer()方法)

当发生GC时,如果GC线程检测到ByteBuf对象只被弱引用对象关联,会将该WeakReference加入refQueue;

当ByteBuf内存被正常释放,会调用WeakReference的clear()方法解除对ByteBuf的引用,后续GC线程不会再将该WeakReference加入refQueue;

Netty在每次创建ByteBuf时,基于抽样率,抽样命中时会轮询(poll)refQueue中的WeakReference对象,轮询返回的非null的WeakReference关联的ByteBuf即为泄漏的堆外内存(代码入口位于ResourceLeakDetector#track()方法)

3 图形化工具

在代码获取堆外内存的基础上,通过自定义接入一些监控工具定时检测获取,绘制图形即可,例如比较流行的Prometheus或者Zabbix

也可以通过jdk自带的Visualvm获取,需要安装Buffer Pools插件,底层原理是访问MXBean中的监控指标,只能获取hasCleaner的DirectByteBuffer的使用情况

此外,对于JNI调用产生的堆外内存分配,可以使用google-perftools进行监控

堆外内存泄漏诊断

堆外内存泄漏的具体原因比较多,先介绍任务队列堆积的监控,再介绍通用堆外内存泄漏诊断思路

1 任务队列堆积

这里的任务队列是值NioEventLoop中的QueuetaskQueue,提交到该任务队列的场景有:

(1) 用户自定义普通任务

ctx.channel().eventLoop().execute(runnable);

(2) 对channel进行写入

channel.write(...)

channel.writeAndFlush(...)

(3) 用户自定义定时任务

ctx.channel().eventLoop().schedule(runnable, 60, TimeUnit.SECONDS);

当队列中积压任务过多,导致消息不能对channel进行写入然后进行释放,会导致内存泄漏

public void channelActive(ChannelHandlerContext ctx) throws NoSuchFieldException, IllegalAccessException {

monitorPendingTaskCount(ctx);

monitorQueueFirstTask(ctx);

monitorOutboundBufSize(ctx);

}

/** 监控任务队列堆积任务数,任务队列中的任务包括io读写任务,业务程序提交任务 */

public void monitorPendingTaskCount(ChannelHandlerContext ctx) {

int totalPendingSize = 0;

for (EventExecutor eventExecutor : ctx.executor().parent()) {

SingleThreadEventExecutor executor = (SingleThreadEventExecutor) eventExecutor;

// 注意,Netty4.1.29以下版本本pendingTasks()方法存在bug,导致线程阻塞问题

// 参考 https://github.com/netty/netty/issues/8196

totalPendingSize += executor.pendingTasks();

}

System.out.println("任务队列中总任务数 = " + totalPendingSize);

}

/** 监控各个堆积的任务队列中第一个任务的类信息 */

public void monitorQueueFirstTask(ChannelHandlerContext ctx) throws NoSuchFieldException, IllegalAccessException {

Field singleThreadField = SingleThreadEventExecutor.class.getDeclaredField("taskQueue");

singleThreadField.setAccessible(true);

for (EventExecutor eventExecutor : ctx.executor().parent()) {

SingleThreadEventExecutor executor = (SingleThreadEventExecutor) eventExecutor;

Runnable task = ((Queue) singleThreadField.get(executor)).peek();

if (null != task) {

System.out.println("任务队列中第一个任务信息:" + task.getClass().getName());

}

}

}

/** 监控出站消息的队列积压的byteBuf大小 */

public void monitorOutboundBufSize(ChannelHandlerContext ctx) {

long outBoundBufSize = ((NioSocketChannel) ctx.channel()).unsafe().outboundBuffer().totalPendingWriteBytes();

System.out.println("出站消息队列中积压的buf大小" + outBoundBufSize);

}

note: 上面程序至少需要基于Netty4.1.29版本才能使用,否则有性能问题

实际基于Netty进行业务开发,耗时的业务逻辑代码应该如何处理?

先说结论,建议自定义一组新的业务线程池,将耗时业务提交业务线程池

Netty的worker线程(NioEventLoop),除了作为NIO线程处理连接数据读取,执行pipeline上channelHandler逻辑,另外还有消费taskQueue中提交的任务,包括channel的write操作。

如果将耗时任务提交到taskQueue,也会影响NIO线程的处理还有taskQueue中的任务,因此建议在单独的业务线程池进行隔离处理

2 通用诊断思路

Netty堆外内存泄漏的原因多种多样,例如代码漏了写调用release();通过retain()增加了ByteBuf的引用计数值而在调用release()时引用计数值未清空;因为Exception导致未能release();ByteBuf引用对象提前被GC,而关联的堆外内存未能回收等等,这里无法全部列举,所以尝试提供一套通用的诊断思路提供参考

首先,需要能复现问题,为了不影响线上服务的运行,尽量在测试环境或者本地环境进行模拟。但这些环境通常没有线上那么大的并发量,可以通过压测工具来模拟请求

对于有些无法模拟的场景,可以通过Linux流量复制工具将线上真实的流量复制到到测试环境,同时不影响线上的业务,类似工具有Gor、tcpreplay、tcpcopy等

能复现之后,接下来就要定位问题所在,先通过前面介绍的监控手段、日志信息试试能不能直接找到问题所在;

如果找不到,就需要定位出堆外内存泄漏的触发条件,但有时应用程序比较庞大,对外提供的流量入口很多,无法逐一排查。

在非线上环境的话,可以将流量入口注释掉,每次注释掉一半,然后再运行检查问题是否还存在,如果存在,继续再注释掉剩下的一半,通过这种二分法的策略通过几次尝试可以很快定位出问题触发条件

定位出触发条件之后,再检查程序中在该触发条件处理逻辑,如果该处理程序很复杂,无法直接看出来,还可以继续注释掉部分代码,二分法排查,直到最后找出具体的问题代码块

整套思路的核心在于,问题复现、监控、排除法,也可以用于排查其他问题,例如堆内内存泄漏、CPU 100%,服务进程挂掉等

总结

整篇文章侧重于介绍知识点和理论,缺少实战环节,这里分享一些优质博客文章:

《netty 堆外内存泄露排查盛宴》 闪电侠手把手带如何debug堆外内存泄漏

https://www.jianshu.com/p/4e96beb37935

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

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

相关文章

linux 硬盘大小 命令,linux 查看磁盘空间大小的命令和方法

这篇文章主要为大家详细介绍了linux 查看磁盘空间大小的命令和方法&#xff0c;具有一定的参考价值&#xff0c;感兴趣的小伙伴们可以参考一下,有需要的朋友可以收藏方便以后借鉴。1.Ubuntu 查看磁盘空间大小命令df -h Df命令是linux系统以磁盘分区为单位查看文件系统&#xff…

matlab GUI 设计 自学笔记

图形用户界面&#xff08;Graphical User Interface&#xff0c;GUI&#xff09;&#xff1a; 用户与计算机进行信息交流的窗口。 设计图形用户界面的方法 调用建立用户界面控件的函数 使用MATLAB提供的GUIDE工具进行可视化设计 控件对象及其操作 &#xff08;1&#xff09…

grub shell 错误_各种情况GRUB损坏进不了系统的解决方案

1.在磁盘分区表变动之后&#xff0c;比如删除了grub.cfg所在分区的前一个分区&#xff0c;导致分区号前移一位时&#xff0c;开机显示 grub rescue> 。处理方法&#xff1a;(假设原来grub.cfg在/dev/sda4&#xff0c;而现在变成了/dev/sda3)ls(查看分区)ls (hd0,7)/boot/(查…

linux 系统盘无法ls,linux – ls:阅读目录.:输入/输出错误

当我尝试在已安装的硬盘上使用“ls”时,我的系统引发了I / O错误.我在用hadoophbase1:/hddata$uname -aLinux hbase1 3.8.0-29-generic #42~precise1-Ubuntu SMP Wed Aug 14 16:19:23 UTC 2013 x86_64 x86_64 x86_64 GNU/Linuxhadoophbase1:/hddata$dfFilesystem 1K-blocks Use…

matlab guide对话框+滑动条+弹出式菜单+列表框的使用

文章目录前言matlab数据传递概观对话框创建利用滑动条实现颜色调控利用弹出式菜单选择并输入文本框利用列表框选择并输入文本框前言 我觉得gui无非就是给程序加了件衣服&#xff0c;其实具体的程序我们都会写&#xff0c;我们唯一不明白的是那几个部分数据之间的传递&#xff…

流放之路材质过滤怎么设置_松下除湿机怎么样 松下除湿机款式有哪些型号【详解】...

马上南方就要进入梅雨季节了&#xff0c;潮湿的天气容易滋生细菌&#xff0c;可能会导致人们出现过敏现象&#xff0c;所以很多家庭都选择购买除湿机&#xff0c;现在市场上的除湿机不是很多&#xff0c;其中松下的除湿机比较受欢迎&#xff0c;所以小编我就来跟大家说一说松下…

matlab图形句柄+图形对象+图形对象的属性+对象操作

文章目录前言&#xff1a;图形对象&#xff1a;图形句柄&#xff1a;图形对象的属性&#xff1a;对象的基本操作figure对象(图形窗口)figure()函数&#xff1a;clf()函数清空当前图形窗口&#xff1a;axes坐标轴对象image()line()text()前言&#xff1a; 下面这些是为了更好地…

linux用户开放crontab权限,linux – / etc / crontab权限

/ etc / crontab文件具有以下权限&#xff1a;-rw-R – R–我知道这个文件是用于系统cron作业的,其他用户不应该有权修改它.当前权限允许所有用户读取对文件的访问权限,使他们能够查看内容.是否有必要让所有用户都能读取/ etc / crontab&#xff1f;我相信所有用户都不应该知道…

echart 高度 不用 不撑满_注意厨房台面高度及细节 装出省心舒服 装出事半功倍...

厨房的装修设计最好还是细节做得好&#xff0c;细节做得能达到事半功倍的效果&#xff0c;厨房台面高度如何设计才是最合适呢&#xff1f;我们一起走进包头装修网了解一下吧&#xff01;下面三个台面的设计细节&#xff0c;也许就能让你家的厨房突然变得好用起来——哪怕你可能…

利用matlab guide制作简易计算器

前言&#xff1a; 当然了这个太简单了&#xff0c;新手可以借鉴一下&#xff0c;举一反三的话还可以添加一些功能或者简洁一些。比如下拉框啊之类的 而且呢&#xff0c;这个你弄明白了&#xff0c;所有运算有关的&#xff0c;输入输出有关的&#xff0c;都大致相同。 实现过程…

linux复制目录命令夹,linux复制目录(文件夹)和打包命令

linux复制目录(文件夹)和打包命令复制目录命令&#xff1a; cp 需要复制的目录 -r 目的目录 (注意带参数-r)压缩文件&#xff1a;zip -r 压缩后文件名 需要压缩的目录喎?http://www.Bkjia.com/kf/ware/vc/" target"_blank" class"keylink">vcD4KP…

c#怎么拟合函数得到参数_吴恩达老师课程笔记系列第32节 -正则化之代价函数(2)...

第32节 -正则化之代价函数(2)参考视频: 7 - 2 - Cost Function (10 min).mkv 上面的回归问题中如果我们的模型是&#xff1a;我们可以从之前的事例中看出&#xff0c;正是那些高次项导致了过拟合的产生&#xff0c;所以如果我们能让这些高次项的系数接近于 0 的话&#xff0c;我…

Matlab guide菜单+快捷菜单的使用

菜单选择&#xff1a; Opening函数&#xff1a; openingFcn执行于窗口可见之前&#xff0c;这个时候你想做什么的话可以把代码写进去 function plott_OpeningFcn(hObject, eventdata, handles, varargin) t1/200:1/200:1; plot(t,sin(2*pi*t*10),r);几个回调函数&#xff1a; …

linux include 编译,linux-如何使用OpenSSL include编译.c文件?

linux-如何使用OpenSSL include编译.c文件&#xff1f;我正在尝试编译一个包含以下内容的小.c文件&#xff1a;#include #include #include #include 在我拥有.c文件的同一文件夹中&#xff0c;我拥有带有所有这些文件(以及更多文件)的/ openssl&#xff0c;也在突触包管理器中…

c向文件中插入数据_如何把数据写入顺序文件中,VBA代码中Write#语句的利用

大家好&#xff0c;我们今日继续讲解VBA代码解决方案的第132讲内容&#xff1a;使用 Write #语句把数据写入打开顺序文件中。在上一讲的内容中我们讲了打开一文本文件来写入数据的两种方法有&#xff1a;Append或Output&#xff0c;那么问题来了&#xff0c;如何往打开的文件中…

JavaScript变量声明+数据类型+数字格式+操作符+进制

文章目录1.那些高大上的概念术语都是指什么什么是web应用&#xff1f;什么是前台后台&#xff1f;怎么做网页界面呢&#xff1f;JavaScript可以应用到什么上面&#xff1f;2.JavaScript实例&#xff1a;3.语言基础变量声明数据类型基本数据类型&#xff1a;引用数据类型&#x…

linux查看帮助文档的命令,Linux如何使用查看一个命令的帮助文档呢?

摘要:下文讲述Linux操作系统中查看命令的帮助文档的方法分享&#xff0c;如下所示&#xff1b;实现思路:使用 --help命令即可查看命令自带的帮助文档信息例:查看ls命令的帮助信息[rootlocalhost test]# ls --helpUsage: ls [OPTION]... [FILE]...List information about the FI…

python如何更改entry属性_如何在Python3中更改Gtk3 Entry文本颜色?

我在我的应用程序中有一个Gtk.Entry()列表,我想改变其中一些文本的颜色.我尝试了以下方法&#xff1a;#!/usr/bin/python3# Filename: mywindow.pyfrom gi.repository import Gtkfrom gi.repository import Gdkclass MyWindow(Gtk.Window):def __init__(self):Gtk.Window.__ini…

linux 磁盘簇,linux系统exec簇工作原理

操作系统是一个用来和硬件打交道并为用户程序提供一个有限服务集的低级支撑软件。一个计算机系统是一个硬件和软件的共生体&#xff0c;它们互相依赖&#xff0c;不可分割。计算机的硬件&#xff0c;含有外围设备、处理器、内存、硬盘和其他的电子设备组成计算机的发动机。但是…

JavaScript 函数定义+内置函数使用+array对象+object类型

文章目录函数定义random()setInterval()setTimeout()数组对象object函数定义 两种格式&#xff1a; function 自定义函数名称(参数1&#xff0c;参数2&#xff0c;...&#xff0c;参数n){ //函数体 return 返回值; }函数表达式写法&#xff08;匿名函数&#xff09;&#xff…