Linux 多线程程序虚拟内存占用过大有什么问题吗? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
algas
V2EX    Linux

Linux 多线程程序虚拟内存占用过大有什么问题吗?

  algas 2016-10-09 11:40:21 +08:00 12097 次点击
这是一个创建于 3290 天前的主题,其中的信息可能已经有所发展或是发生改变。
最近写了一个程序,总是在计算规模比较大的时候异常退出
[1] 47393 segmentation fault ./a.out 300 0.5

发现退出总是发生在启动多线程的部分,多线程部不需要额外申请内存。
在退出发生前,我观察到系统程序虚拟内存比较高, RSS ~ 80G , VSZ ~ 135G 。
机器只有 128G 物理内存,没有分配 swap 分区和文件, VSZ 明显超过了物理内存,不知道是不是因为这个原因导致程序退出的,求指点。


> ulimit -a

-t: cpu time (seconds) unlimited
-f: file size (blocks) unlimited
-d: data seg size (kbytes) unlimited
-s: stack size (kbytes) unlimited
-c: core file size (blocks) 0
-m: resident set size (kbytes) unlimited
-u: processes 515250
-n: file descriptors 1024
-l: locked-in-memory size (kbytes) 64
-v: address space (kbytes) unlimited
-x: file locks unlimited
-i: pending signals 515250
-q: bytes in POSIX msg queues 819200
-e: max nice 0
-r: max rt priority 0
-N 15: unlimited
第 1 条附言    2016-10-09 21:26:10 +08:00
发现一些有用的知识

2 、 segfault at 和 error 4 这两条信息可以得出是内存读出错, 4 的意义如下,可以对照参考:
bit2:值为 1 表示是用户态程序内存访问越界,值为 0 表示是内核态程序内存访问越界
bit1: 值为 1 表示是写操作导致内存访问越界,值为 0 表示是读操作导致内存访问越界
bit0: 值为 1 表示没有足够的权限访问非法地址的内容,值为 0 表示访问的非法地址根本没有对应的页面,也就是无效地址

抄自: http://blog.csdn.net/zhaohaijie600/article/details/45246569

大概意思是说我的程序在用户态,对非法地址,写操作了....?
第 2 条附言    2016-10-10 00:16:23 +08:00
感谢大家指点,学习到很多。

我大概找到原因了,问题在于 int 型变量溢出了....

int N = 220 * 220;
double *correlatiOnMatrix= malloc(N * N * sizeof(double));

这里我没有检查 malloc 是否成功了,因为 N*N 会溢出 32 位整数,所以并没有申请到内存,
所以多线程部分一写数据就挂了。
43 条回复    2016-10-15 09:45:44 +08:00
wayslog
    1
wayslog  
   2016-10-09 11:50:34 +08:00
那个……老兄……你开了多少线程……没记错的话,每个线程默认会分配 8M 的空间。而且,线程过多其实并不是一件好事儿, IO bound 的话考虑开核心数量的 2x 到 4x , CPU bound 的话干脆开 1x 就行……
ryd994
    2
ryd994  
   2016-10-09 12:20:35 +08:00 via Android
虚拟内存不一定占用物理内存,不说 swap , mmap 也是计入的。
不过如果是内存不足的话应该会 oom 异常。 segfault 建议还是先查内存 bug ,上 valgrind
有可能是多线程有 bug 啊,特别是竞态导致超预期数据
algas
    3
algas  
OP
   2016-10-09 12:43:13 +08:00
@wayslog
40 线程, cpu 是 12 物理核心,两路服务器,应该没有超开的。
退出前会开启 20 到 30 多个线程。
algas
    4
algas  
OP
   2016-10-09 12:49:47 +08:00
@ryd994
感觉上更像是 stack 之类的爆了,说实话我对这个不太了解,但是我已经完全手动申请释放内存了。
异常退出的部分没有申请内存,多线程部分只是计算不同序列的平均值之类的东西,读写都是不同位置的内存。
这个程序是同样使用 40 线程计算小一点的系统是完全正常的,不像存在内存泻露的样子。

