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?

5

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

  • +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
5

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. Then, you'll have to choose how to use it.

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))
  • 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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.