Linux系列:如何用 C#调用 C方法造成内存泄露

news2025/4/15 19:13:37

一个简单的非托管内存泄露

1. 构建 so 文件

在 Windows 平台上我们会通过 MSVC 编译器将 C代码编译出一个成品 .dll,在 Linux 上通常会借助 gcc 将 c 编译成 .so 文件,这个.so 全称 Shared Object,为了方便讲解,先上一段简单的代码:


#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>

#define BLOCK_SIZE (10 * 1024)              // 每个块 10K
#define TOTAL_SIZE (1 * 1024 * 1024 * 1024) // 总计 1GB
#define BLOCKS (TOTAL_SIZE / BLOCK_SIZE)    // 计算需要的块数

void heapmalloc()
{
    uint8_t *blocks[BLOCKS]; // 存储每个块的指针

    // 分配 1GB 内存,分成多个小块
    for (size_t i = 0; i < BLOCKS; i++)
    {
        blocks[i] = (uint8_t *)malloc(BLOCK_SIZE);
        if (blocks[i] == NULL)
        {
            printf("内存分配失败!\n");
            return;
        }

        // 确保每个块都被实际占用
        memset(blocks[i], 20, BLOCK_SIZE);
    }

    printf("已经分配 1GB 内存在堆上!\n");
}

接下来使用 gcc 编译,参考如下:


gcc -shared -o libmyleak.so -fPIC myleak.c

  • -shared: 编译成共享库

  • -fPIC: 指定共享库可以在内存任意位置被加载(地址无关性)

命令执行完之后,就可以看到一个 .so 文件了,截图如下:

最后可以用 nm 命令验证下 libmyleak.so 中是否有 Text 段下的 heapmalloc 导出函数。


root@ubuntu2404:/data2/c# nm libmyleak.so
0000000000004028 b completed.0
                 w __cxa_finalize@GLIBC_2.2.5
00000000000010c0 t deregister_tm_clones
0000000000001130 t __do_global_dtors_aux
0000000000003e00 d __do_global_dtors_aux_fini_array_entry
0000000000004020 d __dso_handle
0000000000003e08 d _DYNAMIC
000000000000125c t _fini
0000000000001170 t frame_dummy
0000000000003df8 d __frame_dummy_init_array_entry
00000000000020f8 r __FRAME_END__
0000000000003fe8 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
000000000000203c r __GNU_EH_FRAME_HDR
0000000000001179 T heapmalloc
0000000000001000 t _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 U malloc@GLIBC_2.2.5
                 U memset@GLIBC_2.2.5
                 U puts@GLIBC_2.2.5
00000000000010f0 t register_tm_clones
                 U __stack_chk_fail@GLIBC_2.4
0000000000004028 d __TMC_END__

2. C# 代码调用

so构建好了之后,后面就比较好说了,使用 dotnet new console -n CSharpApplication --use-program-main true 新建一个CS项目。


root@ubuntu2404:/data2/csharp# dotnet new console -n CSharpApplication --use-program-main true
The template "Console App" was created successfully.

Processing post-creation actions...
Restoring /data2/csharp/CSharpApplication/CSharpApplication.csproj:
  Determining projects to restore...
  Restored /data2/csharp/CSharpApplication/CSharpApplication.csproj (in 1.7 sec).
Restore succeeded.

编译下 C# 项目,然后将 libmyleak.so 放到 C#项目的 bin目录,修改 C# 代码如下:


using System.Runtime.InteropServices;

namespace CSharpApplication;

class Program
{
    [DllImport("libmyleak.so", CallingConvention = CallingConvention.Cdecl)]
    public static extern void heapmalloc();

    static void Main(string[] args)
    {
        heapmalloc();
        Console.ReadLine();
    }
}

最后用 dotnet CSharpApplication.dll 运行:


root@ubuntu2404:/data2/csharp/CSharpApplication/bin/Debug/net8.0# dotnet CSharpApplication.dll
已经分配 1GB 内存在堆上!

程序是跑起来了,那真的是吃了1G呢? 可以先用 htop 观察程序,从截图看没毛病。

那这 1G 真的在 heap 上吗? 可以用 maps 观察。


root@ubuntu2404:~# ps -ef | grep CSharp
root       10764   10730  0 13:35 pts/21   00:00:00 dotnet CSharpApplication.dll
root       11049   11027  0 13:41 pts/22   00:00:00 grep --color=auto CSharp

