Mercurial > vim
comparison src/testdir/test_memory_usage.vim @ 16003:879829e44091 v8.1.1007
patch 8.1.1007: using closure may consume a lot of memory
commit https://github.com/vim/vim/commit/209b8e3e3bf7a4a3d102134124120f6c7f57d560
Author: Bram Moolenaar <Bram@vim.org>
Date: Thu Mar 14 13:43:24 2019 +0100
patch 8.1.1007: using closure may consume a lot of memory
Problem: Using closure may consume a lot of memory.
Solution: unreference items that are no longer needed. Add a test. (Ozaki
Kiichi, closes #3961)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Thu, 14 Mar 2019 13:45:06 +0100 |
parents | |
children | f4a206d7a04d |
comparison
equal
deleted
inserted
replaced
16002:7834d7d14b48 | 16003:879829e44091 |
---|---|
1 " Tests for memory usage. | |
2 | |
3 if !has('terminal') || has('gui_running') || $ASAN_OPTIONS !=# '' | |
4 " Skip tests on Travis CI ASAN build because it's difficult to estimate | |
5 " memory usage. | |
6 finish | |
7 endif | |
8 | |
9 source shared.vim | |
10 | |
11 func s:pick_nr(str) abort | |
12 return substitute(a:str, '[^0-9]', '', 'g') * 1 | |
13 endfunc | |
14 | |
15 if has('win32') | |
16 if !executable('wmic') | |
17 finish | |
18 endif | |
19 func s:memory_usage(pid) abort | |
20 let cmd = printf('wmic process where processid=%d get WorkingSetSize', a:pid) | |
21 return s:pick_nr(system(cmd)) / 1024 | |
22 endfunc | |
23 elseif has('unix') | |
24 if !executable('ps') | |
25 finish | |
26 endif | |
27 func s:memory_usage(pid) abort | |
28 return s:pick_nr(system('ps -o rss= -p ' . a:pid)) | |
29 endfunc | |
30 else | |
31 finish | |
32 endif | |
33 | |
34 " Wait for memory usage to level off. | |
35 func s:monitor_memory_usage(pid) abort | |
36 let proc = {} | |
37 let proc.pid = a:pid | |
38 let proc.hist = [] | |
39 let proc.min = 0 | |
40 let proc.max = 0 | |
41 | |
42 func proc.op() abort | |
43 " Check the last 200ms. | |
44 let val = s:memory_usage(self.pid) | |
45 if self.min > val | |
46 let self.min = val | |
47 elseif self.max < val | |
48 let self.max = val | |
49 endif | |
50 call add(self.hist, val) | |
51 if len(self.hist) < 20 | |
52 return 0 | |
53 endif | |
54 let sample = remove(self.hist, 0) | |
55 return len(uniq([sample] + self.hist)) == 1 | |
56 endfunc | |
57 | |
58 call WaitFor({-> proc.op()}, 10000) | |
59 return {'last': get(proc.hist, -1), 'min': proc.min, 'max': proc.max} | |
60 endfunc | |
61 | |
62 let s:term_vim = {} | |
63 | |
64 func s:term_vim.start(...) abort | |
65 let self.buf = term_start([GetVimProg()] + a:000) | |
66 let self.job = term_getjob(self.buf) | |
67 call WaitFor({-> job_status(self.job) ==# 'run'}) | |
68 let self.pid = job_info(self.job).process | |
69 endfunc | |
70 | |
71 func s:term_vim.stop() abort | |
72 call term_sendkeys(self.buf, ":qall!\<CR>") | |
73 call WaitFor({-> job_status(self.job) ==# 'dead'}) | |
74 exe self.buf . 'bwipe!' | |
75 endfunc | |
76 | |
77 func s:vim_new() abort | |
78 return copy(s:term_vim) | |
79 endfunc | |
80 | |
81 func Test_memory_func_capture_vargs() | |
82 " Case: if a local variable captures a:000, funccall object will be free | |
83 " just after it finishes. | |
84 let testfile = 'Xtest.vim' | |
85 call writefile([ | |
86 \ 'func s:f(...)', | |
87 \ ' let x = a:000', | |
88 \ 'endfunc', | |
89 \ 'for _ in range(10000)', | |
90 \ ' call s:f(0)', | |
91 \ 'endfor', | |
92 \ ], testfile) | |
93 | |
94 let vim = s:vim_new() | |
95 call vim.start('--clean', '-c', 'set noswapfile', testfile) | |
96 let before = s:monitor_memory_usage(vim.pid).last | |
97 | |
98 call term_sendkeys(vim.buf, ":so %\<CR>") | |
99 call WaitFor({-> term_getcursor(vim.buf)[0] == 1}) | |
100 let after = s:monitor_memory_usage(vim.pid) | |
101 | |
102 " Estimate the limit of max usage as 2x initial usage. | |
103 call assert_inrange(before, 2 * before, after.max) | |
104 " In this case, garbase collecting is not needed. | |
105 call assert_equal(after.last, after.max) | |
106 | |
107 call vim.stop() | |
108 call delete(testfile) | |
109 endfunc | |
110 | |
111 func Test_memory_func_capture_lvars() | |
112 " Case: if a local variable captures l: dict, funccall object will not be | |
113 " free until garbage collector runs, but after that memory usage doesn't | |
114 " increase so much even when rerun Xtest.vim since system memory caches. | |
115 let testfile = 'Xtest.vim' | |
116 call writefile([ | |
117 \ 'func s:f()', | |
118 \ ' let x = l:', | |
119 \ 'endfunc', | |
120 \ 'for _ in range(10000)', | |
121 \ ' call s:f()', | |
122 \ 'endfor', | |
123 \ ], testfile) | |
124 | |
125 let vim = s:vim_new() | |
126 call vim.start('--clean', '-c', 'set noswapfile', testfile) | |
127 let before = s:monitor_memory_usage(vim.pid).last | |
128 | |
129 call term_sendkeys(vim.buf, ":so %\<CR>") | |
130 call WaitFor({-> term_getcursor(vim.buf)[0] == 1}) | |
131 let after = s:monitor_memory_usage(vim.pid) | |
132 | |
133 " Rerun Xtest.vim. | |
134 for _ in range(3) | |
135 call term_sendkeys(vim.buf, ":so %\<CR>") | |
136 call WaitFor({-> term_getcursor(vim.buf)[0] == 1}) | |
137 let last = s:monitor_memory_usage(vim.pid).last | |
138 endfor | |
139 | |
140 call assert_inrange(before, after.max + (after.last - before), last) | |
141 | |
142 call vim.stop() | |
143 call delete(testfile) | |
144 endfunc |