-
Notifications
You must be signed in to change notification settings - Fork 226
Expand file tree
/
Copy pathgc.java
More file actions
3321 lines (3124 loc) · 140 KB
/
gc.java
File metadata and controls
3321 lines (3124 loc) · 140 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package org.python.modules;
import java.util.Set;
import java.util.List;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.HashSet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import org.python.core.JyAttribute;
import org.python.core.Py;
import org.python.core.PyException;
import org.python.core.PyList;
import org.python.core.PyObject;
import org.python.core.PyInstance;
import org.python.core.PyString;
import org.python.core.Traverseproc;
import org.python.core.TraverseprocDerived;
import org.python.core.Visitproc;
import org.python.core.Untraversable;
import org.python.core.finalization.FinalizeTrigger;
import org.python.modules._weakref.GlobalRef;
import org.python.modules._weakref.ReferenceBackend;
//These imports belong to the out commented section on MXBean-based
//gc sync far below. That section is kept to document this failed
//approach and allow easy reproduction of this failure.
//import java.lang.management.*;
//import javax.management.*;
//import javax.management.openmbean.*;
/**
* In Jython, the gc module notably differs from that in CPython. This comes from the different ways
* Jython and CPython perform garbage collection. While CPython's garbage collection is based on
* <a href="http://en.wikipedia.org/wiki/Reference_counting" target="_blank"> reference
* counting</a>, Jython is backed by Java's gc, which is based on a
* <a href="http://en.wikipedia.org/wiki/Tracing_garbage_collection" target="_blank"> mark-and-sweep
* approach</a>.
* <p>
* This difference becomes most notable if finalizers are involved that perform resurrection. While
* the resurrected object itself behaves rather similar between Jython and CPython, things are more
* delicate with objects that are reachable (i.e. strongly referenced) via the resurrected object
* exclusively. While in CPython such objects do not get their finalizers called, Jython/Java would
* call all their finalizers. That is because Java detects the whole unreachable subgraph as garbage
* and thus calls all their finalizers without any chance of direct intervention. CPython instead
* detects the unreachable object and calls its finalizer, which makes the object reachable again.
* Then all other objects are reachable from it and CPython does not treat them as garbage and does
* not call their finalizers at all. This further means that in Jython weak references to such
* indirectly resurrected objects break, while these persist in CPython.
* <p>
* As of Jython 2.7, the gc module offers some options to emulate CPython behavior. Especially see
* the flags {@link #PRESERVE_WEAKREFS_ON_RESURRECTION}, {@link #DONT_FINALIZE_RESURRECTED_OBJECTS}
* and {@link #DONT_FINALIZE_CYCLIC_GARBAGE} for this.
* <p>
* Another difference is that CPython's gc module offers some debug features like counting of
* collected cyclic trash, which are hard to support by Jython. As of Jython 2.7 the introduction of
* a traverseproc mechanism (c.f. {@link org.python.core.Traverseproc}) made support of these
* features feasible. As support of these features comes with a significant emulation cost, one must
* explicitly tell gc to perform this. To make objects subject to cyclic trash counting, these
* objects must be gc-monitored in Jython. See {@link #monitorObject(PyObject)},
* {@link #unmonitorObject(PyObject)}, {@link #MONITOR_GLOBAL} and {@link #stopMonitoring()} for
* this.
* <p>
* If at least one object is gc-monitored, {@link #collect()} works synchronously in the sense that
* it blocks until all gc-monitored objects that are garbage actually have been collected and had
* their finalizers called and completed. {@link #collect()} will report the number of collected
* objects in the same manner as in CPython, i.e. counts only those that participate in reference
* cycles. This allows a unified test implementation across Jython and CPython (which applies to
* most tests in test_gc.py). If not any object is gc-monitored, {@link #collect()} just delegates
* to {@link java.lang.System#gc()}, runs asynchronously (i.e. non-blocking) and returns
* {@link #UNKNOWN_COUNT}. See also {@link #DEBUG_SAVEALL} for a useful gc debugging feature that is
* supported by Jython from version 2.7 onwards.
* <p>
* Implementing all these features in Jython involved a lot of synchronization logic. While care was
* taken to implement this without using timeouts as far as possible and rely on locks, states and
* system/hardware independent synchronization techniques, this was not entirely feasible.<br>
* The aspects that were only feasible using a timeout are waiting for gc to enqueue all collected
* objects (i.e. weak references to monitored objects that were gc'ed) to the reference queue and
* waiting for gc to run all PyObject finalizers.
* <p>
* Waiting for trash could in theory be strictly synchronized by using {@code MXBean}s, i.e.
* <a href=
* "https://docs.oracle.com/javase/7/docs/jre/api/management/extension/index.html?com/sun/management/GcInfo.html"
* target="_blank">GarbageCollectionNotificationInfo</a> and related API. However, experiments
* showed that the arising gc notifications do not reliably indicate when enqueuing was done for a
* specific gc run. We kept the experimental implementation in source code comments to allow easy
* reproducibility of this issue. (Note that out commented code contradicts Jython styleguide, but
* this one - however - is needed to document this infeasible approach and is explicitly declared
* accordingly).
* <p>
* But how <b>is</b> sync done now? We insert a sentinel before running gc and wait until this
* sentinel was collected. Timestamps are taken to give us an idea at which time scales the gc of
* the current JVM performs. We then wait until twice the measured time (i.e. duration from call to
* {@link java.lang.System#gc()} until the sentinel reference was enqueued) has passed after the
* last reference was enqueued by gc. While this approach is not entirely safe in theory, it passes
* all tests on various systems and machines we had available for testing so far. We consider it
* more robust than a fixed-length timeout and regard it the best known feasible compromise to
* emulate synchronous gc runs in Java.
* <p>
* The other timing-based synchronization issue - waiting for finalizers to run - is solved as
* follows. Since PyObject finalizers are based on
* {@link org.python.core.finalization.FinalizeTrigger}s, Jython has full control about these
* finalization process from a central point. Before such a finalizer runs, it calls
* {@link #notifyPreFinalization()} and when it is done, it calls {@link #notifyPostFinalization()}.
* While processing of a finalizer can be of arbitrary duration, it widely holds that Java's gc
* thread calls the next finalizer almost instantaneously after the former. That means that a
* timestamp taken in {@link #notifyPreFinalization()} is usually delayed only few milliseconds -
* often even reported as 0 milliseconds - after the last taken timestamp in
* {@link #notifyPostFinalization()} (i.e. that was called by the previous finalizer). Jython's gc
* module assumes the end of Java's finalization process if {@link #postFinalizationTimeOut}
* milliseconds passed after a call of {@link #notifyPostFinalization()} without another call to
* {@link #notifyPreFinalization()} in that time. The default value of
* {@link #postFinalizationTimeOut} is {@code 100}, which is far larger than the usual almost-zero
* duration between finalizer calls.<br>
* This process can be disturbed by third-party finalizers of non-PyObjects brought into the process
* by external libraries. If these finalizers are of short duration (which applies to typical
* finalizers), one can deal with this by adjusting {@link #postFinalizationTimeOut}, which was
* declared {@code public} for exactly this purpose. However if the external framework causing the
* issue is Jython aware, a cleaner solution would be to let its finalizers call
* {@link #notifyPreFinalization()} and {@link #notifyPostFinalization()} appropriately. In that
* case these finalizers must not terminate by throwing an exception before
* {@link #notifyPostFinalization()} was called. This is a strict requirement, since a deadlock can
* be caused otherwise.
* <p>
* Note that the management API (c.f. <a href=
* "https://docs.oracle.com/javase/7/docs/jre/api/management/extension/index.html?com/sun/management/GcInfo.html"
* target="_blank">com.sun.management.GarbageCollectionNotificationInfo</a>) does not emit any
* notifications that allow to detect the end of the finalization phase. So this API provides no
* alternative to the described technique.
* <p>
* Usually Java's gc provides hardly any guarantee about its collection and finalization process. It
* not even guarantees that finalizers are called at all (c.f.
* <a href="http://howtodoinjava.com/2012/10/31/why-not-to-use-finalize-method-in-java" target=
* "_blank">http://howtodoinjava.com/2012/10/31/why-not-to-use-finalize-method-in-java</a>). While
* at least the most common JVM implementations usually <b>do</b> call finalizers reliably under
* normal conditions, there still is no specific finalization order guaranteed (one might reasonably
* expect that this would be related to reference connection graph topology, but this appears not to
* be the case). However Jython now offers some functionality to compensate this situation. Via
* {@link #registerPreFinalizationProcess(Runnable)} and
* {@link #registerPostFinalizationProcess(Runnable)} and related methods one can now listen to
* beginning and end of the finalization process. Note that this functionality relies on the
* technique described in the former paragraph (i.e. based on calls to
* {@link #notifyPreFinalization()} and {@link #notifyPostFinalization()}) and thus underlies its
* unsafety, if third-party finalizers are involved. Such finalizers can cause false-positive runs
* of registered (pre/post) finalization processes, so this feature should be used with some care.
* It is recommended to use it only in such a way that false-positive runs would not cause serious
* harm, but only some loss in performance or so.
*/
public class gc {
/**
* A constant that can occur as result of {@link #collect()} and
* indicates an unknown number of collected cyclic trash.
* It is intentionally not valued -1 as that value is
* reserved to indicate an error.
*/
public static final int UNKNOWN_COUNT = -2;
/* Jython-specific gc flags: */
/**
* This flag tells every newly created PyObject to register for
* gc monitoring. This allows {@link #collect()} to report the
* number of collected objects.
*
* @see #setJythonGCFlags(short)
* @see #getJythonGCFlags()
* @see #addJythonGCFlags(short)
* @see #removeJythonGCFlags(short)
*/
public static final short MONITOR_GLOBAL = (1<<0);
/**
* CPython prior to 3.4 does not finalize cyclic garbage
* PyObjects, while Jython does this by default. This flag
* tells Jython's gc to mimic CPython <3.4 behavior (i.e.
* add such objects to {@code gc.garbage} list instead).
*
* @see #setJythonGCFlags(short)
* @see #getJythonGCFlags()
* @see #addJythonGCFlags(short)
* @see #removeJythonGCFlags(short)
*/
public static final short DONT_FINALIZE_CYCLIC_GARBAGE = (1<<1);
/**
* If a PyObject is resurrected during its finalization
* process and was weakly referenced, Jython breaks the
* weak references to the resurrected PyObject by default.
* In CPython these persist, if the object was indirectly
* resurrected due to resurrection of its owner.
* This flag tells Jython's gc to preserve weak references
* to such resurrected PyObjects.
* It only works if all involved objects implement the
* traverseproc mechanism properly (see
* {@link org.python.core.Traverseproc}).
* Note that this feature comes with some cost as it can
* delay garbage collection of some weak referenced objects
* for several gc cycles if activated. So we recommend to
* use it only for debugging.
*
* @see #setJythonGCFlags(short)
* @see #getJythonGCFlags()
* @see #addJythonGCFlags(short)
* @see #removeJythonGCFlags(short)
*/
public static final short PRESERVE_WEAKREFS_ON_RESURRECTION = (1<<2);
/**
* If in CPython an object is resurrected via its finalizer
* and contained strong references to other objects, these
* are also resurrected and not finalized in CPython (as
* their reference count never drops to zero). In contrast
* to that, Jython calls finalizers for all objects that
* were unreachable when gc started (regardless of resurrections
* and in unpredictable order). This flag emulates CPython
* behavior in Jython. Note that this emulation comes with a
* significant cost as it can delay collection of many objects
* for several gc cycles. Its main intention is for debugging
* resurrection-sensitive code.
*
* @see #setJythonGCFlags(short)
* @see #getJythonGCFlags()
* @see #addJythonGCFlags(short)
* @see #removeJythonGCFlags(short)
*/
public static final short DONT_FINALIZE_RESURRECTED_OBJECTS = (1<<3);
public static final short FORCE_DELAYED_FINALIZATION = (1<<4);
public static final short FORCE_DELAYED_WEAKREF_CALLBACKS = (1<<5);
/**
* <p>
* Reflection-based traversal is an inefficient fallback method to
* traverse PyObject subtypes that don't implement
* {@link org.python.core.Traverseproc} and
* are not marked as {@link org.python.core.Untraversable}.
* Such a situation indicates that the programmer was not aware of
* Jython's traverseproc mechanism and reflection is used to
* compensate this.
* </p>
* <p>
* This flag allows to inhibit reflection-based traversal. If it is
* activated, objects that don't implement
* {@link org.python.core.Traverseproc}
* are always treated as if they were marked as
* {@link org.python.core.Untraversable}.
* </p>
* <p>
* Note that reflection-based traversal fallback is performed by
* default. Further note that Jython emits warning messages if
* reflection-based traversal occurs or if an object is encountered
* that neither implements {@link org.python.core.Traverseproc}
* nor is marked as {@link org.python.core.Untraversable} (even if
* reflection-based traversal is inhibited). See
* {@link #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING} and
* {@link #INSTANCE_TRAVERSE_BY_REFLECTION_WARNING} to control
* these warning messages.
* </p>
*
* @see #setJythonGCFlags(short)
* @see #getJythonGCFlags()
* @see #addJythonGCFlags(short)
* @see #removeJythonGCFlags(short)
* @see #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING
* @see #INSTANCE_TRAVERSE_BY_REFLECTION_WARNING
*/
public static final short DONT_TRAVERSE_BY_REFLECTION = (1<<6);
/**
* <p>
* If this flag is not set, gc warns whenever an object would be subject to
* reflection-based traversal.
* Note that if this flag is not set, the warning will occur even if
* reflection-based traversal is not active. The purpose of this behavior is
* to identify objects that don't properly support the traverseproc mechanism,
* i.e. instances of PyObject subclasses that neither implement
* {@link org.python.core.Traverseproc},
* nor are annotated with the {@link org.python.core.Untraversable} annotation.
* </p>
* <p>
* A SUPPRESS flag was chosen rather than a WARN flag, so that warning is the
* default behavior - the user must actively set this flag in order to not to
* be warned.
* This is because in an ideal implementation reflection-based traversal never
* occurs; it is only an inefficient fallback.
* </p>
*
* @see #setJythonGCFlags(short)
* @see #getJythonGCFlags()
* @see #addJythonGCFlags(short)
* @see #removeJythonGCFlags(short)
* @see #INSTANCE_TRAVERSE_BY_REFLECTION_WARNING
*/
public static final short SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING = (1<<7);
/**
* Makes gc emit reflection-based traversal warning for every traversed
* object instead of only once per class.
* A potential reflection-based traversal occurs whenever an object is
* traversed that neither implements {@link org.python.core.Traverseproc},
* nor is annotated with the {@link org.python.core.Untraversable} annotation.
*
* @see #setJythonGCFlags(short)
* @see #getJythonGCFlags()
* @see #addJythonGCFlags(short)
* @see #removeJythonGCFlags(short)
* @see #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING
*/
public static final short INSTANCE_TRAVERSE_BY_REFLECTION_WARNING = (1<<8);
/**
* In Jython one usually uses {@code Py.writeDebug} for debugging output.
* However that method is only verbose if an appropriate verbose level
* was set. In CPython it is enough to set gc {@code DEBUG} flags to get
* gc messages, no matter what overall verbose level is selected.
* This flag tells Jython to use {@code Py.writeDebug} for debugging output.
* If it is not set (default case), gc debugging output (if gc {@code VERBOSE}
* or {@code DEBUG} flags are set) is directly written to {@code System.err}.
*
* @see #setJythonGCFlags(short)
* @see #getJythonGCFlags()
* @see #addJythonGCFlags(short)
* @see #removeJythonGCFlags(short)
*/
public static final short USE_PY_WRITE_DEBUG = (1<<9);
/**
* Enables collection-related verbose output.
*
* @see #setJythonGCFlags(short)
* @see #getJythonGCFlags()
* @see #addJythonGCFlags(short)
* @see #removeJythonGCFlags(short)
*/
public static final short VERBOSE_COLLECT = (1<<10);
/**
* Enables weakref-related verbose output.
*
* @see #setJythonGCFlags(short)
* @see #getJythonGCFlags()
* @see #addJythonGCFlags(short)
* @see #removeJythonGCFlags(short)
*/
public static final short VERBOSE_WEAKREF = (1<<11);
/**
* Enables delayed finalization related verbose output.
*
* @see #setJythonGCFlags(short)
* @see #getJythonGCFlags()
* @see #addJythonGCFlags(short)
* @see #removeJythonGCFlags(short)
*/
public static final short VERBOSE_DELAYED = (1<<12);
/**
* Enables finalization-related verbose output.
*
* @see #setJythonGCFlags(short)
* @see #getJythonGCFlags()
* @see #addJythonGCFlags(short)
* @see #removeJythonGCFlags(short)
*/
public static final short VERBOSE_FINALIZE = (1<<13);
/**
* Bit combination of the flags {@link #VERBOSE_COLLECT},
* {@link #VERBOSE_WEAKREF}, {@link #VERBOSE_DELAYED},
* {@link #VERBOSE_FINALIZE}.
*
* @see #setJythonGCFlags(short)
* @see #getJythonGCFlags()
* @see #addJythonGCFlags(short)
* @see #removeJythonGCFlags(short)
*/
public static final short VERBOSE =
VERBOSE_COLLECT | VERBOSE_WEAKREF | VERBOSE_DELAYED | VERBOSE_FINALIZE;
/* set for debugging information */
/**
* print collection statistics
* (in Jython scoped on monitored objects)
*
* @see #set_debug(int)
* @see #get_debug()
*/
public static final int DEBUG_STATS = (1<<0);
/**
* print collectable objects
* (in Jython scoped on monitored objects)
*
* @see #set_debug(int)
* @see #get_debug()
*/
public static final int DEBUG_COLLECTABLE = (1<<1);
/**
* print uncollectable objects
* (in Jython scoped on monitored objects)
*
* @see #set_debug(int)
* @see #get_debug()
*/
public static final int DEBUG_UNCOLLECTABLE = (1<<2);
/**
* print instances
* (in Jython scoped on monitored objects)
*
* @see #set_debug(int)
* @see #get_debug()
*/
public static final int DEBUG_INSTANCES = (1<<3);
/**
* print other objects
* (in Jython scoped on monitored objects)
*
* @see #set_debug(int)
* @see #get_debug()
*/
public static final int DEBUG_OBJECTS = (1<<4);
/**
* save all garbage in gc.garbage
* (in Jython scoped on monitored objects)
*
* @see #set_debug(int)
* @see #get_debug()
*/
public static final int DEBUG_SAVEALL = (1<<5);
/**
* Bit combination of the flags {@link #DEBUG_COLLECTABLE},
* {@link #DEBUG_UNCOLLECTABLE}, {@link #DEBUG_INSTANCES},
* {@link #DEBUG_OBJECTS}, {@link #DEBUG_SAVEALL}.
*
* @see #set_debug(int)
* @see #get_debug()
*/
public static final int DEBUG_LEAK = DEBUG_COLLECTABLE |
DEBUG_UNCOLLECTABLE |
DEBUG_INSTANCES |
DEBUG_OBJECTS |
DEBUG_SAVEALL;
private static short gcFlags = DONT_TRAVERSE_BY_REFLECTION;
private static int debugFlags = 0;
private static boolean monitorNonTraversable = false;
private static boolean waitingForFinalizers = false;
private static final AtomicBoolean gcRunning = new AtomicBoolean(false);
private static final Set<WeakReferenceGC> monitoredObjects = new HashSet<>();
private static HashSet<Class<? extends PyObject>> reflectionWarnedClasses;
private static ReferenceQueue<Object> gcTrash;
private static int finalizeWaitCount = 0;
private static int initWaitTime = 10, defaultWaitFactor = 2;
private static long lastRemoveTimeStamp = -1, maxWaitTime = initWaitTime;
private static int gcMonitoredRunCount = 0;
public static long gcRecallTime = 4000;
/**
* list of uncollectable objects
*/
public static PyList garbage = new PyList();
/* Finalization preprocess/postprocess-related declarations: */
private static final List<Runnable> preFinalizationProcess = new ArrayList<>();
private static final List<Runnable> postFinalizationProcess = new ArrayList<>();
private static final List<Runnable> preFinalizationProcessRemove = new ArrayList<>();
private static final List<Runnable> postFinalizationProcessRemove = new ArrayList<>();
private static Thread postFinalizationProcessor;
public static long postFinalizationTimeOut = 100;
private static long postFinalizationTimestamp = System.currentTimeMillis()-2*postFinalizationTimeOut;
private static int openFinalizeCount = 0;
private static boolean postFinalizationPending = false;
private static boolean lockPostFinalization = false;
/* Resurrection-safe finalizer- and weakref-related declarations: */
private static IdentityHashMap<PyObject, PyObject> delayedFinalizables, resurrectionCriticals;
private static int abortedCyclicFinalizers = 0;
/* Some modes to control aspects of delayed finalization: */
private static final byte DO_NOTHING_SPECIAL = 0;
private static final byte MARK_REACHABLE_CRITICALS = 1;
private static final byte NOTIFY_FOR_RERUN = 2;
private static byte delayedFinalizationMode = DO_NOTHING_SPECIAL;
private static boolean notifyRerun = false;
public static final String __doc__ =
"This module provides access to the garbage collector for reference cycles.\n" +
"\n" +
"enable() -- Enable automatic garbage collection (does nothing in Jython).\n" +
"disable() -- Disable automatic garbage collection (raises NotImplementedError in Jython).\n" +
"isenabled() -- Returns True because Java garbage collection cannot be disabled.\n" +
"collect() -- Do a full collection right now (potentially expensive).\n" +
"get_count() -- Return the current collection counts (raises NotImplementedError in Jython).\n" +
"set_debug() -- Set debugging flags.\n" +
"get_debug() -- Get debugging flags.\n" +
"set_threshold() -- Set the collection thresholds (raise NotImplementedError in Jython).\n" +
"get_threshold() -- Return the current the collection thresholds (raise NotImplementedError in Jython).\n" +
"get_objects() -- Return a list of all objects tracked by the collector (raises NotImplementedError in Jython).\n" +
"is_tracked() -- Returns true if a given object is tracked (i.e. monitored in Jython).\n" +
"get_referrers() -- Return the list of objects that refer to an object (only finds monitored referrers in Jython).\n" +
"get_referents() -- Return the list of objects that an object refers to.\n";
public static final String __name__ = "gc";
public static final PyString __doc__enable = new PyString(
"enable() -> None\n" +
"\n" +
"Enable automatic garbage collection.\n" +
"(does nothing in Jython)\n");
public static final PyString __doc__disable = new PyString(
"disable() -> None\n" +
"\n" +
"Disable automatic garbage collection.\n" +
"(raises NotImplementedError in Jython)\n");
public static final PyString __doc__isenabled = new PyString(
"isenabled() -> status\n" +
"\n" +
"Returns true if automatic garbage collection is enabled.\n");
public static final PyString __doc__collect = new PyString(
"collect([generation]) -> n\n" +
"\n" +
"With no arguments, run a full collection. The optional argument\n" +
"may be an integer specifying which generation to collect. A ValueError\n" +
"is raised if the generation number is invalid.\n\n" +
"The number of unreachable objects is returned.\n" +
"(Jython emulates CPython cyclic trash counting if objects are monitored.\n" +
"If no objects are monitored, returns -2\n");
public static final PyString __doc__get_count = new PyString(
"get_count() -> (count0, count1, count2)\n" +
"\n" +
"Return the current collection counts\n" +
"(raises NotImplementedError in Jython)\n");
public static final PyString __doc__set_debug = new PyString(
"set_debug(flags) -> None\n" +
"\n" +
"Set the garbage collection debugging flags. Debugging information is\n" +
"written to sys.stderr.\n" +
"\n" +
"flags is an integer and can have the following bits turned on:\n" +
"\n" +
" DEBUG_STATS - Print statistics during collection.\n" +
" DEBUG_COLLECTABLE - Print collectable objects found.\n" +
" DEBUG_UNCOLLECTABLE - Print unreachable but uncollectable objects found.\n" +
" DEBUG_INSTANCES - Print instance objects.\n" +
" DEBUG_OBJECTS - Print objects other than instances.\n" +
" DEBUG_SAVEALL - Save objects to gc.garbage rather than freeing them.\n" +
" DEBUG_LEAK - Debug leaking programs (everything but STATS).\n");
public static final PyString __doc__get_debug = new PyString(
"get_debug() -> flags\n" +
"\n" +
"Get the garbage collection debugging flags.\n");
public static final PyString __doc__set_thresh = new PyString(
"set_threshold(threshold0, [threshold1, threshold2]) -> None\n" +
"\n" +
"Sets the collection thresholds. Setting threshold0 to zero disables\n" +
"collection.\n" +
"(raises NotImplementedError in Jython)\n");
public static final PyString __doc__get_thresh = new PyString(
"get_threshold() -> (threshold0, threshold1, threshold2)\n" +
"\n" +
"Return the current collection thresholds\n" +
"(raises NotImplementedError in Jython)\n");
public static final PyString __doc__get_objects = new PyString(
"get_objects() -> [...]\n" +
"\n" +
"Return a list of objects tracked by the collector (excluding the list\n" +
"returned).\n" +
"(raises NotImplementedError in Jython)\n");
public static final PyString __doc__is_tracked = new PyString(
"is_tracked(obj) -> bool\n" +
"\n" +
"Returns true if the object is tracked by the garbage collector.\n" +
"(i.e. monitored in Jython)\n");
public static final PyString __doc__get_referrers = new PyString(
"get_referrers(*objs) -> list\n" +
"Return the list of objects that directly refer to any of objs.\n" +
"(only finds monitored referrers in Jython)");
public static final PyString __doc__get_referents = new PyString(
"get_referents(*objs) -> list\n" +
"Return the list of objects that are directly referred to by objs.");
public static class CycleMarkAttr {
private boolean cyclic = false;
private boolean uncollectable = false;
public boolean monitored = false;
CycleMarkAttr() {
}
CycleMarkAttr(boolean cyclic, boolean uncollectable) {
this.cyclic = cyclic;
this.uncollectable = uncollectable;
}
public boolean isCyclic() {
return cyclic || uncollectable;
}
public boolean isUncollectable() {
return uncollectable;
}
public void setFlags(boolean cyclic, boolean uncollectable) {
this.cyclic = cyclic;
this.uncollectable = uncollectable;
}
}
private static class WeakReferenceGC extends WeakReference<PyObject> {
int hashCode = 0;
public String str = null, inst_str = null;
public String cls;
boolean isInstance;
boolean hasFinalizer = false;
CycleMarkAttr cycleMark;
WeakReferenceGC(PyObject referent) {
super(referent);
isInstance = referent instanceof PyInstance;
cycleMark = (CycleMarkAttr)
JyAttribute.getAttr(referent, JyAttribute.GC_CYCLE_MARK_ATTR);
hashCode = System.identityHashCode(referent);
cls = referent.getClass().getName();
updateHasFinalizer();
}
WeakReferenceGC(PyObject referent, ReferenceQueue<Object> q) {
super(referent, q);
isInstance = referent instanceof PyInstance;
cycleMark = (CycleMarkAttr)
JyAttribute.getAttr(referent, JyAttribute.GC_CYCLE_MARK_ATTR);
hashCode = System.identityHashCode(referent);
cls = referent.getClass().getName();
updateHasFinalizer();
}
public void updateHasFinalizer() {
PyObject gt = get();
Object fn = JyAttribute.getAttr(gt, JyAttribute.FINALIZE_TRIGGER_ATTR);
hasFinalizer = fn != null && ((FinalizeTrigger) fn).isActive();
}
public void initStr(PyObject referent) {
PyObject ref = referent;
if (referent == null) {
ref = get();
}
try {
if (ref instanceof PyInstance) {
String name = ((PyInstance) ref).fastGetClass().__name__;
if (name == null) {
name = "?";
}
inst_str = String.format("<%.100s instance at %s>",
name, Py.idstr(ref));
}
str = String.format("<%.100s %s>",
ref.getType().getName(), Py.idstr(ref));
} catch (Exception e) {
str = "<"+ref.getClass().getSimpleName()+" "
+System.identityHashCode(ref)+">";
}
}
@Override
public String toString() {
return str;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object ob) {
Object ownReferent = get();
if (ob instanceof WeakReferenceGC) {
Object otherReferent = ((WeakReferenceGC) ob).get();
if (ownReferent == null || otherReferent == null) {
return ownReferent == otherReferent &&
/* We compare the cached hash codes in order to get an idea
* whether in the both-null-case the referent was equal once.
*/
hashCode == ((WeakReferenceGC) ob).hashCode;
} else {
return otherReferent.equals(ownReferent)
/* Here the hash codes are only compared as a consistency check. */
&& ((WeakReferenceGC) ob).hashCode == hashCode;
}
} else if (ob instanceof WeakrefGCCompareDummy) {
if (ownReferent == null ||
((WeakrefGCCompareDummy) ob).compare == null) {
return ownReferent ==
((WeakrefGCCompareDummy) ob).compare &&
/* We compare the cached hash codes in order to get an idea
* whether in the both-null-case the referent was equal once.
*/
hashCode == ((WeakrefGCCompareDummy) ob).hashCode;
} else {
return ownReferent.equals(((WeakrefGCCompareDummy) ob).compare)
/* Here the hash codes are only compared as a consistency check. */
&& hashCode == ((WeakrefGCCompareDummy) ob).hashCode;
}
} else {
return false;
}
}
}
private static class WeakrefGCCompareDummy {
public static WeakrefGCCompareDummy defaultInstance =
new WeakrefGCCompareDummy();
protected PyObject compare;
int hashCode = 0;
public void setCompare(PyObject compare) {
this.compare = compare;
hashCode = System.identityHashCode(compare);
}
public void clearCompare() {
compare = null;
hashCode = 0;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
@SuppressWarnings("rawtypes")
public boolean equals(Object ob) {
if (ob instanceof Reference) {
return compare.equals(((Reference) ob).get());
} else if (ob instanceof WeakrefGCCompareDummy) {
return compare.equals(((WeakrefGCCompareDummy) ob).compare);
} else {
return compare.equals(ob);
}
}
}
private static class GCSentinel {
Thread waiting;
public GCSentinel(Thread notifyOnFinalize) {
waiting = notifyOnFinalize;
}
@Override
protected void finalize() throws Throwable {
notifyPreFinalization();
if ((gcFlags & VERBOSE_COLLECT) != 0) {
writeDebug("gc", "Sentinel finalizer called...");
}
if (lastRemoveTimeStamp != -1) {
long diff = maxWaitTime*defaultWaitFactor-System.currentTimeMillis()+lastRemoveTimeStamp;
while (diff > 0) {
try {
Thread.sleep(diff);
} catch (InterruptedException ie) {}
diff = maxWaitTime*defaultWaitFactor-System.currentTimeMillis()+lastRemoveTimeStamp;
}
}
if (waiting != null) {
waiting.interrupt();
}
if ((gcFlags & VERBOSE_COLLECT) != 0) {
writeDebug("gc", "Sentinel finalizer done");
}
notifyPostFinalization();
}
}
/**
* Works like {@link org.python.core.Py#writeDebug(String, String)},
* but prints to {@link org.python.core.Py#writeDebug(String, String)}
* (i.e. subject to Jython's verbose level)
* or directly to {@code System.err}, according to
* {@link #USE_PY_WRITE_DEBUG}.
*
* @see #USE_PY_WRITE_DEBUG
* @see org.python.core.Py#writeDebug(String, String)
*/
public static void writeDebug(String type, String msg) {
if ((gcFlags & USE_PY_WRITE_DEBUG) != 0) {
Py.writeDebug(type, msg);
} else {
System.err.println(type + ": " + msg);
}
}
//--------------delayed finalization section-----------------------------------
/**
* In addition to what
* {@link org.python.core.finalization.FinalizeTrigger#ensureFinalizer(PyObject)}
* does, this method also restores the finalizer's
* {@link org.python.core.finalization.FinalizeTrigger}'s flags by taking the
* values from the former finalizer. On the other hand - in contrast to
* {@link org.python.core.finalization.FinalizeTrigger#ensureFinalizer(PyObject)} -
* this method would not create a {@link org.python.core.finalization.FinalizeTrigger}
* for an object that did not have one before (i.e. the method checks for an old
* (dead) trigger before it creates a new one. <br><br>
* If a new finalizer is needed due to an
* ordinary resurrection (i.e. the object's finalizer was called),
* {@link org.python.core.finalization.FinalizeTrigger#ensureFinalizer(PyObject)}
* is the right choice. If a finalization was vetoed in context of delayed
* finalization (i.e. a resurrection that pretends not to be one and didn't run
* the finalizer), this method is the better choice as it helps to make the new
* {@link org.python.core.finalization.FinalizeTrigger} look exactly like the
* old one regarding flags etc.
* E.g. this method is called by {@link #abortDelayedFinalization(PyObject)}.
*
* @see #abortDelayedFinalization(PyObject)
*/
public static void restoreFinalizer(PyObject obj) {
FinalizeTrigger ft =
(FinalizeTrigger) JyAttribute.getAttr(obj, JyAttribute.FINALIZE_TRIGGER_ATTR);
boolean notify = false;
if (ft != null) {
FinalizeTrigger.ensureFinalizer(obj);
/* ensure that the old finalize won't run in any case */
ft.clear();
((FinalizeTrigger) JyAttribute.getAttr(obj,
JyAttribute.FINALIZE_TRIGGER_ATTR)).flags = ft.flags;
notify = (ft.flags & FinalizeTrigger.NOTIFY_GC_FLAG) != 0;
}
if ((gcFlags & VERBOSE_DELAYED) != 0 || (gcFlags & VERBOSE_FINALIZE) != 0) {
writeDebug("gc", "restore finalizer of "+obj);
}
CycleMarkAttr cm = (CycleMarkAttr)
JyAttribute.getAttr(obj, JyAttribute.GC_CYCLE_MARK_ATTR);
if (cm != null && cm.monitored) {
monitorObject(obj, true);
}
if (notify) {
boolean cyclic;
if (cm != null && cm.isUncollectable()) {
cyclic = true;
} else {
markCyclicObjects(obj, true);
cm = (CycleMarkAttr) JyAttribute.getAttr(obj, JyAttribute.GC_CYCLE_MARK_ATTR);
cyclic = cm != null && cm.isUncollectable();
}
if ((gcFlags & VERBOSE_DELAYED) != 0 || (gcFlags & VERBOSE_FINALIZE) != 0) {
writeDebug("gc", "notify finalizer abort; cyclic? "+cyclic);
}
notifyAbortFinalize(obj, cyclic);
}
}
/**
* Restores weak references pointing to {@code rst}. Note that
* this does not prevent callbacks, unless it is called during
* finalization phase (e.g. by a finalizer) and
* {@link #delayedWeakrefCallbacksEnabled()} returns {@code true}.
* In a manual fashion, one can enforce this by using the gc flag
* {@link #FORCE_DELAYED_WEAKREF_CALLBACKS}. Alternatively, one can
* use the automatic way via the gc flag
* {@link #PRESERVE_WEAKREFS_ON_RESURRECTION}, but then one would
* not need to call this method anyway. The manual way has better
* performance, but also brings more responsibilies.
*
* @see #delayedWeakrefCallbacksEnabled()
* @see #FORCE_DELAYED_WEAKREF_CALLBACKS
* @see #PRESERVE_WEAKREFS_ON_RESURRECTION
*/
public static void restoreWeakReferences(PyObject rst) {
ReferenceBackend toRestore = (ReferenceBackend)
JyAttribute.getAttr(rst, JyAttribute.WEAK_REF_ATTR);
if (toRestore != null) {
toRestore.restore(rst);
}
}
private static class DelayedFinalizationProcess implements Runnable {
static DelayedFinalizationProcess defaultInstance =
new DelayedFinalizationProcess();
private static void performFinalization(PyObject del) {
if ((gcFlags & VERBOSE_DELAYED) != 0) {
writeDebug("gc", "delayed finalize of "+del);
}
FinalizeTrigger ft = (FinalizeTrigger)
JyAttribute.getAttr(del, JyAttribute.FINALIZE_TRIGGER_ATTR);
if (ft != null) {
ft.performFinalization();
} else if ((gcFlags & VERBOSE_DELAYED) != 0) {
writeDebug("gc", "no FinalizeTrigger");
}
}
@Override
public void run() {
if ((gcFlags & VERBOSE_DELAYED) != 0) {
writeDebug("gc", "run delayed finalization. Index: "+
gcMonitoredRunCount);
}
Set<PyObject> criticals = resurrectionCriticals.keySet();
if (delayedFinalizationMode == DO_NOTHING_SPECIAL &&
(gcFlags & (PRESERVE_WEAKREFS_ON_RESURRECTION |
DONT_FINALIZE_RESURRECTED_OBJECTS)) == 0) {
/* In this case we can do a cheap variant... */
if ((gcFlags & FORCE_DELAYED_WEAKREF_CALLBACKS) != 0) {
if ((gcFlags & VERBOSE_DELAYED) != 0) {
writeDebug("gc", "process delayed callbacks (force-branch)");
}
GlobalRef.processDelayedCallbacks();
}
if ((gcFlags & FORCE_DELAYED_FINALIZATION) != 0) {
if ((gcFlags & VERBOSE_DELAYED) != 0) {
writeDebug("gc", "process delayed finalizers (force-branch)");
}
for (PyObject del: delayedFinalizables.keySet()) {
performFinalization(del);
}
for (PyObject cr: criticals) {
performFinalization(cr);
}
delayedFinalizables.clear();
resurrectionCriticals.clear();
}
if ((gcFlags & VERBOSE_DELAYED) != 0) {
writeDebug("gc", "forced delayed finalization run done");
}
return;
}
Set<PyObject> cyclicCriticals = removeNonCyclic(criticals);
cyclicCriticals.retainAll(criticals);
criticals.removeAll(cyclicCriticals);
Set<PyObject> criticalReachablePool = findReachables(criticals);
/* to avoid concurrent modification: */
ArrayList<PyObject> criticalReachables = new ArrayList<>();
FinalizeTrigger fn;
if (delayedFinalizationMode == MARK_REACHABLE_CRITICALS) {
for (PyObject obj: criticalReachablePool) {
fn = (FinalizeTrigger) JyAttribute.getAttr(obj,
JyAttribute.FINALIZE_TRIGGER_ATTR);
if (fn != null && fn.isActive() && fn.isFinalized()) {
criticalReachables.add(obj);
JyAttribute.setAttr(obj,
JyAttribute.GC_DELAYED_FINALIZE_CRITICAL_MARK_ATTR,
Integer.valueOf(gcMonitoredRunCount));
}
}
} else {
for (PyObject obj: criticalReachablePool) {
fn = (FinalizeTrigger) JyAttribute.getAttr(obj,
JyAttribute.FINALIZE_TRIGGER_ATTR);
if (fn != null && fn.isActive() && fn.isFinalized()) {
criticalReachables.add(obj);
}
}
}
criticals.removeAll(criticalReachables);
if ((gcFlags & PRESERVE_WEAKREFS_ON_RESURRECTION) != 0) {
if ((gcFlags & VERBOSE_DELAYED) != 0) {
writeDebug("gc", "restore potentially resurrected weak references...");
}
for (PyObject rst: criticalReachablePool) {
restoreWeakReferences(rst);
}
GlobalRef.processDelayedCallbacks();
}
criticalReachablePool.clear();
if ((gcFlags & DONT_FINALIZE_RESURRECTED_OBJECTS) != 0) {
/* restore all finalizers that might belong to resurrected objects: */
if ((gcFlags & VERBOSE_DELAYED) != 0) {
writeDebug("gc", "restore "+criticalReachables.size()+
" potentially resurrected finalizers...");
}
for (PyObject obj: criticalReachables) {