root@ubuntu2404:~# cat /proc/10764/maps
614e1f592000-614e1f598000 r--p 00000000 08:02 1479867                    /usr/lib/dotnet/dotnet
614e1f598000-614e1f5a4000 r-xp 00005000 08:02 1479867                    /usr/lib/dotnet/dotnet
614e1f5a4000-614e1f5a5000 r--p 00010000 08:02 1479867                    /usr/lib/dotnet/dotnet
614e1f5a5000-614e1f5a6000 rw-p 00010000 08:02 1479867                    /usr/lib/dotnet/dotnet
614e5b5d9000-614e9b8a8000 rw-p 00000000 00:00 0                          [heap]
...


root@ubuntu2404:~# pmap 10764
10764:   dotnet CSharpApplication.dll
0000614e1f592000     24K r---- dotnet
0000614e1f598000     48K r-x-- dotnet
0000614e1f5a4000      4K r---- dotnet
0000614e1f5a5000      4K rw--- dotnet
0000614e5b5d9000 1051452K rw---   [ anon ]
...

根据 linux 进程的内存布局,可执行image之后是 heap 堆,可以看到 [heap] 约等于1G (614e9b8a8000 - 614e5b5d9000),即 pmap 中的 1051452K。

总结

部署在 Linux上的.NET程序同样存在 非托管内存泄露 的问题,这篇文章的例子虽然很简单,希望能给大家带来一些思考和观测途径吧。

文章转载自:一线码农

原文链接:Linux系列:如何用 C#调用 C方法造成内存泄露 - 一线码农 - 博客园

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

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

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

相关文章

C# 数据类型相关

分类 按照数据复杂程度 按照数据存储 类型转换 隐式转换 隐式转换无法完成由精度高的数据类型向精度低的数据类型转换 显式转换 又称为强制类型转换&#xff0c;显示转换不一定总是成功&#xff0c;且转换过程中可能出现数据丢失 int num 666;float result (float)num; …

使用Word时无法粘贴,弹出错误提示:运行时错误‘53‘:文件未找到:MathPage.WLL

报错说明 使用Word时无法粘贴&#xff0c;粘贴时弹出提示如下&#xff1a; 一般出现这种情况时&#xff0c;我想你是刚装完MathType不久&#xff0c;博主装的是MathType7版本&#xff0c;出现了这个问题。 出现这个问题的原因是"mathpage.wll"这个文件在Office的插…

玩转python: 深度解析Python高阶函数及推导式

1 高阶函数&#xff1a;工程化编程的基石 1.1 高阶函数基础概念 高阶函数&#xff08;Higher-Order Function&#xff09;是函数式编程范式的核心要素&#xff0c;指能够接受函数作为参数或返回函数作为结果的函数。在Python中&#xff0c;这类函数构成了数据处理的基础架构&…

Linux第五讲----gcc与g++,makefile/make

1.代码编译 1.1预处理 我们通过vim编辑完文件之后&#xff0c;想看一下运行结果这时我们便可以试用gcc编译C语言&#xff0c;g编译c. 编译代码&#xff1a; 上述两种方法均可&#xff0c;code.c是我的c语言文件&#xff0c;mycode是我给编译后产生的二进制文件起的名&#x…

ubuntu22.04下Meshlab打开obj文件闪退——使用Appimage并放入收藏夹中

文章目录 ubuntu22.04下Meshlab打开obj文件闪退,查了下是meshlab的apt没做好。 官网下载:https://www.meshlab.net/#download 赋予权限 sudo chmod a+x MeshLab2023.12-linux.AppImage 双击运行即可 打开权限——下面操作是放在桌面上的 创建桌面快捷方式 # 在 ~/desktop (…

MAVEN的环境配置

在下载好maven后或解压maven安装包后进行环境配置 1.在用户环境变量中 新建一个MAVEN_HOME 地址为MAVEN目录 注&#xff1a;地址为解压后maven文件的根目录&#xff01;&#xff01;&#xff01; 2.在系统环境变量的path中添加该变量 %MAVEN_HOME%\bin 3. 测试maven安装是否成…

强化学习无痛上手笔记第1课

文章目录 Markov Decision ProcessDefinitionRelated Concepts Policy for MDP AgentDefinitionJudgement for PolicyValue FunctionsTD formula for value functionsRelation of V and QPolicy CriterionPolicy Improvement TheoremOptimal PolicyReinforcement Learning Fund…

智能设备上的 AI 移植与部署:新趋势与实践案例

1. 引言&#xff1a;智能设备如何运行 AI&#xff1f; 随着人工智能&#xff08;AI&#xff09;技术的快速发展&#xff0c;AI 计算已经从云端走向边缘&#xff0c;嵌入到智能设备中&#xff0c;如智能手机、智能摄像头、机器人、自动驾驶汽车等。这种本地化 AI 计算能够减少延…

【USRP】NVIDIA Sionna:用于 6G 物理层研究的开源库

