PageRenderTime 85ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/test/ruby/test_gc.rb

https://github.com/luke-gru/ruby
Ruby | 454 lines | 394 code | 56 blank | 4 comment | 6 complexity | cfef37269899fe7e7844edabd5077286 MD5 | raw file
  1. # frozen_string_literal: false
  2. require 'test/unit'
  3. class TestGc < Test::Unit::TestCase
  4. class S
  5. def initialize(a)
  6. @a = a
  7. end
  8. end
  9. def test_gc
  10. prev_stress = GC.stress
  11. GC.stress = false
  12. assert_nothing_raised do
  13. tmp = nil
  14. 1.upto(10000) {
  15. tmp = [0,1,2,3,4,5,6,7,8,9]
  16. }
  17. end
  18. l=nil
  19. 100000.times {
  20. l = S.new(l)
  21. }
  22. GC.start
  23. assert true # reach here or dumps core
  24. l = []
  25. 100000.times {
  26. l.push([l])
  27. }
  28. GC.start
  29. assert true # reach here or dumps core
  30. GC.stress = prev_stress
  31. end
  32. def use_rgengc?
  33. GC::OPTS.include? 'USE_RGENGC'.freeze
  34. end
  35. def test_enable_disable
  36. GC.enable
  37. assert_equal(false, GC.enable)
  38. assert_equal(false, GC.disable)
  39. assert_equal(true, GC.disable)
  40. assert_equal(true, GC.disable)
  41. assert_nil(GC.start)
  42. assert_equal(true, GC.enable)
  43. assert_equal(false, GC.enable)
  44. ensure
  45. GC.enable
  46. end
  47. def test_start_full_mark
  48. return unless use_rgengc?
  49. GC.start(full_mark: false)
  50. assert_nil GC.latest_gc_info(:major_by)
  51. GC.start(full_mark: true)
  52. assert_not_nil GC.latest_gc_info(:major_by)
  53. end
  54. def test_start_immediate_sweep
  55. GC.start(immediate_sweep: false)
  56. assert_equal false, GC.latest_gc_info(:immediate_sweep)
  57. GC.start(immediate_sweep: true)
  58. assert_equal true, GC.latest_gc_info(:immediate_sweep)
  59. end
  60. def test_count
  61. c = GC.count
  62. GC.start
  63. assert_operator(c, :<, GC.count)
  64. end
  65. def test_stat
  66. res = GC.stat
  67. assert_equal(false, res.empty?)
  68. assert_kind_of(Integer, res[:count])
  69. arg = Hash.new
  70. res = GC.stat(arg)
  71. assert_equal(arg, res)
  72. assert_equal(false, res.empty?)
  73. assert_kind_of(Integer, res[:count])
  74. stat, count = {}, {}
  75. GC.start
  76. GC.stat(stat)
  77. ObjectSpace.count_objects(count)
  78. assert_equal(count[:TOTAL]-count[:FREE], stat[:heap_live_slots])
  79. assert_equal(count[:FREE], stat[:heap_free_slots])
  80. # measure again without GC.start
  81. 1000.times{ "a" + "b" }
  82. GC.stat(stat)
  83. ObjectSpace.count_objects(count)
  84. assert_equal(count[:FREE], stat[:heap_free_slots])
  85. end
  86. def test_stat_argument
  87. assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) {GC.stat(:"\u{30eb 30d3 30fc}")}
  88. end
  89. def test_stat_single
  90. stat = GC.stat
  91. assert_equal stat[:count], GC.stat(:count)
  92. assert_raise(ArgumentError){ GC.stat(:invalid) }
  93. end
  94. def test_stat_constraints
  95. stat = GC.stat
  96. assert_equal stat[:total_allocated_pages], stat[:heap_allocated_pages] + stat[:total_freed_pages]
  97. assert_operator stat[:heap_sorted_length], :>=, stat[:heap_eden_pages] + stat[:heap_allocatable_pages], "stat is: " + stat.inspect
  98. assert_equal stat[:heap_available_slots], stat[:heap_live_slots] + stat[:heap_free_slots] + stat[:heap_final_slots]
  99. assert_equal stat[:heap_live_slots], stat[:total_allocated_objects] - stat[:total_freed_objects] - stat[:heap_final_slots]
  100. assert_equal stat[:heap_allocated_pages], stat[:heap_eden_pages] + stat[:heap_tomb_pages]
  101. if use_rgengc?
  102. assert_equal stat[:count], stat[:major_gc_count] + stat[:minor_gc_count]
  103. end
  104. end
  105. def test_latest_gc_info
  106. assert_separately %w[--disable-gem], __FILE__, __LINE__, <<-'eom'
  107. GC.start
  108. count = GC.stat(:heap_free_slots) + GC.stat(:heap_allocatable_pages) * GC::INTERNAL_CONSTANTS[:HEAP_PAGE_OBJ_LIMIT]
  109. count.times{ "a" + "b" }
  110. assert_equal :newobj, GC.latest_gc_info[:gc_by]
  111. eom
  112. GC.start
  113. assert_equal :force, GC.latest_gc_info[:major_by] if use_rgengc?
  114. assert_equal :method, GC.latest_gc_info[:gc_by]
  115. assert_equal true, GC.latest_gc_info[:immediate_sweep]
  116. GC.stress = true
  117. assert_equal :force, GC.latest_gc_info[:major_by]
  118. ensure
  119. GC.stress = false
  120. end
  121. def test_latest_gc_info_argument
  122. info = {}
  123. GC.latest_gc_info(info)
  124. assert_not_empty info
  125. assert_equal info[:gc_by], GC.latest_gc_info(:gc_by)
  126. assert_raise(ArgumentError){ GC.latest_gc_info(:invalid) }
  127. assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) {GC.latest_gc_info(:"\u{30eb 30d3 30fc}")}
  128. end
  129. def test_singleton_method
  130. assert_in_out_err(%w[--disable-gems], <<-EOS, [], [], "[ruby-dev:42832]")
  131. GC.stress = true
  132. 10.times do
  133. obj = Object.new
  134. def obj.foo() end
  135. def obj.bar() raise "obj.foo is called, but this is obj.bar" end
  136. obj.foo
  137. end
  138. EOS
  139. end
  140. def test_singleton_method_added
  141. assert_in_out_err(%w[--disable-gems], <<-EOS, [], [], "[ruby-dev:44436]")
  142. class BasicObject
  143. undef singleton_method_added
  144. def singleton_method_added(mid)
  145. raise
  146. end
  147. end
  148. b = proc {}
  149. class << b; end
  150. b.clone rescue nil
  151. GC.start
  152. EOS
  153. end
  154. def test_gc_parameter
  155. env = {
  156. "RUBY_GC_MALLOC_LIMIT" => "60000000",
  157. "RUBY_GC_HEAP_INIT_SLOTS" => "100000"
  158. }
  159. assert_normal_exit("exit", "[ruby-core:39777]", :child_env => env)
  160. env = {
  161. "RUBYOPT" => "",
  162. "RUBY_GC_HEAP_INIT_SLOTS" => "100000"
  163. }
  164. assert_in_out_err([env, "-e", "exit"], "", [], [], "[ruby-core:39795]")
  165. assert_in_out_err([env, "-W0", "-e", "exit"], "", [], [], "[ruby-core:39795]")
  166. assert_in_out_err([env, "-W1", "-e", "exit"], "", [], [], "[ruby-core:39795]")
  167. assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_HEAP_INIT_SLOTS=100000/, "[ruby-core:39795]")
  168. env = {
  169. "RUBY_GC_HEAP_GROWTH_FACTOR" => "2.0",
  170. "RUBY_GC_HEAP_GROWTH_MAX_SLOTS" => "10000"
  171. }
  172. assert_normal_exit("exit", "", :child_env => env)
  173. assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_HEAP_GROWTH_FACTOR=2.0/, "")
  174. assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_HEAP_GROWTH_MAX_SLOTS=10000/, "[ruby-core:57928]")
  175. env = {
  176. "RUBY_GC_HEAP_INIT_SLOTS" => "100000",
  177. "RUBY_GC_HEAP_FREE_SLOTS" => "10000",
  178. "RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR" => "0.9",
  179. }
  180. assert_normal_exit("exit", "", :child_env => env)
  181. assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR=0\.9/, "")
  182. # always full GC when RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR < 1.0
  183. 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?
  184. # check obsolete
  185. assert_in_out_err([{'RUBY_FREE_MIN' => '100'}, '-w', '-eexit'], '', [],
  186. /RUBY_FREE_MIN is obsolete. Use RUBY_GC_HEAP_FREE_SLOTS instead/)
  187. assert_in_out_err([{'RUBY_HEAP_MIN_SLOTS' => '100'}, '-w', '-eexit'], '', [],
  188. /RUBY_HEAP_MIN_SLOTS is obsolete. Use RUBY_GC_HEAP_INIT_SLOTS instead/)
  189. env = {
  190. "RUBY_GC_MALLOC_LIMIT" => "60000000",
  191. "RUBY_GC_MALLOC_LIMIT_MAX" => "160000000",
  192. "RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR" => "2.0"
  193. }
  194. assert_normal_exit("exit", "", :child_env => env)
  195. assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_MALLOC_LIMIT=6000000/, "")
  196. assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_MALLOC_LIMIT_MAX=16000000/, "")
  197. assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR=2.0/, "")
  198. if use_rgengc?
  199. env = {
  200. "RUBY_GC_OLDMALLOC_LIMIT" => "60000000",
  201. "RUBY_GC_OLDMALLOC_LIMIT_MAX" => "160000000",
  202. "RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR" => "2.0"
  203. }
  204. assert_normal_exit("exit", "", :child_env => env)
  205. assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_OLDMALLOC_LIMIT=6000000/, "")
  206. assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_OLDMALLOC_LIMIT_MAX=16000000/, "")
  207. assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR=2.0/, "")
  208. end
  209. end
  210. def test_profiler_enabled
  211. GC::Profiler.enable
  212. assert_equal(true, GC::Profiler.enabled?)
  213. GC::Profiler.disable
  214. assert_equal(false, GC::Profiler.enabled?)
  215. ensure
  216. GC::Profiler.disable
  217. end
  218. def test_profiler_clear
  219. skip "for now"
  220. assert_separately %w[--disable-gem], __FILE__, __LINE__, <<-'eom', timeout: 30
  221. GC::Profiler.enable
  222. GC.start
  223. assert_equal(1, GC::Profiler.raw_data.size)
  224. GC::Profiler.clear
  225. assert_equal(0, GC::Profiler.raw_data.size)
  226. 200.times{ GC.start }
  227. assert_equal(200, GC::Profiler.raw_data.size)
  228. GC::Profiler.clear
  229. assert_equal(0, GC::Profiler.raw_data.size)
  230. eom
  231. end
  232. def test_profiler_total_time
  233. GC::Profiler.enable
  234. GC::Profiler.clear
  235. GC.start
  236. assert_operator(GC::Profiler.total_time, :>=, 0)
  237. ensure
  238. GC::Profiler.disable
  239. end
  240. def test_finalizing_main_thread
  241. assert_in_out_err(%w[--disable-gems], <<-EOS, ["\"finalize\""], [], "[ruby-dev:46647]")
  242. ObjectSpace.define_finalizer(Thread.main) { p 'finalize' }
  243. EOS
  244. end
  245. def test_expand_heap
  246. assert_separately %w[--disable-gem], __FILE__, __LINE__, <<-'eom'
  247. GC.start
  248. base_length = GC.stat[:heap_eden_pages]
  249. (base_length * 500).times{ 'a' }
  250. GC.start
  251. base_length = GC.stat[:heap_eden_pages]
  252. (base_length * 500).times{ 'a' }
  253. GC.start
  254. assert_in_epsilon base_length, (v = GC.stat[:heap_eden_pages]), 1/8r,
  255. "invalid heap expanding (base_length: #{base_length}, GC.stat[:heap_eden_pages]: #{v})"
  256. a = []
  257. (base_length * 500).times{ a << 'a'; nil }
  258. GC.start
  259. assert_operator base_length, :<, GC.stat[:heap_eden_pages] + 1
  260. eom
  261. end
  262. def test_gc_internals
  263. assert_not_nil GC::INTERNAL_CONSTANTS[:HEAP_PAGE_OBJ_LIMIT]
  264. assert_not_nil GC::INTERNAL_CONSTANTS[:RVALUE_SIZE]
  265. end
  266. def test_sweep_in_finalizer
  267. bug9205 = '[ruby-core:58833] [Bug #9205]'
  268. 2.times do
  269. assert_ruby_status([], <<-'end;', bug9205, timeout: 120)
  270. raise_proc = proc do |id|
  271. GC.start
  272. end
  273. 1000.times do
  274. ObjectSpace.define_finalizer(Object.new, raise_proc)
  275. end
  276. end;
  277. end
  278. end
  279. def test_exception_in_finalizer
  280. bug9168 = '[ruby-core:58652] [Bug #9168]'
  281. assert_normal_exit(<<-'end;', bug9168, encoding: Encoding::ASCII_8BIT)
  282. raise_proc = proc {raise}
  283. 10000.times do
  284. ObjectSpace.define_finalizer(Object.new, raise_proc)
  285. Thread.handle_interrupt(RuntimeError => :immediate) {break}
  286. Thread.handle_interrupt(RuntimeError => :on_blocking) {break}
  287. Thread.handle_interrupt(RuntimeError => :never) {break}
  288. end
  289. end;
  290. end
  291. def test_interrupt_in_finalizer
  292. bug10595 = '[ruby-core:66825] [Bug #10595]'
  293. src = <<-'end;'
  294. Signal.trap(:INT, 'DEFAULT')
  295. pid = $$
  296. Thread.start do
  297. 10.times {
  298. sleep 0.1
  299. Process.kill("INT", pid) rescue break
  300. }
  301. end
  302. f = proc {1000.times {}}
  303. loop do
  304. ObjectSpace.define_finalizer(Object.new, f)
  305. end
  306. end;
  307. out, err, status = assert_in_out_err(["-e", src], "", [], [], bug10595, signal: :SEGV) do |*result|
  308. break result
  309. end
  310. unless /mswin|mingw/ =~ RUBY_PLATFORM
  311. assert_equal("INT", Signal.signame(status.termsig), bug10595)
  312. end
  313. assert_match(/Interrupt/, err.first, proc {err.join("\n")})
  314. assert_empty(out)
  315. end
  316. def test_verify_internal_consistency
  317. assert_nil(GC.verify_internal_consistency)
  318. end
  319. def test_gc_stress_on_realloc
  320. assert_normal_exit(<<-'end;', '[Bug #9859]')
  321. class C
  322. def initialize
  323. @a = nil
  324. @b = nil
  325. @c = nil
  326. @d = nil
  327. @e = nil
  328. @f = nil
  329. end
  330. end
  331. GC.stress = true
  332. C.new
  333. end;
  334. end
  335. def test_gc_stress_at_startup
  336. assert_in_out_err([{"RUBY_DEBUG"=>"gc_stress"}], '', [], [], '[Bug #15784]', success: true, timeout: 60)
  337. end
  338. def test_gc_disabled_start
  339. begin
  340. disabled = GC.disable
  341. c = GC.count
  342. GC.start
  343. assert_equal 1, GC.count - c
  344. ensure
  345. GC.enable unless disabled
  346. end
  347. end
  348. def test_vm_object
  349. assert_normal_exit <<-'end', '[Bug #12583]'
  350. ObjectSpace.each_object{|o| o.singleton_class rescue 0}
  351. ObjectSpace.each_object{|o| case o when Module then o.instance_methods end}
  352. end
  353. end
  354. def test_exception_in_finalizer_procs
  355. result = []
  356. c1 = proc do
  357. result << :c1
  358. raise
  359. end
  360. c2 = proc do
  361. result << :c2
  362. raise
  363. end
  364. tap {
  365. tap {
  366. obj = Object.new
  367. ObjectSpace.define_finalizer(obj, c1)
  368. ObjectSpace.define_finalizer(obj, c2)
  369. obj = nil
  370. }
  371. }
  372. GC.start
  373. skip "finalizers did not get run" if result.empty?
  374. assert_equal([:c1, :c2], result)
  375. end
  376. def test_exception_in_finalizer_method
  377. @result = []
  378. def self.c1(x)
  379. @result << :c1
  380. raise
  381. end
  382. def self.c2(x)
  383. @result << :c2
  384. raise
  385. end
  386. tap {
  387. tap {
  388. obj = Object.new
  389. ObjectSpace.define_finalizer(obj, method(:c1))
  390. ObjectSpace.define_finalizer(obj, method(:c2))
  391. obj = nil
  392. }
  393. }
  394. GC.start
  395. skip "finalizers did not get run" if @result.empty?
  396. assert_equal([:c1, :c2], @result)
  397. end
  398. end