另外就是,小规模情况下没有问题,出现问题的计算规模都要占用一半以上的物理内存, valgrind 是不是没有办法对付这种情况?
ryd994
    5
ryd994  
   2016-10-09 13:04:31 +08:00 via Android
@algas 估计会很慢
能不能用更少线程计算大问题呢?
试试至少跑一下,有些内存错误小规模可能神不知鬼不觉没有爆发,但 valgrind 查非法访问一看就知道了
icylord
    6
icylord  
   2016-10-09 13:13:33 +08:00
排除代码 bug 了嘛
reus
    7
reus  
   2016-10-09 13:27:51 +08:00
用 rust 写就没有 segfault 了,理论上
wayslog
    8
wayslog  
   2016-10-09 13:37:26 +08:00
@reus 你试试 let mut v = [1u8; 1024*1024*1024*4+1];
wayslog
    9
wayslog  
   2016-10-09 13:38:55 +08:00
@reus 我们不排除 segfault ,该爆栈还是要爆,但是 rust 的极小 runtime 会拦截这些 segfault ,改成 panic ,能更好的 debug 。

另外上一个提问的安全解答是: let mut v = box [1u8; 1024*1024*1024*4+1];
algas
    10
algas  
OP
   2016-10-09 14:03:58 +08:00
@ryd994
我尝试一下。


@icylord
之前都已经在用来出结果了,最近找了个配置很好的机器,才发现这种问题。


编译参数:
g++ -O3 -fopenmp -mavx -mfma -fPIC --share -g -I ./ testa.cpp -o libtesta.so