目录 Sionna&#xff1a;用于 6G 物理层研究的开源库主要特点实现6G研究的民主化支持 5G、6G 等模块化、可扩展、可伸缩快速启动您的研究 好处原生人工智能支持综合研究平台开放生态系统 安装笔记使用 pip 安装基于Docker的安装从源代码安装“你好世界&#xff01;”探索锡奥纳…

LLM大型语言模型(一)

1. 什么是 LLM&#xff1f; LLM&#xff08;大型语言模型&#xff09;是一种神经网络&#xff0c;专门用于理解、生成并对人类文本作出响应。这些模型是深度神经网络&#xff0c;通常训练于海量文本数据上&#xff0c;有时甚至覆盖了整个互联网的公开文本。 LLM 中的 “大” …

BUU44 [BJDCTF2020]ZJCTF,不过如此1 [php://filter][正则表达式get输入数据][捕获组反向引用][php中单双引号]

题目&#xff1a; 我仿佛见到了一位故人。。。也难怪&#xff0c;题目就是ZJCTF 按要求提交/?textdata://,I have a dream&filenext.php后&#xff1a; ......不太行&#xff0c;好像得用filephp://filter/convert.base64-encode/resourcenext.php 耶&#xff1f;那 f…

软考中级-数据库-3.3 数据结构-树

定义:树是n(n>=0)个结点的有限集合。当n=0时称为空树。在任一非空树中,有且仅有一个称为根的结点:其余结点可分为m(m>=0)个互不相交的有限集T1,T2,T3...,Tm…,其中每个集合又都是一棵树,并且称为根结点的子树。 树的相关概念 1、双亲、孩子和兄弟: 2、结点的度:一个结…

磁盘空间不足|如何安全清理以释放磁盘空间(开源+节流)

背景&#xff1a; 最近往数据库里存的东西有点多&#xff0c;磁盘不够用 查看磁盘使用情况 df -h /dev/sda5&#xff08;根目录 /&#xff09; 已使用 92% 咱们来开源节流 目录 背景&#xff1a; 一、开源 二、节流 1.查找 大于 500MB 的文件&#xff1a; 1. Snap 缓存…

SpringCloud系列教程(十二):网关配置动态路由

除了token以外&#xff0c;还有一个很实用的功能就是把网关的路由配置放到nacos上&#xff0c;并且修改路由配置的时候&#xff0c;网关服务可以动态的更新&#xff0c;这样我们在调整网络配置的时候&#xff0c;就不用重启服务了。所以我们需要用到两个重要的类&#xff1a;Na…

Java-实现PDF合同模板填写内容并导出PDF文件

可用于公司用户合同导出pdf文件 效果图 一、导入所需要jar包 <!--生成PDF--><dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.11</version></dependency><dependency&…

基于STM32的环境监测系统(自制蓝牙APP)

目录 项目概述 实物图 演示视频 概述 硬件模块 原理图以及PCB 0.96寸OLED屏幕&#xff08;SSD1306&#xff09; CubeMX配置 初始化代码 MQ-2烟雾传感器 CubeMX配置 初始化代码 DHT11温湿度模块 驱动代码 HC-05蓝牙模块 CubeMX配置 ​编辑 空闲中断回调函数 有…

月结保障:回滚慢、行锁频发

问题背景 3.1号月结现场保障&#xff0c;到场了先让kill了一个账务的会话&#xff0c;回滚了20min&#xff0c;巡检的时候发现报表库有几条行锁&#xff1a;enq: TX - row lock contention&#xff0c;sql&#xff1a;delete from table_name 语句已经失败&#xff0c;正在回滚…

Golang的微服务服务发现机制

## 1. Golang微服务服务发现机制 微服务架构已经成为当今软件开发的主流趋势&#xff0c;它能将复杂的单体应用拆分成小而独立的服务单元&#xff0c;实现更快的开发、部署和扩展。在微服务架构中&#xff0c;服务发现是非常重要的一环&#xff0c;它能够实现服务之间的自动发现…

Keepalived 入门详解:高可用集群部署最佳实践!

1. 什么是 Keepalived&#xff1f; 在分布式集群中&#xff0c;单点故障&#xff08;SPOF&#xff09; 是影响系统稳定性的重要问题。Keepalived 作为一款高可用服务软件&#xff0c;可以有效防止集群单点故障&#xff0c;保障系统的高可用性。 Keepalived 最初是为 LVS&#…

SparkStreaming之04:调优

SparkStreaming调优 一 、要点 4.1 SparkStreaming运行原理 深入理解 4.2 调优策略 4.2.1 调整BlockReceiver的数量 案例演示&#xff1a; object MultiReceiverNetworkWordCount {def main(args: Array[String]) {val sparkConf new SparkConf().setAppName("Networ…