2003-09-04 20:36:47 -04:00
|
|
|
require 'test/unit'
|
|
|
|
|
2011-09-30 21:00:40 -04:00
|
|
|
require_relative "envutil"
|
|
|
|
|
2003-09-04 20:36:47 -04:00
|
|
|
class TestGc < Test::Unit::TestCase
|
|
|
|
class S
|
|
|
|
def initialize(a)
|
|
|
|
@a = a
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_gc
|
2006-12-31 10:02:22 -05:00
|
|
|
prev_stress = GC.stress
|
|
|
|
GC.stress = false
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2003-09-05 11:15:43 -04:00
|
|
|
assert_nothing_raised do
|
2003-09-04 20:36:47 -04:00
|
|
|
1.upto(10000) {
|
|
|
|
tmp = [0,1,2,3,4,5,6,7,8,9]
|
|
|
|
}
|
|
|
|
tmp = nil
|
|
|
|
end
|
|
|
|
l=nil
|
|
|
|
100000.times {
|
|
|
|
l = S.new(l)
|
|
|
|
}
|
|
|
|
GC.start
|
|
|
|
assert true # reach here or dumps core
|
|
|
|
l = []
|
|
|
|
100000.times {
|
|
|
|
l.push([l])
|
|
|
|
}
|
|
|
|
GC.start
|
|
|
|
assert true # reach here or dumps core
|
2006-12-31 10:02:22 -05:00
|
|
|
|
|
|
|
GC.stress = prev_stress
|
2003-09-04 20:36:47 -04:00
|
|
|
end
|
2008-06-05 10:57:05 -04:00
|
|
|
|
2014-06-30 02:14:37 -04:00
|
|
|
def use_rgengc?
|
|
|
|
GC::OPTS.include? 'USE_RGENGC'.freeze
|
|
|
|
end
|
|
|
|
|
2008-06-05 10:57:05 -04:00
|
|
|
def test_enable_disable
|
|
|
|
GC.enable
|
|
|
|
assert_equal(false, GC.enable)
|
|
|
|
assert_equal(false, GC.disable)
|
|
|
|
assert_equal(true, GC.disable)
|
|
|
|
assert_equal(true, GC.disable)
|
|
|
|
assert_nil(GC.start)
|
|
|
|
assert_equal(true, GC.enable)
|
|
|
|
assert_equal(false, GC.enable)
|
|
|
|
ensure
|
|
|
|
GC.enable
|
|
|
|
end
|
|
|
|
|
2013-12-06 00:11:51 -05:00
|
|
|
def test_start_full_mark
|
2014-06-30 02:14:37 -04:00
|
|
|
return unless use_rgengc?
|
|
|
|
|
2013-12-06 00:11:51 -05:00
|
|
|
GC.start(full_mark: false)
|
|
|
|
assert_nil GC.latest_gc_info(:major_by)
|
|
|
|
|
|
|
|
GC.start(full_mark: true)
|
|
|
|
assert_not_nil GC.latest_gc_info(:major_by)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_start_immediate_sweep
|
|
|
|
GC.start(immediate_sweep: false)
|
|
|
|
assert_equal false, GC.latest_gc_info(:immediate_sweep)
|
|
|
|
|
|
|
|
GC.start(immediate_sweep: true)
|
|
|
|
assert_equal true, GC.latest_gc_info(:immediate_sweep)
|
|
|
|
end
|
|
|
|
|
2008-06-05 10:57:05 -04:00
|
|
|
def test_count
|
|
|
|
c = GC.count
|
|
|
|
GC.start
|
|
|
|
assert_operator(c, :<, GC.count)
|
|
|
|
end
|
2010-10-27 15:02:24 -04:00
|
|
|
|
|
|
|
def test_stat
|
|
|
|
res = GC.stat
|
|
|
|
assert_equal(false, res.empty?)
|
|
|
|
assert_kind_of(Integer, res[:count])
|
|
|
|
|
|
|
|
arg = Hash.new
|
|
|
|
res = GC.stat(arg)
|
|
|
|
assert_equal(arg, res)
|
|
|
|
assert_equal(false, res.empty?)
|
|
|
|
assert_kind_of(Integer, res[:count])
|
2013-03-18 11:30:02 -04:00
|
|
|
|
|
|
|
stat, count = {}, {}
|
|
|
|
GC.start
|
|
|
|
GC.stat(stat)
|
|
|
|
ObjectSpace.count_objects(count)
|
2014-09-09 21:48:23 -04:00
|
|
|
assert_equal(count[:TOTAL]-count[:FREE], stat[:heap_live_slots])
|
|
|
|
assert_equal(count[:FREE], stat[:heap_free_slots])
|
2013-10-04 06:05:40 -04:00
|
|
|
|
|
|
|
# measure again without GC.start
|
|
|
|
1000.times{ "a" + "b" }
|
|
|
|
GC.stat(stat)
|
|
|
|
ObjectSpace.count_objects(count)
|
2014-09-09 21:48:23 -04:00
|
|
|
assert_equal(count[:FREE], stat[:heap_free_slots])
|
2010-10-27 15:02:24 -04:00
|
|
|
end
|
2010-12-30 09:17:32 -05:00
|
|
|
|
2014-03-30 17:55:59 -04:00
|
|
|
def test_stat_argument
|
|
|
|
assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) {GC.stat(:"\u{30eb 30d3 30fc}")}
|
|
|
|
end
|
|
|
|
|
2013-12-05 02:45:13 -05:00
|
|
|
def test_stat_single
|
|
|
|
stat = GC.stat
|
|
|
|
assert_equal stat[:count], GC.stat(:count)
|
|
|
|
assert_raise(ArgumentError){ GC.stat(:invalid) }
|
|
|
|
end
|
|
|
|
|
2014-09-09 22:47:05 -04:00
|
|
|
def test_stat_constraints
|
|
|
|
stat = GC.stat
|
|
|
|
assert_equal stat[:total_allocated_pages], stat[:heap_allocated_pages] + stat[:total_freed_pages]
|
2014-09-10 03:24:24 -04:00
|
|
|
assert_operator stat[:heap_sorted_length], :>=, stat[:heap_eden_pages] + stat[:heap_allocatable_pages], "stat is: " + stat.inspect
|
2014-09-09 22:47:05 -04:00
|
|
|
assert_equal stat[:heap_available_slots], stat[:heap_live_slots] + stat[:heap_free_slots] + stat[:heap_final_slots]
|
|
|
|
assert_equal stat[:heap_live_slots], stat[:total_allocated_objects] - stat[:total_freed_objects] - stat[:heap_final_slots]
|
2014-09-10 03:24:24 -04:00
|
|
|
assert_equal stat[:heap_allocated_pages], stat[:heap_eden_pages] + stat[:heap_tomb_pages]
|
2014-09-09 22:47:05 -04:00
|
|
|
|
|
|
|
if use_rgengc?
|
|
|
|
assert_equal stat[:count], stat[:major_gc_count] + stat[:minor_gc_count]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-12-05 05:30:38 -05:00
|
|
|
def test_latest_gc_info
|
2013-11-30 16:02:00 -05:00
|
|
|
GC.start
|
2014-09-09 21:48:23 -04:00
|
|
|
count = GC.stat(:heap_free_slots) + GC.stat(:heap_allocatable_pages) * GC::INTERNAL_CONSTANTS[:HEAP_OBJ_LIMIT]
|
2014-06-03 05:47:54 -04:00
|
|
|
count.times{ "a" + "b" }
|
2013-12-05 05:30:38 -05:00
|
|
|
assert_equal :newobj, GC.latest_gc_info[:gc_by]
|
2013-11-28 02:40:35 -05:00
|
|
|
|
|
|
|
GC.start
|
2014-07-24 07:13:19 -04:00
|
|
|
assert_equal :force, GC.latest_gc_info[:major_by] if use_rgengc?
|
2013-12-05 05:30:38 -05:00
|
|
|
assert_equal :method, GC.latest_gc_info[:gc_by]
|
|
|
|
assert_equal true, GC.latest_gc_info[:immediate_sweep]
|
2013-11-28 02:40:35 -05:00
|
|
|
|
|
|
|
GC.stress = true
|
2014-07-24 07:13:19 -04:00
|
|
|
assert_equal :force, GC.latest_gc_info[:major_by]
|
2013-11-28 02:40:35 -05:00
|
|
|
ensure
|
|
|
|
GC.stress = false
|
|
|
|
end
|
|
|
|
|
2013-12-05 05:30:38 -05:00
|
|
|
def test_latest_gc_info_argument
|
|
|
|
info = {}
|
|
|
|
GC.latest_gc_info(info)
|
|
|
|
|
|
|
|
assert_not_empty info
|
|
|
|
assert_equal info[:gc_by], GC.latest_gc_info(:gc_by)
|
|
|
|
assert_raises(ArgumentError){ GC.latest_gc_info(:invalid) }
|
2014-03-30 17:55:59 -04:00
|
|
|
assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) {GC.latest_gc_info(:"\u{30eb 30d3 30fc}")}
|
2013-12-05 05:30:38 -05:00
|
|
|
end
|
|
|
|
|
2010-12-30 09:17:32 -05:00
|
|
|
def test_singleton_method
|
2012-04-17 23:38:09 -04:00
|
|
|
assert_in_out_err(%w[--disable-gems], <<-EOS, [], [], "[ruby-dev:42832]")
|
2010-12-30 09:17:32 -05:00
|
|
|
GC.stress = true
|
|
|
|
10.times do
|
|
|
|
obj = Object.new
|
|
|
|
def obj.foo() end
|
|
|
|
def obj.bar() raise "obj.foo is called, but this is obj.bar" end
|
|
|
|
obj.foo
|
|
|
|
end
|
2012-04-17 23:38:09 -04:00
|
|
|
EOS
|
2010-12-30 09:17:32 -05:00
|
|
|
end
|
2011-09-30 21:00:40 -04:00
|
|
|
|
2012-05-27 23:12:33 -04:00
|
|
|
def test_singleton_method_added
|
|
|
|
assert_in_out_err(%w[--disable-gems], <<-EOS, [], [], "[ruby-dev:44436]")
|
|
|
|
class BasicObject
|
2012-05-28 20:52:41 -04:00
|
|
|
undef singleton_method_added
|
2012-05-27 23:12:33 -04:00
|
|
|
def singleton_method_added(mid)
|
|
|
|
raise
|
|
|
|
end
|
|
|
|
end
|
|
|
|
b = proc {}
|
|
|
|
class << b; end
|
|
|
|
b.clone rescue nil
|
|
|
|
GC.start
|
|
|
|
EOS
|
|
|
|
end
|
|
|
|
|
2011-09-30 21:00:40 -04:00
|
|
|
def test_gc_parameter
|
|
|
|
env = {
|
|
|
|
"RUBY_GC_MALLOC_LIMIT" => "60000000",
|
2013-11-22 22:33:10 -05:00
|
|
|
"RUBY_GC_HEAP_INIT_SLOTS" => "100000"
|
2011-09-30 21:00:40 -04:00
|
|
|
}
|
2011-10-03 11:02:58 -04:00
|
|
|
assert_normal_exit("exit", "[ruby-core:39777]", :child_env => env)
|
|
|
|
|
|
|
|
env = {
|
2011-10-03 21:25:04 -04:00
|
|
|
"RUBYOPT" => "",
|
2013-11-22 22:33:10 -05:00
|
|
|
"RUBY_GC_HEAP_INIT_SLOTS" => "100000"
|
2011-10-03 11:02:58 -04:00
|
|
|
}
|
|
|
|
assert_in_out_err([env, "-e", "exit"], "", [], [], "[ruby-core:39795]")
|
2011-10-03 22:44:55 -04:00
|
|
|
assert_in_out_err([env, "-W0", "-e", "exit"], "", [], [], "[ruby-core:39795]")
|
|
|
|
assert_in_out_err([env, "-W1", "-e", "exit"], "", [], [], "[ruby-core:39795]")
|
2013-11-22 22:33:10 -05:00
|
|
|
assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_HEAP_INIT_SLOTS=100000/, "[ruby-core:39795]")
|
2013-03-13 10:52:00 -04:00
|
|
|
|
|
|
|
env = {
|
2013-11-22 22:33:10 -05:00
|
|
|
"RUBY_GC_HEAP_GROWTH_FACTOR" => "2.0",
|
|
|
|
"RUBY_GC_HEAP_GROWTH_MAX_SLOTS" => "10000"
|
2013-03-13 10:52:00 -04:00
|
|
|
}
|
|
|
|
assert_normal_exit("exit", "", :child_env => env)
|
2013-11-22 22:33:10 -05:00
|
|
|
assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_HEAP_GROWTH_FACTOR=2.0/, "")
|
|
|
|
assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_HEAP_GROWTH_MAX_SLOTS=10000/, "[ruby-core:57928]")
|
|
|
|
|
2014-02-16 22:27:13 -05:00
|
|
|
env = {
|
|
|
|
"RUBY_GC_HEAP_INIT_SLOTS" => "100000",
|
|
|
|
"RUBY_GC_HEAP_FREE_SLOTS" => "10000",
|
|
|
|
"RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR" => "0.9",
|
|
|
|
}
|
|
|
|
assert_normal_exit("exit", "", :child_env => env)
|
|
|
|
assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR=0\.9/, "")
|
2014-06-30 02:14:37 -04:00
|
|
|
|
2014-02-16 22:27:13 -05:00
|
|
|
# always full GC when RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR < 1.0
|
2014-06-30 02:14:37 -04:00
|
|
|
assert_in_out_err([env, "-e", "1000_000.times{Object.new}; p(GC.stat[:minor_gc_count] < GC.stat[:major_gc_count])"], "", ['true'], //, "") if use_rgengc?
|
2014-02-16 22:27:13 -05:00
|
|
|
|
2013-11-22 22:33:10 -05:00
|
|
|
# check obsolete
|
|
|
|
assert_in_out_err([{'RUBY_FREE_MIN' => '100'}, '-w', '-eexit'], '', [],
|
|
|
|
/RUBY_FREE_MIN is obsolete. Use RUBY_GC_HEAP_FREE_SLOTS instead/)
|
|
|
|
assert_in_out_err([{'RUBY_HEAP_MIN_SLOTS' => '100'}, '-w', '-eexit'], '', [],
|
|
|
|
/RUBY_HEAP_MIN_SLOTS is obsolete. Use RUBY_GC_HEAP_INIT_SLOTS instead/)
|
2013-09-27 05:36:48 -04:00
|
|
|
|
|
|
|
env = {
|
|
|
|
"RUBY_GC_MALLOC_LIMIT" => "60000000",
|
|
|
|
"RUBY_GC_MALLOC_LIMIT_MAX" => "160000000",
|
|
|
|
"RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR" => "2.0"
|
|
|
|
}
|
|
|
|
assert_normal_exit("exit", "", :child_env => env)
|
|
|
|
assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_MALLOC_LIMIT=6000000/, "")
|
|
|
|
assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_MALLOC_LIMIT_MAX=16000000/, "")
|
|
|
|
assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR=2.0/, "")
|
2013-11-22 22:33:10 -05:00
|
|
|
|
2014-06-30 02:14:37 -04:00
|
|
|
if use_rgengc?
|
|
|
|
env = {
|
|
|
|
"RUBY_GC_OLDMALLOC_LIMIT" => "60000000",
|
|
|
|
"RUBY_GC_OLDMALLOC_LIMIT_MAX" => "160000000",
|
|
|
|
"RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR" => "2.0"
|
|
|
|
}
|
|
|
|
assert_normal_exit("exit", "", :child_env => env)
|
|
|
|
assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_OLDMALLOC_LIMIT=6000000/, "")
|
|
|
|
assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_OLDMALLOC_LIMIT_MAX=16000000/, "")
|
|
|
|
assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR=2.0/, "")
|
|
|
|
end
|
2011-09-30 21:00:40 -04:00
|
|
|
end
|
2012-08-01 22:47:12 -04:00
|
|
|
|
|
|
|
def test_profiler_enabled
|
|
|
|
GC::Profiler.enable
|
|
|
|
assert_equal(true, GC::Profiler.enabled?)
|
|
|
|
GC::Profiler.disable
|
|
|
|
assert_equal(false, GC::Profiler.enabled?)
|
|
|
|
ensure
|
|
|
|
GC::Profiler.disable
|
|
|
|
end
|
2012-11-21 08:15:10 -05:00
|
|
|
|
|
|
|
def test_profiler_clear
|
2012-12-13 20:38:12 -05:00
|
|
|
assert_separately %w[--disable-gem], __FILE__, __LINE__, <<-'eom'
|
2012-11-21 08:15:10 -05:00
|
|
|
GC::Profiler.enable
|
|
|
|
|
|
|
|
GC.start
|
|
|
|
assert_equal(1, GC::Profiler.raw_data.size)
|
2012-11-21 18:09:46 -05:00
|
|
|
GC::Profiler.clear
|
2012-11-21 08:15:10 -05:00
|
|
|
assert_equal(0, GC::Profiler.raw_data.size)
|
|
|
|
|
|
|
|
200.times{ GC.start }
|
|
|
|
assert_equal(200, GC::Profiler.raw_data.size)
|
2012-11-21 18:09:46 -05:00
|
|
|
GC::Profiler.clear
|
2012-11-21 08:15:10 -05:00
|
|
|
assert_equal(0, GC::Profiler.raw_data.size)
|
2012-12-13 20:38:12 -05:00
|
|
|
eom
|
2012-11-21 08:15:10 -05:00
|
|
|
end
|
2012-11-29 17:56:02 -05:00
|
|
|
|
2013-10-28 22:11:26 -04:00
|
|
|
def test_profiler_total_time
|
|
|
|
GC::Profiler.enable
|
|
|
|
GC::Profiler.clear
|
|
|
|
|
|
|
|
GC.start
|
2014-01-15 09:36:51 -05:00
|
|
|
assert_operator(GC::Profiler.total_time, :>=, 0)
|
2013-10-28 22:11:26 -04:00
|
|
|
ensure
|
|
|
|
GC::Profiler.disable
|
|
|
|
end
|
|
|
|
|
2012-11-29 17:56:02 -05:00
|
|
|
def test_finalizing_main_thread
|
|
|
|
assert_in_out_err(%w[--disable-gems], <<-EOS, ["\"finalize\""], [], "[ruby-dev:46647]")
|
|
|
|
ObjectSpace.define_finalizer(Thread.main) { p 'finalize' }
|
|
|
|
EOS
|
|
|
|
end
|
2013-03-18 08:44:55 -04:00
|
|
|
|
|
|
|
def test_expand_heap
|
|
|
|
assert_separately %w[--disable-gem], __FILE__, __LINE__, <<-'eom'
|
|
|
|
GC.start
|
2014-09-09 05:33:52 -04:00
|
|
|
base_length = GC.stat[:heap_eden_pages]
|
2013-11-27 02:07:59 -05:00
|
|
|
(base_length * 500).times{ 'a' }
|
2013-11-27 01:09:55 -05:00
|
|
|
GC.start
|
2014-09-09 05:33:52 -04:00
|
|
|
assert_in_delta base_length, (v = GC.stat[:heap_eden_pages]), 1,
|
|
|
|
"invalid heap expanding (base_length: #{base_length}, GC.stat[:heap_eden_pages]: #{v})"
|
2013-03-18 08:44:55 -04:00
|
|
|
|
|
|
|
a = []
|
2013-11-27 01:09:55 -05:00
|
|
|
(base_length * 500).times{ a << 'a'; nil }
|
2013-03-18 08:44:55 -04:00
|
|
|
GC.start
|
2014-09-09 05:33:52 -04:00
|
|
|
assert_operator base_length, :<, GC.stat[:heap_eden_pages] + 1
|
2013-03-18 08:44:55 -04:00
|
|
|
eom
|
|
|
|
end
|
2013-11-27 01:57:14 -05:00
|
|
|
|
|
|
|
def test_gc_internals
|
|
|
|
assert_not_nil GC::INTERNAL_CONSTANTS[:HEAP_OBJ_LIMIT]
|
|
|
|
assert_not_nil GC::INTERNAL_CONSTANTS[:RVALUE_SIZE]
|
|
|
|
end
|
2013-12-04 20:47:12 -05:00
|
|
|
|
|
|
|
def test_sweep_in_finalizer
|
|
|
|
bug9205 = '[ruby-core:58833] [Bug #9205]'
|
2013-12-05 00:35:34 -05:00
|
|
|
2.times do
|
2014-04-09 01:45:01 -04:00
|
|
|
assert_ruby_status([], <<-'end;', bug9205, timeout: 60)
|
2013-12-04 20:47:12 -05:00
|
|
|
raise_proc = proc do |id|
|
|
|
|
GC.start
|
|
|
|
end
|
|
|
|
1000.times do
|
|
|
|
ObjectSpace.define_finalizer(Object.new, raise_proc)
|
|
|
|
end
|
|
|
|
end;
|
|
|
|
end
|
|
|
|
end
|
2013-12-15 23:12:48 -05:00
|
|
|
|
2013-12-17 02:19:25 -05:00
|
|
|
def test_exception_in_finalizer
|
|
|
|
bug9168 = '[ruby-core:58652] [Bug #9168]'
|
|
|
|
assert_normal_exit(<<-'end;', bug9168)
|
|
|
|
raise_proc = proc {raise}
|
|
|
|
10000.times do
|
|
|
|
ObjectSpace.define_finalizer(Object.new, raise_proc)
|
|
|
|
Thread.handle_interrupt(RuntimeError => :immediate) {break}
|
|
|
|
Thread.handle_interrupt(RuntimeError => :on_blocking) {break}
|
|
|
|
Thread.handle_interrupt(RuntimeError => :never) {break}
|
|
|
|
end
|
|
|
|
end;
|
|
|
|
end
|
|
|
|
|
2013-12-15 23:12:48 -05:00
|
|
|
def test_verify_internal_consistency
|
|
|
|
assert_nil(GC.verify_internal_consistency)
|
|
|
|
end
|
2014-06-10 12:55:32 -04:00
|
|
|
|
|
|
|
def test_gc_stress_on_realloc
|
|
|
|
assert_normal_exit(<<-'end;', '[Bug #9859]')
|
|
|
|
class C
|
|
|
|
def initialize
|
|
|
|
@a = nil
|
|
|
|
@b = nil
|
|
|
|
@c = nil
|
|
|
|
@d = nil
|
|
|
|
@e = nil
|
|
|
|
@f = nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
GC.stress = true
|
|
|
|
C.new
|
|
|
|
end;
|
|
|
|
end
|
2003-09-04 20:36:47 -04:00
|
|
|
end
|