gcc basic/*.c *.c -O3 -DSFMT_MEXP=19937 -lpthread -lm -Wall -fopenmp -mavx -mfma -L ./ -ltesta -Wl,-rpath=.

C 和 C++混编,但是程序还没有进入到 c++的库就退出了。
xiaozhaoz
    11
xiaozhaoz  
   2016-10-09 14:06:19 +08:00
确认系统 /proc/sys/vm/overcommit_* 设置。

overcommit* 控制了进程占用虚拟空间 和 系统物理内存的关系。
xiaozhaoz
    12
xiaozhaoz  
   2016-10-09 14:09:26 +08:00
cat /proc/meminfo , 贴出来看看
algas
    13
algas  
OP
   2016-10-09 14:09:53 +08:00
@xiaozhaoz

这 overcommnit_*文件如下,这个完全不明白是什么意思。

[14:08]:ls /proc/sys/vm/overcommit_*
/proc/sys/vm/overcommit_kbytes /proc/sys/vm/overcommit_memory /proc/sys/vm/overcommit_ratio

cat /proc/sys/vm/overcommit_*
0
0
50
algas
    14
algas  
OP
   2016-10-09 14:10:46 +08:00
@xiaozhaoz

[14:10]:cat /proc/meminfo
MemTotal: 131915360 kB
MemFree: 131296756 kB
MemAvailable: 131385392 kB
Buffers: 20536 kB
Cached: 173024 kB
SwapCached: 0 kB
Active: 151716 kB
Inactive: 104916 kB
Active(anon): 63772 kB
Inactive(anon): 2520 kB
Active(file): 87944 kB
Inactive(file): 102396 kB
Unevictable: 68 kB
Mlocked: 68 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 0 kB
Writeback: 0 kB
AnonPages: 63200 kB
Mapped: 53380 kB
Shmem: 3200 kB
Slab: 72120 kB
SReclaimable: 26112 kB
SUnreclaim: 46008 kB
KernelStack: 10416 kB
PageTables: 5484 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 65957680 kB
Committed_AS: 151684 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 499908 kB
VmallocChunk: 34291843068 kB
HardwareCorrupted: 0 kB
AnonHugePages: 10240 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 891976 kB
DirectMap2M: 103849984 kB
DirectMap1G: 31457280 kB
xiaozhaoz
    15
xiaozhaoz  
   2016-10-09 14:19:46 +08:00
CommitLimit: 65957680 kB
Committed_AS: 151684 kB

单一进程,最多只允许 malloc() 66G 虚拟内存, 超过就会 malloc 失败。

echo 1 > /proc/sys/vm/overcommit_memory

再试试看。
ryd994
    16
ryd994  
   2016-10-09 14:20:47 +08:00 via Android
@xiaozhaoz 楼主说的 vsz 和你说的虚拟内存根本不是一个东西, windows 下的虚拟内存, Linux 下叫 swap

@algas 我又查了一下,你这应就是 bug 导致的访问错误
如果 oom 的话, kernal log 里会有记录,比如: http://askubuntu.com/questions/399458/out-of-memory-when-booting-ubuntu-or-any-linux-distro-from-live-usb
alqaz
    17
alqaz  
   2016-10-09 14:29:40 +08:00
是不是应该还是程序有 bug, 既然跑大一点计算才出问题,可不可能是只有这种情况下某些代码才会执行,然后触发了某个 bug?
xiaozhaoz
    18
xiaozhaoz  
   2016-10-09 14:35:27 +08:00   1
@ryd994
如果楼主是 ps 命令看到的 VSZ , 那就是虚拟内存。
VSZ virtual memory size of the process in KiB (1024-byte units). Device mappings are currently excluded

楼主确实没说清楚。
如果整个系统只占用 80G RSS ,一般不会 OOM ,但内存 zone 情况不好说。
所以 dmesg 或者 /var/下面的 log 看一眼才能确定。
xiaozhaoz
    19
xiaozhaoz  
   2016-10-09 14:37:10 +08:00
楼主的 overcommit_memory 设置是 0 , 所以该程序的虚拟内存 overcommit limit 是动态计算的, 不是 66G 。
如果 overcommit_memory == 2, 超过 66G 会立即异常。
xiaozhaoz
    20
xiaozhaoz  
   2016-10-09 14:40:04 +08:00
@ryd994 通过楼主提供的信息,收到是 signal 11 。 所以初步认为是 overcommit , 而不是 oom , oom 是 signal 9.
ryd994
    21
ryd994  
   2016-10-09 14:43:47 +08:00 via Android
@xiaozhaoz 所以我还是倾向于程序有 bug 然后非法访问
WKPlus
    22
WKPlus  
   2016-10-09 14:47:51 +08:00
CommitLimit 只有在 overcommit_memory 是 2 的时候才生效啊,现在 overcommit_memory 是 0 , CommitLimit 是多少没关系。
可以 dmesg 看下有没有 OOM ,我觉得还是程序本身有问题的可能性比较大。
xiaozhaoz
    23
xiaozhaoz  
   2016-10-09 14:50:45 +08:00
@WKPlus

The Linux kernel supports the following overcommit handling modes

0 - Heuristic overcommit handling. Obvious overcommits of
address space are refused. Used for a typical system. It
ensures a seriously wild allocation fails while allowing
overcommit to reduce swap usage. root is allowed to
allocate slightly more memory in this mode. This is the
default.

1 - Always overcommit. Appropriate for some scientific
applications. Classic example is code using sparse arrays
and just relying on the virtual memory consisting almost
entirely of zero pages.

2 - Don't overcommit. The total address space commit
for the system is not permitted to exceed swap + a
configurable amount (default is 50%) of physical RAM.
Depending on the amount you use, in most situations
this means a process will not be killed while accessing
pages but will receive errors on memory allocation as
appropriate.

Useful for applications thatwant to guarantee their
memory allocations will be available in the future
without having to initialize every page.
WKPlus
    24
WKPlus  
   2016-10-09 14:55:24 +08:00
@xiaozhaoz 你贴这段话什么意思呢?
xiaozhaoz
    25
xiaozhaoz  
   2016-10-09 14:59:19 +08:00
@WKPlus ,

overcommit 的意思是是否允许过量使用虚存。

所以
0 是不予许,也就是超过一定的虚存会分配失败,但是一个复杂的算法计算上限。
1 是允许, 也就是虚存可以一直分配下去,知道进程的地址空间,或者 oom 发生。
2 是老模式, 就是 ( 50% * 物理内存) + swap 计算虚存使用上限。
WKPlus
    26
WKPlus  
   2016-10-09 15:04:06 +08:00
@xiaozhaoz 那么我说的 “ CommitLimit 只有在 overcommit_memory 是 2 的时候才生效啊,现在 overcommit_memory 是 0 , CommitLimit 是多少没关系” 没错啊。我是问你为啥突然贴那个给我
stephenyin
    27
stephenyin  
   2016-10-09 17:40:32 +08:00
你这些线程是会频繁启停么? 查查是不是有分支没有 join 或 detach 这些线程!
algas
    28
algas  
OP
   2016-10-09 21:13:03 +08:00
抱歉一直在外面忙,不在电脑前面


@ryd994
貌似用 2 两个线程也算不了更大的情况,退出前 1s 左右记录到的结果如下, ps aux | grep -i pid
username pid cpu mem vsz rss
username 1991 99.9 57.4 105758172 75775140 pts/0 R+ 14:21 6:58 ./a.out 260 0.5
algas
    29
algas  
OP
   2016-10-09 21:13:09 +08:00
@xiaozhaoz
虽然我看不太明白你的意思,大概是说程序自身有问题吧?下面是 dmesg 最后一部分的内容,希望有用。
[10523563.169856] a.out[1948]: segfault at 5b9a92500 ip 00000000004034f3 sp 00002b7021521e60 error 6 in a.out[400000+5000]
[10523563.169885] a.out[1947]: segfault at 56530c140 ip 00000000004034f3 sp 00002b7021320e60 error 6 in a.out[400000+5000]
[10523563.170921] a.out[1954]: segfault at ffffffffb47b7b80 ip 00000000004034f3 sp 00002b7022127e60 error 7
[10523563.171253] a.out[1951]: segfault at 6b7125040 ip 00000000004034f3 sp 00002b7021b24e60 error 6
[10523563.171259] in a.out[400000+5000]
[10523563.171267] in a.out[400000+5000]
[10524098.407830] a.out[2857]: segfault at 20e363320 ip 00000000004034f3 sp 00002b84d78a7e60 error 6
[10524098.407838] a.out[2858]: segfault at 27b29cd20 ip 00000000004034f3 sp 00002b84d7aa8e60 error 6
[10524098.407840] in a.out[400000+5000]
[10524098.407849] a.out[2862]: segfault at 42ef83520 ip 00000000004034f3 sp 00002b84d82ace60 error 6

[10524098.407856] a.out[2856]: segfault at 1a1429920 ip 00000000004034f3 sp 00002b84d76a6e60 error 6
[10524098.407859] in a.out[400000+5000]
[10524098.407859] in a.out[400000+5000]


[10524098.407867] a.out[2863]: segfault at 49bebcf20 ip 00000000004034f3 sp 00002b84d84ade60 error 6 in a.out[400000+5000]
[10524098.407876] in a.out[400000+5000]
algas
    30
algas  
OP
   2016-10-09 21:14:53 +08:00
@stephenyin

不会频繁启动终止,这是一个计算程序,在进入多线程的部分会程序停掉,主线程里是有 join 的。
algas
    31
algas  
OP
   2016-10-09 21:17:21 +08:00
@xiaozhaoz

[10548380.539504] a.out[29535]: segfault at fffffffd08858920 ip 00000000004034f3 sp 00002bab3bb23e60 error 7 in a.out[400000+5000]

这里面的 error 7 是指 signal=7 吗?
algas
    32
algas  
OP
   2016-10-09 21:21:05 +08:00
@WKPlus

dmesg 的信息贴到上面的回复了,里面有 error 6 也有 error 7 ,这个看不懂....
xiaozhaoz
    33
xiaozhaoz  
   2016-10-09 23:08:23 +08:00 via Android
error 6,7 指的是用户空间写物理内存异常。不是 signal x

现在可以肯定,是不是 oom

你这个问题好查,每次异常的 eip 都相同,都是 0x4034f3 ,所以反汇编,根据偏移可以推算到代码,才能确切定位是什么问题。
algas
    34
algas  
OP
   2016-10-10 00:07:41 +08:00
@xiaozhaoz
谢谢你,我应该是找到问题,很低级,但是并不常有机会碰到,我放到 append 里了。
ryd994
    35
ryd994  
   2016-10-10 01:53:19 +08:00 via Android
@algas 主要是我一般没做过这么大规模的。有 segfault 找 valgrind 是我的习惯反射了。
其次就是, int 是 16 位………
long 是 32
long long 是 64
你这个地方应该用 size_t
size_t 的意思是当前环境用于表示地址范围大小,保证不溢出
ryd994
    36
ryd994  
   2016-10-10 01:55:21 +08:00 via Android
correlationMatrix
是做计算物理的前辈么?希望能交个朋友
alqaz
    37
alqaz  
   2016-10-10 09:36:36 +08:00
N*N*8=18G 多,很好奇这是哪方面使用的程序。
araraloren
    38
araraloren  
   2016-10-10 09:53:49 +08:00
@alqaz ~~楼主不是大量计算么,保存中间的计算结果什么的吧,不过一次性申请这么多内存也是醉了。。
algas
    39
algas  
OP
   2016-10-10 12:10:58 +08:00
@ryd994
基本上就是这个问题, linux C int 和 long int 都是 32 位, long long int 是 64 位。

malloc 的参数是 size_t 类型,其实就是 unsigned ,比 int 多一位可用。
所以 malloc 单次申请内存大小受到 size_t 的位数限制,我这里把 size_t 益出了。

如果需要申请连续的一大块内存,可能要研究以下 mmap 函数。

我是学统计物理的,主要对付 toy model ,和真正计算实际问题的那种计算物理不太一样。
algas
    40
algas  
OP
   2016-10-10 12:19:32 +08:00
@alqaz
@araraloren

这个是用来保存矩阵的一个数组,是完全填充的。
类似于计算晶格上的振动模式,如果考虑的系统是 200*200 个原子的正方形区域(很小对不对),
关联矩阵就需要 4e4 * 4e4 个元素,基本溢出 int 类型了。

分块计算的倒是可以避免一次申请这么大的内存,但是程序复杂性估计就要上天了 2333
ryd994
    41
ryd994  
   2016-10-10 12:25:47 +08:00 via Android
@algas mmap 不是用来分配内存的啊……… map 文件………
内存管理算法一般都不能很好的处理特大段内存分配,真的没有办法拆么?
我是学材料工程和计算机双专业,所以计算物理这边多少沾得上
ryd994
    42
ryd994  
   2016-10-10 12:49:44 +08:00 via Android
@algas 你这样写
size_t N = 220 * 220;
size_t 用于表示内存范围的时候不可能溢出,否则就违背定义了。如果我没有记错,在 64 位系统上是 unsigned long long

你的问题是: int 只有 32767 的上限, 220*220=4e4 明显超了,后面 N * N * sizeof(double) 里 N*N 先计算得中间结果,中间结果 promote 为 size_t 与 double 大小相乘后作为 malloc 的参数。第一步时,因为双方都是 int ,所以没有范围调整,所以这里也超了。

重申: size_t 不是 uint16 ,是 uint64 ,如果 size_t 溢出,则当前平台上根本不可能对该内存空间寻址,换言之根本不存在 size_t 溢出而其他方法能正常分配内存的情况。
algas
    43
algas  
OP
   2016-10-15 09:45:44 +08:00
@ryd994 你说的是对的,我之前看了以下 size_t 的类型,就没有管是不是 64 位的 unsigned 。
不过我这里 int 是 32 位的。
sizeof(int) = 4.
关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     867 人在线   最高记录 6679       Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 33ms UTC 19:22 PVG 03:22 LAX 12:22 JFK 15:22
Do have faith in what you're doing.
ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86