9

I needed to edit a lot of files recently for a small change in each file. To get to each file, I browsed through NERDtree and opened one file after another. As I opened more and more files, I noticed that my memory usage grew a LOT. Closing vim gave me a lot of memory back.

I have quite a few plugins installed, but I'm thinking that the reason my memory usage grew so drastically was due to the number of buffers I had open after a few edits.

Is there a way to limit the number of buffers that vim allows to be open at one time, automatically closing old buffers with respect to editing time?

3 Answers3

7

Let's solve the problem, not treat the symptoms. Vim shouldn't normally use large amounts of memory. It would be best isolate the issue. Some tips to help find the culprit:

  • Disable plugins (use a binary search to make it faster)
  • Reduce your ~/.vimrc down to see if anything in there is the problem
  • Disable your ~/.vimrc completely via vim -u NONE

Also see How do I debug my vimrc file?

If you find a memory bug with a plugin then contact the plugin developer. If you find a memory bug with Vim then submit a bug report with steps to reproduce the error. See :h bugs

Martin Tournoij
  • 62,054
  • 25
  • 192
  • 271
Peter Rincker
  • 15,854
  • 1
  • 36
  • 45
  • +1; it should be possible to open a gazillion buffers in Vim at the same time and still be good. As long as you're not viewing the buffer (in a window, or "tab") it's not loaded in memory. – Martin Tournoij Feb 25 '15 at 16:30
  • @Carpetsmoker, buffer variables and settings do not disappear when the buffer is not displayed in a window. If a plugin stores a lot of information for each buffer, as Peter suggested, then memory could be wasted (considering the end-user won't do anything more with the buffer). BTW: A plugin may not store data related to buffers in b:variables but in a s:plugin[bufid] if the plugin maintainer preferred to not pollute the public b: "namespace". In this case, deleting the buffer won't necessarily collect all related variables/memory. – Luc Hermitte Feb 25 '15 at 16:41
6

The following should answer your question.

function! s:SortTimeStamps(lhs, rhs)
  return a:lhs[1] > a:rhs[1] ?  1
     \ : a:lhs[1] < a:rhs[1] ? -1
     \ :                        0
endfunction

function! s:Close(nb_to_keep) let saved_buffers = filter(range(0, bufnr('$')), 'buflisted(v:val) && ! getbufvar(v:val, "&modified")') let times = map(copy(saved_buffers), '[(v:val), getftime(bufname(v:val))]') call filter(times, 'v:val[1] > 0') call sort(times, function('s:SortTimeStamps')) let nb_to_keep = min([a:nb_to_keep, len(times)]) let buffers_to_strip = map(copy(times[0:(nb_to_keep-1)]), 'v:val[0]') exe 'bw '.join(buffers_to_strip, ' ') endfunction

" Two ways to use it " - manually command! -nargs=1 CloseOldBuffers call s:Close(<args>) " - or automatically augroup CloseOldBuffers au! au BufNew * call s:Close(g:nb_buffers_to_keep) augroup END " and don't forget to set the option in your .vimrc let g:nb_buffers_to_keep = 42

This is to be dropped into a plugin or in your vimrc config. Then, you'll have to choose how to use it.

Luc Hermitte
  • 17,351
  • 1
  • 33
  • 49
3

I am not sure how to get the oldest buffers with respect to editing time, but one could, instead, try to close the oldest unedited buffers. Something like:

function CloseLast ()
    python <<EOF
import vim
N = 10
listed_buffers = [b for b in vim.buffers if b.options['buflisted'] and not b.options['modified']]
for i in range (0, len (listed_buffers) - N):
    vim.command (':bd' + str (listed_buffers[i].number))
EOF
endfunction

autocmd BufNew * call CloseLast()

Notes:

  • vim.buffers is a list of every buffer opened in the current session, so it also includes unlisted buffers. It is not the same as the list returned by :ls.
  • Therefore, we must filter out the hidden or deleted buffers. This can be checked using options['buflisted'].
  • Similarly, options['modified'] lets us check if the buffer is modified.
  • N is the number of unmodified, listed buffers you want open.

Thanks to Luc Hermitte's answer from which I learnt how to get the timestamps, you could use the following instead, to get the oldest inactive kicked out first:

listed_buffers = (b for b in vim.buffers if b.options['buflisted'] and not b.options['modified'])
oldest_buffers = sorted (listed_buffers, key = lambda b: eval('getftime("' + b.name + '")'))
for i in range (0, len (oldest_buffers) - N):
    vim.command (':bd' + str (oldest_buffers[i].number))
muru
  • 24,838
  • 8
  • 82
  • 143
  • 1
    You don't need python. Vim is more than enough: :let buffers = filter(range(0, bufnr('$')), 'buflisted(v:val) && ! getbufvar(v:val, "&modified")') + :exe 'bw '.join(buffers, ' ') – Luc Hermitte Feb 25 '15 at 10:58
  • @LucHermitte True, but it's not a question of need. I'm just not familiar enough with Vimscript. IIRC bw's help says you shouldn't use it "unless you know what you're doing". I don't. :) – muru Feb 25 '15 at 11:06
  • Old habits. I always use :bw, and never :bd. I've never seen the point of deleting almost everything from a buffer, but not actually everything. – Luc Hermitte Feb 25 '15 at 14:13