ruby186gc.patch 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. --- a/gc.c (revision 31212)
  2. +++ b/gc.c (working copy)
  3. @@ -22,8 +22,16 @@
  4. #include <setjmp.h>
  5. #include <sys/types.h>
  6. +#ifdef _WIN32
  7. +#include <string.h>
  8. +#else
  9. +#include <strings.h>
  10. +#endif
  11. +
  12. #ifdef HAVE_SYS_TIME_H
  13. #include <sys/time.h>
  14. +#elif defined(_WIN32)
  15. +#include <time.h>
  16. #endif
  17. #ifdef HAVE_SYS_RESOURCE_H
  18. @@ -40,7 +48,6 @@
  19. #if !defined(setjmp) && defined(HAVE__SETJMP)
  20. #define setjmp(env) _setjmp(env)
  21. #endif
  22. -
  23. /* Make alloca work the best possible way. */
  24. #ifdef __GNUC__
  25. # ifndef atarist
  26. @@ -76,6 +83,20 @@
  27. NORETURN(void rb_exc_jump _((VALUE)));
  28. +static unsigned long live_objects = 0;
  29. +unsigned long rb_os_live_objects()
  30. +{ return live_objects; }
  31. +
  32. +#if defined(HAVE_LONG_LONG)
  33. +static unsigned long long allocated_objects = 0;
  34. +unsigned long long rb_os_allocated_objects()
  35. +{ return allocated_objects; }
  36. +#else
  37. +static unsigned long allocated_objects = 0;
  38. +unsigned long rb_os_allocated_objects()
  39. +{ return allocated_objects; }
  40. +#endif
  41. +
  42. void
  43. rb_memerror()
  44. {
  45. @@ -93,6 +114,10 @@
  46. rb_exc_raise(nomem_error);
  47. }
  48. +long gc_allocated_size = 0;
  49. +long gc_num_allocations = 0;
  50. +static int gc_statistics = 0;
  51. +
  52. void *
  53. ruby_xmalloc(size)
  54. long size;
  55. @@ -167,6 +192,8 @@
  56. extern int ruby_in_compile;
  57. static int dont_gc;
  58. +static GC_TIME_TYPE gc_time = 0;
  59. +static int gc_collections = 0;
  60. static int during_gc;
  61. static int need_call_final = 0;
  62. static st_table *finalizer_table = 0;
  63. @@ -201,8 +228,7 @@
  64. * Disables garbage collection, returning <code>true</code> if garbage
  65. * collection was already disabled.
  66. *
  67. - * GC.disable #=> false
  68. - * GC.disable #=> true
  69. + * GC.disable #=> false or true
  70. *
  71. */
  72. @@ -215,6 +241,139 @@
  73. return old;
  74. }
  75. +/*
  76. + * call-seq:
  77. + * GC.enable_stats => true or false
  78. + *
  79. + * Enables garbage collection statistics, returning <code>true</code> if garbage
  80. + * collection statistics was already enabled.
  81. + *
  82. + * GC.enable_stats #=> false or true
  83. + * GC.enable_stats #=> true
  84. + *
  85. + */
  86. +
  87. +VALUE
  88. +rb_gc_enable_stats()
  89. +{
  90. + int old = gc_statistics;
  91. + gc_statistics = Qtrue;
  92. + return old;
  93. +}
  94. +
  95. +/*
  96. + * call-seq:
  97. + * GC.disable_stats => true or false
  98. + *
  99. + * Disables garbage collection statistics, returning <code>true</code> if garbage
  100. + * collection statistics was already disabled.
  101. + *
  102. + * GC.disable_stats #=> false or true
  103. + * GC.disable_stats #=> true
  104. + *
  105. + */
  106. +
  107. +VALUE
  108. +rb_gc_disable_stats()
  109. +{
  110. + int old = gc_statistics;
  111. + gc_statistics = Qfalse;
  112. + gc_allocated_size = 0;
  113. + gc_num_allocations = 0;
  114. + return old;
  115. +}
  116. +
  117. +/*
  118. + * call-seq:
  119. + * GC.clear_stats => nil
  120. + *
  121. + * Clears garbage collection statistics, returning nil. This resets the number
  122. + * of collections (GC.collections) and the time used (GC.time) to 0.
  123. + *
  124. + * GC.clear_stats #=> nil
  125. + *
  126. + */
  127. +
  128. +VALUE
  129. +rb_gc_clear_stats()
  130. +{
  131. + gc_collections = 0;
  132. + gc_time = 0;
  133. + gc_allocated_size = 0;
  134. + gc_num_allocations = 0;
  135. + return Qnil;
  136. +}
  137. +
  138. +/*
  139. + * call-seq:
  140. + * GC.allocated_size => Integer
  141. + *
  142. + * Returns the size of memory (in bytes) allocated since GC statistics collection
  143. + * was enabled.
  144. + *
  145. + * GC.allocated_size #=> 35
  146. + *
  147. + */
  148. +VALUE
  149. +rb_gc_allocated_size()
  150. +{
  151. + return INT2NUM(gc_allocated_size);
  152. +}
  153. +
  154. +/*
  155. + * call-seq:
  156. + * GC.num_allocations => Integer
  157. + *
  158. + * Returns the number of memory allocations since GC statistics collection
  159. + * was enabled.
  160. + *
  161. + * GC.num_allocations #=> 150
  162. + *
  163. + */
  164. +VALUE
  165. +rb_gc_num_allocations()
  166. +{
  167. + return INT2NUM(gc_num_allocations);
  168. +}
  169. +
  170. +/*
  171. + * call-seq:
  172. + * GC.collections => Integer
  173. + *
  174. + * Returns the number of garbage collections performed while GC statistics collection
  175. + * was enabled.
  176. + *
  177. + * GC.collections #=> 35
  178. + *
  179. + */
  180. +
  181. +VALUE
  182. +rb_gc_collections()
  183. +{
  184. + return INT2NUM(gc_collections);
  185. +}
  186. +
  187. +/*
  188. + * call-seq:
  189. + * GC.time => Integer
  190. + *
  191. + * Returns the time spent during garbage collection while GC statistics collection
  192. + * was enabled (in micro seconds).
  193. + *
  194. + * GC.time #=> 20000
  195. + *
  196. + */
  197. +
  198. +VALUE
  199. +rb_gc_time()
  200. +{
  201. +#if HAVE_LONG_LONG
  202. + return LL2NUM(gc_time);
  203. +#else
  204. + return LONG2NUM(gc_time);
  205. +#endif
  206. +}
  207. +
  208. VALUE rb_mGC;
  209. static struct gc_list {
  210. @@ -306,7 +465,7 @@
  211. static RVALUE *freelist = 0;
  212. static RVALUE *deferred_final_list = 0;
  213. -#define HEAPS_INCREMENT 10
  214. +static int heaps_increment = 10;
  215. static struct heaps_slot {
  216. void *membase;
  217. RVALUE *slot;
  218. @@ -315,13 +474,164 @@
  219. static int heaps_length = 0;
  220. static int heaps_used = 0;
  221. -#define HEAP_MIN_SLOTS 10000
  222. -static int heap_slots = HEAP_MIN_SLOTS;
  223. +static int heap_min_slots = 10000;
  224. +static int heap_slots = 10000;
  225. -#define FREE_MIN 4096
  226. +static int heap_free_min = 4096;
  227. +static int heap_slots_increment = 10000;
  228. +static double heap_slots_growth_factor = 1.8;
  229. +static long initial_malloc_limit = GC_MALLOC_LIMIT;
  230. +
  231. +static int verbose_gc_stats = Qfalse;
  232. +
  233. +static FILE* gc_data_file = NULL;
  234. +
  235. static RVALUE *himem, *lomem;
  236. +static void set_gc_parameters()
  237. +{
  238. + char *gc_stats_ptr, *min_slots_ptr, *free_min_ptr, *heap_slots_incr_ptr,
  239. + *heap_incr_ptr, *malloc_limit_ptr, *gc_heap_file_ptr, *heap_slots_growth_factor_ptr;
  240. +
  241. + gc_data_file = stderr;
  242. +
  243. + gc_stats_ptr = getenv("RUBY_GC_STATS");
  244. + if (gc_stats_ptr != NULL) {
  245. + int gc_stats_i = atoi(gc_stats_ptr);
  246. + if (gc_stats_i > 0) {
  247. + verbose_gc_stats = Qtrue;
  248. + }
  249. + }
  250. +
  251. + gc_heap_file_ptr = getenv("RUBY_GC_DATA_FILE");
  252. + if (gc_heap_file_ptr != NULL) {
  253. + FILE* data_file = fopen(gc_heap_file_ptr, "w");
  254. + if (data_file != NULL) {
  255. + gc_data_file = data_file;
  256. + }
  257. + else {
  258. + fprintf(stderr,
  259. + "can't open gc log file %s for writing, using default\n", gc_heap_file_ptr);
  260. + }
  261. + }
  262. +
  263. + min_slots_ptr = getenv("RUBY_HEAP_MIN_SLOTS");
  264. + if (min_slots_ptr != NULL) {
  265. + int min_slots_i = atoi(min_slots_ptr);
  266. + if (verbose_gc_stats) {
  267. + fprintf(gc_data_file, "RUBY_HEAP_MIN_SLOTS=%s\n", min_slots_ptr);
  268. + }
  269. + if (min_slots_i > 0) {
  270. + heap_slots = min_slots_i;
  271. + heap_min_slots = min_slots_i;
  272. + }
  273. + }
  274. +
  275. + free_min_ptr = getenv("RUBY_HEAP_FREE_MIN");
  276. + if (free_min_ptr != NULL) {
  277. + int free_min_i = atoi(free_min_ptr);
  278. + if (verbose_gc_stats) {
  279. + fprintf(gc_data_file, "RUBY_HEAP_FREE_MIN=%s\n", free_min_ptr);
  280. + }
  281. + if (free_min_i > 0) {
  282. + heap_free_min = free_min_i;
  283. + }
  284. + }
  285. +
  286. + heap_incr_ptr = getenv("RUBY_HEAP_INCREMENT");
  287. + if (heap_incr_ptr != NULL) {
  288. + int heap_incr_i = atoi(heap_incr_ptr);
  289. + if (verbose_gc_stats) {
  290. + fprintf(gc_data_file, "RUBY_HEAP_INCREMENT=%s\n", heap_incr_ptr);
  291. + }
  292. + if (heap_incr_i > 0) {
  293. + heaps_increment = heap_incr_i;
  294. + }
  295. + }
  296. +
  297. + heap_slots_incr_ptr = getenv("RUBY_HEAP_SLOTS_INCREMENT");
  298. + if (heap_slots_incr_ptr != NULL) {
  299. + int heap_slots_incr_i = atoi(heap_slots_incr_ptr);
  300. + if (verbose_gc_stats) {
  301. + fprintf(gc_data_file, "RUBY_HEAP_SLOTS_INCREMENT=%s\n", heap_slots_incr_ptr);
  302. + }
  303. + if (heap_slots_incr_i > 0) {
  304. + heap_slots_increment = heap_slots_incr_i;
  305. + }
  306. + }
  307. +
  308. + heap_slots_growth_factor_ptr = getenv("RUBY_HEAP_SLOTS_GROWTH_FACTOR");
  309. + if (heap_slots_growth_factor_ptr != NULL) {
  310. + double heap_slots_growth_factor_d = atoi(heap_slots_growth_factor_ptr);
  311. + if (verbose_gc_stats) {
  312. + fprintf(gc_data_file, "RUBY_HEAP_SLOTS_GROWTH_FACTOR=%s\n", heap_slots_growth_factor_ptr);
  313. + }
  314. + if (heap_slots_growth_factor_d > 0) {
  315. + heap_slots_growth_factor = heap_slots_growth_factor_d;
  316. + }
  317. + }
  318. +
  319. + malloc_limit_ptr = getenv("RUBY_GC_MALLOC_LIMIT");
  320. + if (malloc_limit_ptr != NULL) {
  321. + int malloc_limit_i = atol(malloc_limit_ptr);
  322. + if (verbose_gc_stats) {
  323. + fprintf(gc_data_file, "RUBY_GC_MALLOC_LIMIT=%s\n", malloc_limit_ptr);
  324. + }
  325. + if (malloc_limit_i > 0) {
  326. + initial_malloc_limit = malloc_limit_i;
  327. + }
  328. + }
  329. +}
  330. +
  331. +/*
  332. + * call-seq:
  333. + * GC.dump => nil
  334. + *
  335. + * dumps information about the current GC data structures to the GC log file
  336. + *
  337. + * GC.dump #=> nil
  338. + *
  339. + */
  340. +
  341. +VALUE
  342. +rb_gc_dump()
  343. +{
  344. + int i;
  345. +
  346. + for (i = 0; i < heaps_used; i++) {
  347. + int heap_size = heaps[i].limit;
  348. + fprintf(gc_data_file, "HEAP[%2d]: size=%7d\n", i, heap_size);
  349. + }
  350. +
  351. + return Qnil;
  352. +}
  353. +
  354. +/*
  355. + * call-seq:
  356. + * GC.log String => String
  357. + *
  358. + * Logs string to the GC data file and returns it.
  359. + *
  360. + * GC.log "manual GC call" #=> "manual GC call"
  361. + *
  362. + */
  363. +
  364. +VALUE
  365. +rb_gc_log(self, original_str)
  366. + VALUE self, original_str;
  367. +{
  368. + if (original_str == Qnil) {
  369. + fprintf(gc_data_file, "\n");
  370. + }
  371. + else {
  372. + VALUE str = StringValue(original_str);
  373. + char *p = RSTRING(str)->ptr;
  374. + fprintf(gc_data_file, "%s\n", p);
  375. + }
  376. + return original_str;
  377. +}
  378. +
  379. static void
  380. add_heap()
  381. {
  382. @@ -332,7 +642,7 @@
  383. struct heaps_slot *p;
  384. int length;
  385. - heaps_length += HEAPS_INCREMENT;
  386. + heaps_length += heaps_increment;
  387. length = heaps_length*sizeof(struct heaps_slot);
  388. RUBY_CRITICAL(
  389. if (heaps_used > 0) {
  390. @@ -344,14 +654,18 @@
  391. });
  392. if (p == 0) rb_memerror();
  393. }
  394. + if (gc_statistics) {
  395. + gc_allocated_size += size;
  396. + gc_num_allocations += 1;
  397. + }
  398. for (;;) {
  399. RUBY_CRITICAL(p = (RVALUE*)malloc(sizeof(RVALUE)*(heap_slots+1)));
  400. if (p == 0) {
  401. - if (heap_slots == HEAP_MIN_SLOTS) {
  402. + if (heap_slots == heap_min_slots) {
  403. rb_memerror();
  404. }
  405. - heap_slots = HEAP_MIN_SLOTS;
  406. + heap_slots = heap_min_slots;
  407. continue;
  408. }
  409. heaps[heaps_used].membase = p;
  410. @@ -367,10 +681,17 @@
  411. if (lomem == 0 || lomem > p) lomem = p;
  412. if (himem < pend) himem = pend;
  413. heaps_used++;
  414. - heap_slots *= 1.8;
  415. - if (heap_slots <= 0) heap_slots = HEAP_MIN_SLOTS;
  416. + heap_slots += heap_slots_increment;
  417. + heap_slots_increment *= heap_slots_growth_factor;
  418. + if (heap_slots <= 0) heap_slots = heap_min_slots;
  419. while (p < pend) {
  420. + if (do_gc_stats) {
  421. + int obt = p->as.basic.flags & T_MASK;
  422. + if (obt) {
  423. + free_counts[obt]++;
  424. + }
  425. + }
  426. p->as.free.flags = 0;
  427. p->as.free.next = freelist;
  428. freelist = p;
  429. @@ -402,6 +723,8 @@
  430. RANY(obj)->file = ruby_sourcefile;
  431. RANY(obj)->line = ruby_sourceline;
  432. #endif
  433. + live_objects++;
  434. + allocated_objects++;
  435. return obj;
  436. }
  437. @@ -1062,6 +1385,39 @@
  438. }
  439. }
  440. +static char* obj_type(int tp)
  441. +{
  442. + switch (tp) {
  443. + case T_NIL : return "NIL";
  444. + case T_OBJECT : return "OBJECT";
  445. + case T_CLASS : return "CLASS";
  446. + case T_ICLASS : return "ICLASS";
  447. + case T_MODULE : return "MODULE";
  448. + case T_FLOAT : return "FLOAT";
  449. + case T_STRING : return "STRING";
  450. + case T_REGEXP : return "REGEXP";
  451. + case T_ARRAY : return "ARRAY";
  452. + case T_FIXNUM : return "FIXNUM";
  453. + case T_HASH : return "HASH";
  454. + case T_STRUCT : return "STRUCT";
  455. + case T_BIGNUM : return "BIGNUM";
  456. + case T_FILE : return "FILE";
  457. +
  458. + case T_TRUE : return "TRUE";
  459. + case T_FALSE : return "FALSE";
  460. + case T_DATA : return "DATA";
  461. + case T_MATCH : return "MATCH";
  462. + case T_SYMBOL : return "SYMBOL";
  463. +
  464. + case T_BLKTAG : return "BLKTAG";
  465. + case T_UNDEF : return "UNDEF";
  466. + case T_VARMAP : return "VARMAP";
  467. + case T_SCOPE : return "SCOPE";
  468. + case T_NODE : return "NODE";
  469. + default: return "____";
  470. + }
  471. +}
  472. +
  473. static void
  474. free_unused_heaps()
  475. {
  476. @@ -1093,14 +1449,24 @@
  477. int i;
  478. unsigned long live = 0;
  479. unsigned long free_min = 0;
  480. + live_objects = 0;
  481. + unsigned long really_freed = 0;
  482. + int free_counts[256];
  483. + int live_counts[256];
  484. + int do_gc_stats = gc_statistics & verbose_gc_stats;
  485. +
  486. for (i = 0; i < heaps_used; i++) {
  487. free_min += heaps[i].limit;
  488. }
  489. free_min = free_min * 0.2;
  490. - if (free_min < FREE_MIN)
  491. - free_min = FREE_MIN;
  492. + if (free_min < heap_free_min)
  493. + free_min = heap_free_min;
  494. + if (do_gc_stats) {
  495. + for (i = 0 ; i< 256; i++) { free_counts[i] = live_counts[i] = 0; }
  496. + }
  497. +
  498. if (ruby_in_compile && ruby_parser_stack_on_heap()) {
  499. /* should not reclaim nodes during compilation
  500. if yacc's semantic stack is not allocated on machine stack */
  501. @@ -1138,6 +1504,9 @@
  502. p->as.free.flags = T_DEFERRED;
  503. RDATA(p)->dfree = 0;
  504. }
  505. + if (do_gc_stats) {
  506. + really_freed++;
  507. + }
  508. p->as.free.flags |= FL_MARK;
  509. p->as.free.next = final_list;
  510. final_list = p;
  511. @@ -1153,7 +1522,10 @@
  512. }
  513. else {
  514. RBASIC(p)->flags &= ~FL_MARK;
  515. - live++;
  516. + live_objects++;
  517. + if (do_gc_stats) {
  518. + live_counts[RANY((VALUE)p)->as.basic.flags & T_MASK]++;
  519. + }
  520. }
  521. p++;
  522. }
  523. @@ -1171,8 +1543,8 @@
  524. }
  525. }
  526. if (malloc_increase > malloc_limit) {
  527. - malloc_limit += (malloc_increase - malloc_limit) * (double)live / (live + freed);
  528. - if (malloc_limit < GC_MALLOC_LIMIT) malloc_limit = GC_MALLOC_LIMIT;
  529. + malloc_limit += (malloc_increase - malloc_limit) * (double)live_objects / (live_objects + freed);
  530. + if (malloc_limit < initial_malloc_limit) malloc_limit = initial_malloc_limit;
  531. }
  532. malloc_increase = 0;
  533. if (freed < free_min) {
  534. @@ -1180,6 +1552,20 @@
  535. }
  536. during_gc = 0;
  537. + if (do_gc_stats) {
  538. + fprintf(gc_data_file, "objects processed: %.7d\n", live+freed);
  539. + fprintf(gc_data_file, "live objects : %.7d\n", live);
  540. + fprintf(gc_data_file, "freelist objects : %.7d\n", freed - really_freed);
  541. + fprintf(gc_data_file, "freed objects : %.7d\n", really_freed);
  542. + for(i=0; i<256; i++) {
  543. + if (free_counts[i]>0) {
  544. + fprintf(gc_data_file,
  545. + "kept %.7d / freed %.7d objects of type %s\n",
  546. + live_counts[i], free_counts[i], obj_type(i));
  547. + }
  548. + }
  549. + }
  550. +
  551. /* clear finalization list */
  552. if (final_list) {
  553. deferred_final_list = final_list;
  554. @@ -1394,6 +1780,7 @@
  555. struct gc_list *list;
  556. struct FRAME * volatile frame; /* gcc 2.7.2.3 -O2 bug?? */
  557. jmp_buf save_regs_gc_mark;
  558. + struct timeval gctv1, gctv2;
  559. SET_STACK_END;
  560. #ifdef HAVE_NATIVETHREAD
  561. @@ -1410,6 +1797,14 @@
  562. if (during_gc) return;
  563. during_gc++;
  564. + if (gc_statistics) {
  565. + gc_collections++;
  566. + gettimeofday(&gctv1, NULL);
  567. + if (verbose_gc_stats) {
  568. + fprintf(gc_data_file, "Garbage collection started\n");
  569. + }
  570. + }
  571. +
  572. init_mark_stack();
  573. gc_mark((VALUE)ruby_current_node, 0);
  574. @@ -1485,6 +1880,17 @@
  575. } while (!MARK_STACK_EMPTY);
  576. gc_sweep();
  577. +
  578. + if (gc_statistics) {
  579. + GC_TIME_TYPE musecs_used;
  580. + gettimeofday(&gctv2, NULL);
  581. + musecs_used = ((GC_TIME_TYPE)(gctv2.tv_sec - gctv1.tv_sec) * 1000000) + (gctv2.tv_usec - gctv1.tv_usec);
  582. + gc_time += musecs_used;
  583. +
  584. + if (verbose_gc_stats) {
  585. + fprintf(gc_data_file, "GC time: %d msec\n", musecs_used / 1000);
  586. + }
  587. + }
  588. }
  589. void
  590. @@ -1666,6 +2072,7 @@
  591. if (!rb_gc_stack_start) {
  592. Init_stack(0);
  593. }
  594. + set_gc_parameters();
  595. add_heap();
  596. }
  597. @@ -2109,6 +2516,35 @@
  598. return (VALUE)((long)obj|FIXNUM_FLAG);
  599. }
  600. +/* call-seq:
  601. + * ObjectSpace.live_objects => number
  602. + *
  603. + * Returns the count of objects currently allocated in the system. This goes
  604. + * down after the garbage collector runs.
  605. + */
  606. +static
  607. +VALUE os_live_objects(VALUE self)
  608. +{ return ULONG2NUM(live_objects); }
  609. +
  610. +/* call-seq:
  611. + * ObjectSpace.allocated_objects => number
  612. + *
  613. + * Returns the count of objects allocated since the Ruby interpreter has
  614. + * started. This number can only increase. To know how many objects are
  615. + * currently allocated, use ObjectSpace::live_objects
  616. + */
  617. +static
  618. +VALUE os_allocated_objects(VALUE self)
  619. +{
  620. +#if defined(HAVE_LONG_LONG)
  621. + return ULL2NUM(allocated_objects);
  622. +#else
  623. + return ULONG2NUM(allocated_objects);
  624. +#endif
  625. +}
  626. +
  627. +
  628. +
  629. /*
  630. * The <code>GC</code> module provides an interface to Ruby's mark and
  631. * sweep garbage collection mechanism. Some of the underlying methods
  632. @@ -2126,6 +2562,16 @@
  633. rb_define_singleton_method(rb_mGC, "disable", rb_gc_disable, 0);
  634. rb_define_method(rb_mGC, "garbage_collect", rb_gc_start, 0);
  635. + rb_define_singleton_method(rb_mGC, "enable_stats", rb_gc_enable_stats, 0);
  636. + rb_define_singleton_method(rb_mGC, "disable_stats", rb_gc_disable_stats, 0);
  637. + rb_define_singleton_method(rb_mGC, "clear_stats", rb_gc_clear_stats, 0);
  638. + rb_define_singleton_method(rb_mGC, "allocated_size", rb_gc_allocated_size, 0);
  639. + rb_define_singleton_method(rb_mGC, "num_allocations", rb_gc_num_allocations, 0);
  640. + rb_define_singleton_method(rb_mGC, "collections", rb_gc_collections, 0);
  641. + rb_define_singleton_method(rb_mGC, "time", rb_gc_time, 0);
  642. + rb_define_singleton_method(rb_mGC, "dump", rb_gc_dump, 0);
  643. + rb_define_singleton_method(rb_mGC, "log", rb_gc_log, 1);
  644. +
  645. rb_mObSpace = rb_define_module("ObjectSpace");
  646. rb_define_module_function(rb_mObSpace, "each_object", os_each_obj, -1);
  647. rb_define_module_function(rb_mObSpace, "garbage_collect", rb_gc_start, 0);
  648. @@ -2133,6 +2579,8 @@
  649. rb_define_module_function(rb_mObSpace, "remove_finalizer", rm_final, 1);
  650. rb_define_module_function(rb_mObSpace, "finalizers", finals, 0);
  651. rb_define_module_function(rb_mObSpace, "call_finalizer", call_final, 1);
  652. + rb_define_module_function(rb_mObSpace, "live_objects", os_live_objects, 0);
  653. + rb_define_module_function(rb_mObSpace, "allocated_objects", os_allocated_objects, 0);
  654. rb_define_module_function(rb_mObSpace, "define_finalizer", define_final, -1);
  655. rb_define_module_function(rb_mObSpace, "undefine_finalizer", undefine_final, 1);