DAddYE / leveldb

LevelDB for Ruby (embedded)
http://daddye.it/leveldb
MIT License
126 stars 15 forks source link

Memory leak on basic operations #8

Open dinedal opened 11 years ago

dinedal commented 11 years ago
require 'leveldb'

db = LevelDB::DB.new("/tmp/leaktest")

GC.start
puts GC.stat

10000.times do
  db.get('foo')
  GC.start
end

puts GC.stat

Sample output:

{:count=>7, :heap_used=>897, :heap_length=>4422, :heap_increment=>1964, :heap_live_num=>28158, :heap_free_num=>336751, :heap_final_num=>0, :total_allocated_object=>207656, :total_freed_object=>179498}
{:count=>10007, :heap_used=>620, :heap_length=>4422, :heap_increment=>1964, :heap_live_num=>28165, :heap_free_num=>224051, :heap_final_num=>0, :total_allocated_object=>367680, :total_freed_object=>339515}

Just for db.get on a nonexistant key 10,000 times, looks like there were 160024 allocated objects that can not get freed

DAddYE commented 11 years ago

Can you confirm that with the latest release are we all good?

atomical commented 10 years ago

I'm still seeing a leak. Is there a pointer that isn't being free'd?

abargnesi commented 10 years ago

This GC test is incorrect. The total_freed_object count increases proportional to the total_allocated_object count indicating that allocated objects are being collected.

Also keep in mind that GC.start will not execute a full mark and sweep unless you run GC.start(full_mark: true, immediate_sweep: true).

If you use ObjectSpace.count_objects you can see proper garbage collection. Going further you can dump all objects using ObjectSpace.dump_all(output: open('heap.json', 'w')) and you will see only a handle full of differences.

#!/usr/bin/env ruby

require 'leveldb'
require 'objspace'

db = LevelDB::DB.new("/tmp/leaktest")

GC.start(full_mark: true, immediate_sweep: true)
puts ObjectSpace.count_objects
# {
#     :TOTAL=>92121,
#     :FREE=>44550,
#     :T_OBJECT=>4539,
#     :T_CLASS=>1549,
#     :T_MODULE=>88,
#     :T_FLOAT=>6,
#     :T_STRING=>25147,
#     :T_REGEXP=>342,
#     :T_ARRAY=>8794,
#     :T_HASH=>387,
#     :T_STRUCT=>34,
#     :T_BIGNUM=>2,
#     :T_FILE=>3,
#     :T_DATA=>4074,
#     :T_MATCH=>1,
#     :T_COMPLEX=>1,
#     :T_RATIONAL=>1,
#     :T_NODE=>2469,
#     :T_ICLASS=>134
# }

100000.times do
  db.get('foo')
end

GC.start(full_mark: true, immediate_sweep: true)
puts ObjectSpace.count_objects
# {
#     :TOTAL=>92121,
#     :FREE=>44528,
#     :T_OBJECT=>4539,
#     :T_CLASS=>1549,
#     :T_MODULE=>88,
#     :T_FLOAT=>6,
#     :T_STRING=>25171,
#     :T_REGEXP=>341,
#     :T_ARRAY=>8793,
#     :T_HASH=>389,
#     :T_STRUCT=>34,
#     :T_BIGNUM=>2,
#     :T_FILE=>3,
#     :T_DATA=>4074,
#     :T_MATCH=>1,
#     :T_COMPLEX=>1,
#     :T_RATIONAL=>1,
#     :T_NODE=>2467,
#     :T_ICLASS=>134
# }