bpo-39489: Remove COUNT_ALLOCS special build (GH-18259)

Remove:

* COUNT_ALLOCS macro
* sys.getcounts() function
* SHOW_ALLOC_COUNT code in listobject.c
* SHOW_TRACK_COUNT code in tupleobject.c
* PyConfig.show_alloc_count field
* -X showalloccount command line option
* @test.support.requires_type_collecting decorator
diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst
index 108bd2c..c589a6f 100644
--- a/Doc/c-api/init_config.rst
+++ b/Doc/c-api/init_config.rst
@@ -627,14 +627,6 @@
 
       ``python3 -m MODULE`` argument. Used by :c:func:`Py_RunMain`.
 
-   .. c:member:: int show_alloc_count
-
-      Show allocation counts at exit?
-
-      Set to 1 by :option:`-X showalloccount <-X>` command line option.
-
-      Need a special Python build with ``COUNT_ALLOCS`` macro defined.
-
    .. c:member:: int show_ref_count
 
       Show total reference count at exit?
@@ -702,6 +694,10 @@
 The ``xoptions`` options are parsed to set other options: see :option:`-X`
 option.
 
+.. versionchanged:: 3.9
+
+   The ``show_alloc_count`` field has been removed.
+
 
 Initialization with PyConfig
 ----------------------------
diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst
index 7b205c0..a8a779e 100644
--- a/Doc/c-api/typeobj.rst
+++ b/Doc/c-api/typeobj.rst
@@ -148,15 +148,6 @@
    | :c:member:`~PyTypeObject.tp_vectorcall`        | :c:type:`vectorcallfunc`          |                   |   |   |   |   |
    +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+
 
-If :const:`COUNT_ALLOCS` is defined then the following (internal-only)
-fields exist as well:
-
-* :c:member:`~PyTypeObject.tp_allocs`
-* :c:member:`~PyTypeObject.tp_frees`
-* :c:member:`~PyTypeObject.tp_maxalloc`
-* :c:member:`~PyTypeObject.tp_prev`
-* :c:member:`~PyTypeObject.tp_next`
-
 .. [#slots]
    A slot name in parentheses indicates it is (effectively) deprecated.
    Names in angle brackets should be treated as read-only.
@@ -1904,31 +1895,6 @@
    .. versionadded:: 3.9 (the field exists since 3.8 but it's only used since 3.9)
 
 
-The remaining fields are only defined if the feature test macro
-:const:`COUNT_ALLOCS` is defined, and are for internal use only. They are
-documented here for completeness.  None of these fields are inherited by
-subtypes.
-
-.. c:member:: Py_ssize_t PyTypeObject.tp_allocs
-
-   Number of allocations.
-
-.. c:member:: Py_ssize_t PyTypeObject.tp_frees
-
-   Number of frees.
-
-.. c:member:: Py_ssize_t PyTypeObject.tp_maxalloc
-
-   Maximum simultaneously allocated objects.
-
-.. c:member:: PyTypeObject* PyTypeObject.tp_prev
-
-   Pointer to the previous type object with a non-zero :c:member:`~PyTypeObject.tp_allocs` field.
-
-.. c:member:: PyTypeObject* PyTypeObject.tp_next
-
-   Pointer to the next type object with a non-zero :c:member:`~PyTypeObject.tp_allocs` field.
-
 Also, note that, in a garbage collected Python, :c:member:`~PyTypeObject.tp_dealloc` may be called from
 any Python thread, not just the thread which created the object (if the object
 becomes part of a refcount cycle, that cycle might be collected by a garbage
diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst
index fb88673..2206e50 100644
--- a/Doc/using/cmdline.rst
+++ b/Doc/using/cmdline.rst
@@ -434,9 +434,6 @@
      stored in a traceback of a trace. Use ``-X tracemalloc=NFRAME`` to start
      tracing with a traceback limit of *NFRAME* frames. See the
      :func:`tracemalloc.start` for more information.
-   * ``-X showalloccount`` to output the total count of allocated objects for
-     each type when the program finishes. This only works when Python was built with
-     ``COUNT_ALLOCS`` defined.
    * ``-X importtime`` to show how long each import takes. It shows module
      name, cumulative time (including nested imports) and self time (excluding
      nested imports).  Note that its output may be broken in multi-threaded
@@ -479,6 +476,8 @@
       Using ``-X dev`` option, check *encoding* and *errors* arguments on
       string encoding and decoding operations.
 
+      The ``-X showalloccount`` option has been removed.
+
 
 Options you shouldn't use
 ~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst
index 931f8bf..3b7a9d1 100644
--- a/Doc/whatsnew/3.9.rst
+++ b/Doc/whatsnew/3.9.rst
@@ -348,6 +348,9 @@
   functions are now required to build Python.
   (Contributed by Victor Stinner in :issue:`39395`.)
 
+* The ``COUNT_ALLOCS`` special build macro has been removed.
+  (Contributed by Victor Stinner in :issue:`39489`.)
+
 
 Deprecated
 ==========
@@ -484,6 +487,12 @@
   ``asyncio.Condition`` and ``asyncio.Semaphore``.
   (Contributed by Andrew Svetlov in :issue:`34793`.)
 
+* The :func:`sys.getcounts` function, the ``-X showalloccount`` command line
+  option and the ``show_alloc_count`` field of the C structure
+  :c:type:`PyConfig` have been removed. They required a special Python build by
+  defining ``COUNT_ALLOCS`` macro.
+  (Contributed by Victor Stinner in :issue:`39489`.)
+
 
 Porting to Python 3.9
 =====================
diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h
index 54e6623..c5fa2b3 100644
--- a/Include/cpython/initconfig.h
+++ b/Include/cpython/initconfig.h
@@ -153,7 +153,6 @@
 
     int import_time;        /* PYTHONPROFILEIMPORTTIME, -X importtime */
     int show_ref_count;     /* -X showrefcount */
-    int show_alloc_count;   /* -X showalloccount */
     int dump_refs;          /* PYTHONDUMPREFS */
     int malloc_stats;       /* PYTHONMALLOCSTATS */
 
diff --git a/Include/cpython/object.h b/Include/cpython/object.h
index dc8fd6f..5fcad55 100644
--- a/Include/cpython/object.h
+++ b/Include/cpython/object.h
@@ -255,15 +255,6 @@
 
     destructor tp_finalize;
     vectorcallfunc tp_vectorcall;
-
-#ifdef COUNT_ALLOCS
-    /* these must be last and never explicitly initialized */
-    Py_ssize_t tp_allocs;
-    Py_ssize_t tp_frees;
-    Py_ssize_t tp_maxalloc;
-    struct _typeobject *tp_prev;
-    struct _typeobject *tp_next;
-#endif
 } PyTypeObject;
 
 /* The *real* layout of a type object when allocated on the heap */
@@ -341,8 +332,6 @@
     destructor dealloc = Py_TYPE(op)->tp_dealloc;
 #ifdef Py_TRACE_REFS
     _Py_ForgetReference(op);
-#else
-    _Py_INC_TPFREES(op);
 #endif
     (*dealloc)(op);
 }
diff --git a/Include/object.h b/Include/object.h
index 7a5f573..1a2e704 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -405,20 +405,6 @@
 #define _Py_DEC_REFTOTAL
 #endif /* Py_REF_DEBUG */
 
-#ifdef COUNT_ALLOCS
-PyAPI_FUNC(void) _Py_inc_count(struct _typeobject *);
-PyAPI_FUNC(void) _Py_dec_count(struct _typeobject *);
-#define _Py_INC_TPALLOCS(OP)    _Py_inc_count(Py_TYPE(OP))
-#define _Py_INC_TPFREES(OP)     _Py_dec_count(Py_TYPE(OP))
-#define _Py_DEC_TPFREES(OP)     Py_TYPE(OP)->tp_frees--
-#define _Py_COUNT_ALLOCS_COMMA  ,
-#else
-#define _Py_INC_TPALLOCS(OP)
-#define _Py_INC_TPFREES(OP)
-#define _Py_DEC_TPFREES(OP)
-#define _Py_COUNT_ALLOCS_COMMA
-#endif /* COUNT_ALLOCS */
-
 /* Update the Python traceback of an object. This function must be called
    when a memory block is reused from a free list. */
 PyAPI_FUNC(int) _PyTraceMalloc_NewReference(PyObject *op);
@@ -438,15 +424,13 @@
     if (_Py_tracemalloc_config.tracing) {
         _PyTraceMalloc_NewReference(op);
     }
-    _Py_INC_TPALLOCS(op);
     _Py_INC_REFTOTAL;
     Py_REFCNT(op) = 1;
 }
 
-static inline void _Py_ForgetReference(PyObject *op)
+static inline void _Py_ForgetReference(PyObject *Py_UNUSED(op))
 {
-    (void)op; /* may be unused, shut up -Wunused-parameter */
-    _Py_INC_TPFREES(op);
+    /* nothing to do */
 }
 #endif /* !Py_TRACE_REFS */
 
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index 26a1e69..c8db387 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -325,7 +325,7 @@
     if dev_mode:
         args.extend(('-X', 'dev'))
     for opt in ('faulthandler', 'tracemalloc', 'importtime',
-                'showalloccount', 'showrefcount', 'utf8'):
+                'showrefcount', 'utf8'):
         if opt in xoptions:
             value = xoptions[opt]
             if value is True:
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index 215bab8..259c706 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -2512,9 +2512,6 @@
             if item in obj:
                 del obj[item]
 
-requires_type_collecting = unittest.skipIf(hasattr(sys, 'getcounts'),
-                        'types are immortal if COUNT_ALLOCS is defined')
-
 def args_from_interpreter_flags():
     """Return a list of command-line arguments reproducing the current
     settings in sys.flags and sys.warnoptions."""
diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py
index 73ef962..87842b9 100644
--- a/Lib/test/test_embed.py
+++ b/Lib/test/test_embed.py
@@ -356,7 +356,6 @@
         'tracemalloc': 0,
         'import_time': 0,
         'show_ref_count': 0,
-        'show_alloc_count': 0,
         'dump_refs': 0,
         'malloc_stats': 0,
 
@@ -729,7 +728,6 @@
             'tracemalloc': 2,
             'import_time': 1,
             'show_ref_count': 1,
-            'show_alloc_count': 1,
             'malloc_stats': 1,
 
             'stdio_encoding': 'iso8859-1',
diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py
index 18f8d10..acb6391 100644
--- a/Lib/test/test_gc.py
+++ b/Lib/test/test_gc.py
@@ -2,7 +2,7 @@
 import unittest.mock
 from test.support import (verbose, refcount_test, run_unittest,
                           cpython_only, start_threads,
-                          temp_dir, requires_type_collecting, TESTFN, unlink,
+                          temp_dir, TESTFN, unlink,
                           import_module)
 from test.support.script_helper import assert_python_ok, make_script
 
@@ -131,7 +131,6 @@
         del a
         self.assertNotEqual(gc.collect(), 0)
 
-    @requires_type_collecting
     def test_newinstance(self):
         class A(object):
             pass
@@ -709,7 +708,6 @@
         stderr = run_command(code % "gc.DEBUG_SAVEALL")
         self.assertNotIn(b"uncollectable objects at shutdown", stderr)
 
-    @requires_type_collecting
     def test_gc_main_module_at_shutdown(self):
         # Create a reference cycle through the __main__ module and check
         # it gets collected at interpreter shutdown.
@@ -723,7 +721,6 @@
         rc, out, err = assert_python_ok('-c', code)
         self.assertEqual(out.strip(), b'__del__ called')
 
-    @requires_type_collecting
     def test_gc_ordinary_module_at_shutdown(self):
         # Same as above, but with a non-__main__ module.
         with temp_dir() as script_dir:
@@ -743,7 +740,6 @@
             rc, out, err = assert_python_ok('-c', code)
             self.assertEqual(out.strip(), b'__del__ called')
 
-    @requires_type_collecting
     def test_global_del_SystemExit(self):
         code = """if 1:
             class ClassWithDel:
diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py
index 501e931..8a123fa 100644
--- a/Lib/test/test_io.py
+++ b/Lib/test/test_io.py
@@ -3492,7 +3492,6 @@
             """.format(iomod=iomod, kwargs=kwargs)
         return assert_python_ok("-c", code)
 
-    @support.requires_type_collecting
     def test_create_at_shutdown_without_encoding(self):
         rc, out, err = self._check_create_at_shutdown()
         if err:
@@ -3502,7 +3501,6 @@
         else:
             self.assertEqual("ok", out.decode().strip())
 
-    @support.requires_type_collecting
     def test_create_at_shutdown_with_encoding(self):
         rc, out, err = self._check_create_at_shutdown(encoding='utf-8',
                                                       errors='strict')
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index c38fdae..e223522 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -4252,7 +4252,6 @@
             h.close()
             logging.setLoggerClass(logging.Logger)
 
-    @support.requires_type_collecting
     def test_logging_at_shutdown(self):
         # Issue #20037
         code = """if 1:
diff --git a/Lib/test/test_module.py b/Lib/test/test_module.py
index efe9a8e..1d44563 100644
--- a/Lib/test/test_module.py
+++ b/Lib/test/test_module.py
@@ -1,7 +1,7 @@
 # Test the module type
 import unittest
 import weakref
-from test.support import gc_collect, requires_type_collecting
+from test.support import gc_collect
 from test.support.script_helper import assert_python_ok
 
 import sys
@@ -101,7 +101,6 @@
         gc_collect()
         self.assertEqual(f().__dict__["bar"], 4)
 
-    @requires_type_collecting
     def test_clear_dict_in_ref_cycle(self):
         destroyed = []
         m = ModuleType("foo")
@@ -266,7 +265,6 @@
         self.assertEqual(r[-len(ends_with):], ends_with,
                          '{!r} does not end with {!r}'.format(r, ends_with))
 
-    @requires_type_collecting
     def test_module_finalization_at_shutdown(self):
         # Module globals and builtins should still be available during shutdown
         rc, out, err = assert_python_ok("-c", "from test import final_a")
diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py
index 2f347bd..175f7c8 100644
--- a/Lib/test/test_support.py
+++ b/Lib/test/test_support.py
@@ -493,7 +493,6 @@
             ['-Wignore', '-X', 'dev'],
             ['-X', 'faulthandler'],
             ['-X', 'importtime'],
-            ['-X', 'showalloccount'],
             ['-X', 'showrefcount'],
             ['-X', 'tracemalloc'],
             ['-X', 'tracemalloc=3'],
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index 947c935..58701a1 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -819,7 +819,6 @@
         c = sys.getallocatedblocks()
         self.assertIn(c, range(b - 50, b + 50))
 
-    @test.support.requires_type_collecting
     def test_is_finalizing(self):
         self.assertIs(sys.is_finalizing(), False)
         # Don't use the atexit module because _Py_Finalizing is only set
@@ -841,7 +840,6 @@
         rc, stdout, stderr = assert_python_ok('-c', code)
         self.assertEqual(stdout.rstrip(), b'True')
 
-    @test.support.requires_type_collecting
     def test_issue20602(self):
         # sys.flags and sys.float_info were wiped during shutdown.
         code = """if 1:
@@ -1295,8 +1293,6 @@
         # type
         # static type: PyTypeObject
         fmt = 'P2nPI13Pl4Pn9Pn11PIPP'
-        if hasattr(sys, 'getcounts'):
-            fmt += '3n2P'
         s = vsize(fmt)
         check(int, s)
         # class
diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py
index 62f2d54..a9d31af 100644
--- a/Lib/test/test_threading.py
+++ b/Lib/test/test_threading.py
@@ -3,8 +3,7 @@
 """
 
 import test.support
-from test.support import (verbose, import_module, cpython_only,
-                          requires_type_collecting)
+from test.support import verbose, import_module, cpython_only
 from test.support.script_helper import assert_python_ok, assert_python_failure
 
 import random
@@ -552,7 +551,6 @@
         self.assertEqual(err, b"")
         self.assertEqual(data, "Thread-1\nTrue\nTrue\n")
 
-    @requires_type_collecting
     def test_main_thread_during_shutdown(self):
         # bpo-31516: current_thread() should still point to the main thread
         # at shutdown
@@ -1113,7 +1111,6 @@
         self.assertIn("ZeroDivisionError", err)
         self.assertNotIn("Unhandled exception", err)
 
-    @requires_type_collecting
     def test_print_exception_stderr_is_none_1(self):
         script = r"""if True:
             import sys
diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py
index 7135d99..60e0b58 100644
--- a/Lib/test/test_traceback.py
+++ b/Lib/test/test_traceback.py
@@ -174,7 +174,6 @@
         # Issue #18960: coding spec should have no effect
         do_test("x=0\n# coding: GBK\n", "h\xe9 ho", 'utf-8', 5)
 
-    @support.requires_type_collecting
     def test_print_traceback_at_exit(self):
         # Issue #22599: Ensure that it is possible to use the traceback module
         # to display an exception at Python exit
diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py
index c6fb097..268ecb0 100644
--- a/Lib/test/test_warnings/__init__.py
+++ b/Lib/test/test_warnings/__init__.py
@@ -1227,7 +1227,6 @@
 
 
 class FinalizationTest(unittest.TestCase):
-    @support.requires_type_collecting
     def test_finalization(self):
         # Issue #19421: warnings.warn() should not crash
         # during Python finalization
diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py
index 228bc17..63c7255 100644
--- a/Lib/test/test_weakref.py
+++ b/Lib/test/test_weakref.py
@@ -649,7 +649,6 @@
         del c1, c2, C, D
         gc.collect()
 
-    @support.requires_type_collecting
     def test_callback_in_cycle_resurrection(self):
         import gc
 
diff --git a/Misc/NEWS.d/next/Build/2020-01-29-19-17-02.bpo-39489.HKPzv-.rst b/Misc/NEWS.d/next/Build/2020-01-29-19-17-02.bpo-39489.HKPzv-.rst
new file mode 100644
index 0000000..652a435
--- /dev/null
+++ b/Misc/NEWS.d/next/Build/2020-01-29-19-17-02.bpo-39489.HKPzv-.rst
@@ -0,0 +1 @@
+Remove ``COUNT_ALLOCS`` special build.
diff --git a/Misc/SpecialBuilds.txt b/Misc/SpecialBuilds.txt
index d1a0321..27369ab 100644
--- a/Misc/SpecialBuilds.txt
+++ b/Misc/SpecialBuilds.txt
@@ -46,9 +46,7 @@
 Turn on heavy reference debugging.  This is major surgery.  Every PyObject grows
 two more pointers, to maintain a doubly-linked list of all live heap-allocated
 objects.  Most built-in type objects are not in this list, as they're statically
-allocated.  Starting in Python 2.3, if COUNT_ALLOCS (see below) is also defined,
-a static type object T does appear in this list if at least one object of type T
-has been created.
+allocated.
 
 Note that because the fundamental PyObject layout changes, Python modules
 compiled with Py_TRACE_REFS are incompatible with modules compiled without it.
@@ -165,55 +163,6 @@
 "#ifdef Py_DEBUG" blocks.
 
 
-COUNT_ALLOCS
-------------
-
-Each type object grows three new members:
-
-    /* Number of times an object of this type was allocated. */
-    int tp_allocs;
-
-    /* Number of times an object of this type was deallocated. */
-    int tp_frees;
-
-    /* Highwater mark:  the maximum value of tp_allocs - tp_frees so
-     * far; or, IOW, the largest number of objects of this type alive at
-     * the same time.
-     */
-    int tp_maxalloc;
-
-Allocation and deallocation code keeps these counts up to date.  Py_FinalizeEx()
-displays a summary of the info returned by sys.getcounts() (see below), along
-with assorted other special allocation counts (like the number of tuple
-allocations satisfied by a tuple free-list, the number of 1-character strings
-allocated, etc).
-
-Before Python 2.2, type objects were immortal, and the COUNT_ALLOCS
-implementation relies on that.  As of Python 2.2, heap-allocated type/ class
-objects can go away.  COUNT_ALLOCS can blow up in 2.2 and 2.2.1 because of this;
-this was fixed in 2.2.2.  Use of COUNT_ALLOCS makes all heap-allocated type
-objects immortal, except for those for which no object of that type is ever
-allocated.
-
-Starting with Python 2.3, If Py_TRACE_REFS is also defined, COUNT_ALLOCS
-arranges to ensure that the type object for each allocated object appears in the
-doubly-linked list of all objects maintained by Py_TRACE_REFS.
-
-Special gimmicks:
-
-sys.getcounts()
-    Return a list of 4-tuples, one entry for each type object for which at least
-    one object of that type was allocated.  Each tuple is of the form:
-
-        (tp_name, tp_allocs, tp_frees, tp_maxalloc)
-
-    Each distinct type object gets a distinct entry in this list, even if two or
-    more type objects have the same tp_name (in which case there's no way to
-    distinguish them by looking at this list).  The list is ordered by time of
-    first object allocation: the type object for which the first allocation of
-    an object of that type occurred most recently is at the front of the list.
-
-
 LLTRACE
 -------
 
diff --git a/Misc/python.man b/Misc/python.man
index 3645b02..89a15a5 100644
--- a/Misc/python.man
+++ b/Misc/python.man
@@ -286,10 +286,6 @@
         traceback of a trace. Use -X tracemalloc=NFRAME to start tracing with a
         traceback limit of NFRAME frames
 
-    -X showalloccount: output the total count of allocated objects for each
-        type when the program finishes. This only works when Python was built with
-        COUNT_ALLOCS defined
-
     -X importtime: show how long each import takes. It shows module name,
         cumulative time (including nested imports) and self time (excluding
         nested imports). Note that its output may be broken in multi-threaded
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 943bee6..4dcfde4 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -3589,16 +3589,6 @@
     /* If Py_REF_DEBUG, _Py_NewReference bumped _Py_RefTotal, so
      * we need to undo that. */
     _Py_DEC_REFTOTAL;
-    /* If Py_TRACE_REFS, _Py_NewReference re-added self to the object
-     * chain, so no more to do there.
-     * If COUNT_ALLOCS, the original decref bumped tp_frees, and
-     * _Py_NewReference bumped tp_allocs:  both of those need to be
-     * undone.
-     */
-#ifdef COUNT_ALLOCS
-    --Py_TYPE(self)->tp_frees;
-    --Py_TYPE(self)->tp_allocs;
-#endif
 }
 
 static PyObject *
diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c
index 5fd92f7..00151b8 100644
--- a/Objects/bytesobject.c
+++ b/Objects/bytesobject.c
@@ -18,10 +18,6 @@
 
 #include "clinic/bytesobject.c.h"
 
-#ifdef COUNT_ALLOCS
-Py_ssize_t _Py_null_strings, _Py_one_strings;
-#endif
-
 static PyBytesObject *characters[UCHAR_MAX + 1];
 static PyBytesObject *nullstring;
 
@@ -68,9 +64,6 @@
     assert(size >= 0);
 
     if (size == 0 && (op = nullstring) != NULL) {
-#ifdef COUNT_ALLOCS
-        _Py_null_strings++;
-#endif
         Py_INCREF(op);
         return (PyObject *)op;
     }
@@ -112,9 +105,6 @@
     if (size == 1 && str != NULL &&
         (op = characters[*str & UCHAR_MAX]) != NULL)
     {
-#ifdef COUNT_ALLOCS
-        _Py_one_strings++;
-#endif
         Py_INCREF(op);
         return (PyObject *)op;
     }
@@ -148,16 +138,10 @@
         return NULL;
     }
     if (size == 0 && (op = nullstring) != NULL) {
-#ifdef COUNT_ALLOCS
-        _Py_null_strings++;
-#endif
         Py_INCREF(op);
         return (PyObject *)op;
     }
     if (size == 1 && (op = characters[*str & UCHAR_MAX]) != NULL) {
-#ifdef COUNT_ALLOCS
-        _Py_one_strings++;
-#endif
         Py_INCREF(op);
         return (PyObject *)op;
     }
diff --git a/Objects/listobject.c b/Objects/listobject.c
index 2c07ceb..c93a0fe 100644
--- a/Objects/listobject.c
+++ b/Objects/listobject.c
@@ -94,29 +94,6 @@
     return 0;
 }
 
-/* Debug statistic to compare allocations with reuse through the free list */
-#undef SHOW_ALLOC_COUNT
-#ifdef SHOW_ALLOC_COUNT
-static size_t count_alloc = 0;
-static size_t count_reuse = 0;
-
-static void
-show_alloc(void)
-{
-    PyInterpreterState *interp = _PyInterpreterState_Get();
-    if (!interp->config.show_alloc_count) {
-        return;
-    }
-
-    fprintf(stderr, "List allocations: %" PY_FORMAT_SIZE_T "d\n",
-        count_alloc);
-    fprintf(stderr, "List reuse through freelist: %" PY_FORMAT_SIZE_T
-        "d\n", count_reuse);
-    fprintf(stderr, "%.2f%% reuse rate\n\n",
-        (100.0*count_reuse/(count_alloc+count_reuse)));
-}
-#endif
-
 /* Empty list reuse scheme to save calls to malloc and free */
 #ifndef PyList_MAXFREELIST
 #define PyList_MAXFREELIST 80
@@ -156,13 +133,6 @@
 PyList_New(Py_ssize_t size)
 {
     PyListObject *op;
-#ifdef SHOW_ALLOC_COUNT
-    static int initialized = 0;
-    if (!initialized) {
-        Py_AtExit(show_alloc);
-        initialized = 1;
-    }
-#endif
 
     if (size < 0) {
         PyErr_BadInternalCall();
@@ -172,16 +142,10 @@
         numfree--;
         op = free_list[numfree];
         _Py_NewReference((PyObject *)op);
-#ifdef SHOW_ALLOC_COUNT
-        count_reuse++;
-#endif
     } else {
         op = PyObject_GC_New(PyListObject, &PyList_Type);
         if (op == NULL)
             return NULL;
-#ifdef SHOW_ALLOC_COUNT
-        count_alloc++;
-#endif
     }
     if (size <= 0)
         op->ob_item = NULL;
diff --git a/Objects/longobject.c b/Objects/longobject.c
index 124b837..9115fa1 100644
--- a/Objects/longobject.c
+++ b/Objects/longobject.c
@@ -35,10 +35,6 @@
 #define IS_SMALL_INT(ival) (-NSMALLNEGINTS <= (ival) && (ival) < NSMALLPOSINTS)
 #define IS_SMALL_UINT(ival) ((ival) < NSMALLPOSINTS)
 
-#ifdef COUNT_ALLOCS
-Py_ssize_t _Py_quick_int_allocs, _Py_quick_neg_int_allocs;
-#endif
-
 static PyObject *
 get_small_int(sdigit ival)
 {
@@ -46,12 +42,6 @@
     PyThreadState *tstate = _PyThreadState_GET();
     PyObject *v = (PyObject*)tstate->interp->small_ints[ival + NSMALLNEGINTS];
     Py_INCREF(v);
-#ifdef COUNT_ALLOCS
-    if (ival >= 0)
-        _Py_quick_int_allocs++;
-    else
-        _Py_quick_neg_int_allocs++;
-#endif
     return v;
 }
 
diff --git a/Objects/object.c b/Objects/object.c
index 67a6386..2154d11 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -113,120 +113,6 @@
 }
 #endif  /* Py_TRACE_REFS */
 
-#ifdef COUNT_ALLOCS
-static PyTypeObject *type_list;
-/* All types are added to type_list, at least when
-   they get one object created. That makes them
-   immortal, which unfortunately contributes to
-   garbage itself. If unlist_types_without_objects
-   is set, they will be removed from the type_list
-   once the last object is deallocated. */
-static int unlist_types_without_objects;
-extern Py_ssize_t _Py_tuple_zero_allocs, _Py_fast_tuple_allocs;
-extern Py_ssize_t _Py_quick_int_allocs, _Py_quick_neg_int_allocs;
-extern Py_ssize_t _Py_null_strings, _Py_one_strings;
-void
-_Py_dump_counts(FILE* f)
-{
-    PyInterpreterState *interp = _PyInterpreterState_Get();
-    if (!interp->config.show_alloc_count) {
-        return;
-    }
-
-    PyTypeObject *tp;
-    for (tp = type_list; tp; tp = tp->tp_next)
-        fprintf(f, "%s alloc'd: %" PY_FORMAT_SIZE_T "d, "
-            "freed: %" PY_FORMAT_SIZE_T "d, "
-            "max in use: %" PY_FORMAT_SIZE_T "d\n",
-            tp->tp_name, tp->tp_allocs, tp->tp_frees,
-            tp->tp_maxalloc);
-    fprintf(f, "fast tuple allocs: %" PY_FORMAT_SIZE_T "d, "
-        "empty: %" PY_FORMAT_SIZE_T "d\n",
-        _Py_fast_tuple_allocs, _Py_tuple_zero_allocs);
-    fprintf(f, "fast int allocs: pos: %" PY_FORMAT_SIZE_T "d, "
-        "neg: %" PY_FORMAT_SIZE_T "d\n",
-        _Py_quick_int_allocs, _Py_quick_neg_int_allocs);
-    fprintf(f, "null strings: %" PY_FORMAT_SIZE_T "d, "
-        "1-strings: %" PY_FORMAT_SIZE_T "d\n",
-        _Py_null_strings, _Py_one_strings);
-}
-
-PyObject *
-_Py_get_counts(void)
-{
-    PyTypeObject *tp;
-    PyObject *result;
-    PyObject *v;
-
-    result = PyList_New(0);
-    if (result == NULL)
-        return NULL;
-    for (tp = type_list; tp; tp = tp->tp_next) {
-        v = Py_BuildValue("(snnn)", tp->tp_name, tp->tp_allocs,
-                          tp->tp_frees, tp->tp_maxalloc);
-        if (v == NULL) {
-            Py_DECREF(result);
-            return NULL;
-        }
-        if (PyList_Append(result, v) < 0) {
-            Py_DECREF(v);
-            Py_DECREF(result);
-            return NULL;
-        }
-        Py_DECREF(v);
-    }
-    return result;
-}
-
-void
-_Py_inc_count(PyTypeObject *tp)
-{
-    if (tp->tp_next == NULL && tp->tp_prev == NULL) {
-        /* first time; insert in linked list */
-        if (type_list)
-            type_list->tp_prev = tp;
-        tp->tp_next = type_list;
-        /* Note that as of Python 2.2, heap-allocated type objects
-         * can go away, but this code requires that they stay alive
-         * until program exit.  That's why we're careful with
-         * refcounts here.  type_list gets a new reference to tp,
-         * while ownership of the reference type_list used to hold
-         * (if any) was transferred to tp->tp_next in the line above.
-         * tp is thus effectively immortal after this.
-         */
-        Py_INCREF(tp);
-        type_list = tp;
-#ifdef Py_TRACE_REFS
-        /* Also insert in the doubly-linked list of all objects,
-         * if not already there.
-         */
-        _Py_AddToAllObjects((PyObject *)tp, 0);
-#endif
-    }
-    tp->tp_allocs++;
-    if (tp->tp_allocs - tp->tp_frees > tp->tp_maxalloc)
-        tp->tp_maxalloc = tp->tp_allocs - tp->tp_frees;
-}
-
-void _Py_dec_count(PyTypeObject *tp)
-{
-    tp->tp_frees++;
-    if (unlist_types_without_objects &&
-        tp->tp_allocs == tp->tp_frees) {
-        /* unlink the type from type_list */
-        if (tp->tp_prev)
-            tp->tp_prev->tp_next = tp->tp_next;
-        else
-            type_list = tp->tp_next;
-        if (tp->tp_next)
-            tp->tp_next->tp_prev = tp->tp_prev;
-        tp->tp_next = tp->tp_prev = NULL;
-        Py_DECREF(tp);
-    }
-}
-
-#endif
-
 #ifdef Py_REF_DEBUG
 /* Log a fatal error; doesn't return. */
 void
@@ -349,15 +235,6 @@
     /* If Py_REF_DEBUG, _Py_NewReference bumped _Py_RefTotal, so
      * we need to undo that. */
     _Py_DEC_REFTOTAL;
-    /* If Py_TRACE_REFS, _Py_NewReference re-added self to the object
-     * chain, so no more to do there.
-     * If COUNT_ALLOCS, the original decref bumped tp_frees, and
-     * _Py_NewReference bumped tp_allocs:  both of those need to be
-     * undone. */
-#ifdef COUNT_ALLOCS
-    --Py_TYPE(self)->tp_frees;
-    --Py_TYPE(self)->tp_allocs;
-#endif
     return -1;
 }
 
@@ -1970,7 +1847,6 @@
     op->_ob_next->_ob_prev = op->_ob_prev;
     op->_ob_prev->_ob_next = op->_ob_next;
     op->_ob_next = op->_ob_prev = NULL;
-    _Py_INC_TPFREES(op);
 }
 
 /* Print all live objects.  Because PyObject_Print is called, the
@@ -2289,8 +2165,6 @@
     destructor dealloc = Py_TYPE(op)->tp_dealloc;
 #ifdef Py_TRACE_REFS
     _Py_ForgetReference(op);
-#else
-    _Py_INC_TPFREES(op);
 #endif
     (*dealloc)(op);
 }
diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c
index 08f7022..2708b9a 100644
--- a/Objects/tupleobject.c
+++ b/Objects/tupleobject.c
@@ -28,43 +28,10 @@
 static PyTupleObject *free_list[PyTuple_MAXSAVESIZE];
 static int numfree[PyTuple_MAXSAVESIZE];
 #endif
-#ifdef COUNT_ALLOCS
-Py_ssize_t _Py_fast_tuple_allocs;
-Py_ssize_t _Py_tuple_zero_allocs;
-#endif
-
-/* Debug statistic to count GC tracking of tuples.
-   Please note that tuples are only untracked when considered by the GC, and
-   many of them will be dead before. Therefore, a tracking rate close to 100%
-   does not necessarily prove that the heuristic is inefficient.
-*/
-#ifdef SHOW_TRACK_COUNT
-static Py_ssize_t count_untracked = 0;
-static Py_ssize_t count_tracked = 0;
-
-static void
-show_track(void)
-{
-    PyInterpreterState *interp = _PyInterpreterState_Get();
-    if (!interp->config.show_alloc_count) {
-        return;
-    }
-
-    fprintf(stderr, "Tuples created: %" PY_FORMAT_SIZE_T "d\n",
-        count_tracked + count_untracked);
-    fprintf(stderr, "Tuples tracked by the GC: %" PY_FORMAT_SIZE_T
-        "d\n", count_tracked);
-    fprintf(stderr, "%.2f%% tuple tracking rate\n\n",
-        (100.0*count_tracked/(count_untracked+count_tracked)));
-}
-#endif
 
 static inline void
 tuple_gc_track(PyTupleObject *op)
 {
-#ifdef SHOW_TRACK_COUNT
-    count_tracked++;
-#endif
     _PyObject_GC_TRACK(op);
 }
 
@@ -106,9 +73,6 @@
         assert(size != 0);
         free_list[size] = (PyTupleObject *) op->ob_item[0];
         numfree[size]--;
-#ifdef COUNT_ALLOCS
-        _Py_fast_tuple_allocs++;
-#endif
         /* Inline PyObject_InitVar */
 #ifdef Py_TRACE_REFS
         Py_SIZE(op) = size;
@@ -139,9 +103,6 @@
     if (size == 0 && free_list[0]) {
         op = free_list[0];
         Py_INCREF(op);
-#ifdef COUNT_ALLOCS
-        _Py_tuple_zero_allocs++;
-#endif
         return (PyObject *) op;
     }
 #endif
@@ -227,10 +188,6 @@
             _PyObject_GC_MAY_BE_TRACKED(elt))
             return;
     }
-#ifdef SHOW_TRACK_COUNT
-    count_tracked--;
-    count_untracked++;
-#endif
     _PyObject_GC_UNTRACK(op);
 }
 
@@ -1001,9 +958,6 @@
 
     (void)PyTuple_ClearFreeList();
 #endif
-#ifdef SHOW_TRACK_COUNT
-    show_track();
-#endif
 }
 
 /*********************** Tuple Iterator **************************/
diff --git a/Programs/_testembed.c b/Programs/_testembed.c
index b98a38a..b98696c 100644
--- a/Programs/_testembed.c
+++ b/Programs/_testembed.c
@@ -506,7 +506,6 @@
     config.import_time = 1;
 
     config.show_ref_count = 1;
-    config.show_alloc_count = 1;
     /* FIXME: test dump_refs: bpo-34223 */
 
     putenv("PYTHONMALLOCSTATS=0");
diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h
index daca0e6..4615eba 100644
--- a/Python/clinic/sysmodule.c.h
+++ b/Python/clinic/sysmodule.c.h
@@ -758,27 +758,6 @@
     return return_value;
 }
 
-#if defined(COUNT_ALLOCS)
-
-PyDoc_STRVAR(sys_getcounts__doc__,
-"getcounts($module, /)\n"
-"--\n"
-"\n");
-
-#define SYS_GETCOUNTS_METHODDEF    \
-    {"getcounts", (PyCFunction)sys_getcounts, METH_NOARGS, sys_getcounts__doc__},
-
-static PyObject *
-sys_getcounts_impl(PyObject *module);
-
-static PyObject *
-sys_getcounts(PyObject *module, PyObject *Py_UNUSED(ignored))
-{
-    return sys_getcounts_impl(module);
-}
-
-#endif /* defined(COUNT_ALLOCS) */
-
 PyDoc_STRVAR(sys__getframe__doc__,
 "_getframe($module, depth=0, /)\n"
 "--\n"
@@ -988,11 +967,7 @@
     #define SYS_GETTOTALREFCOUNT_METHODDEF
 #endif /* !defined(SYS_GETTOTALREFCOUNT_METHODDEF) */
 
-#ifndef SYS_GETCOUNTS_METHODDEF
-    #define SYS_GETCOUNTS_METHODDEF
-#endif /* !defined(SYS_GETCOUNTS_METHODDEF) */
-
 #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF
     #define SYS_GETANDROIDAPILEVEL_METHODDEF
 #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */
-/*[clinic end generated code: output=decd687b7631de4b input=a9049054013a1b77]*/
+/*[clinic end generated code: output=39eb34a01fb9a919 input=a9049054013a1b77]*/
diff --git a/Python/initconfig.c b/Python/initconfig.c
index 9a784c7..493b4bb 100644
--- a/Python/initconfig.c
+++ b/Python/initconfig.c
@@ -73,9 +73,6 @@
              tracemalloc module. By default, only the most recent frame is stored in a\n\
              traceback of a trace. Use -X tracemalloc=NFRAME to start tracing with a\n\
              traceback limit of NFRAME frames\n\
-         -X showalloccount: output the total count of allocated objects for each\n\
-             type when the program finishes. This only works when Python was built with\n\
-             COUNT_ALLOCS defined\n\
          -X importtime: show how long each import takes. It shows module name,\n\
              cumulative time (including nested imports) and self time (excluding\n\
              nested imports). Note that its output may be broken in multi-threaded\n\
@@ -800,7 +797,6 @@
     COPY_ATTR(tracemalloc);
     COPY_ATTR(import_time);
     COPY_ATTR(show_ref_count);
-    COPY_ATTR(show_alloc_count);
     COPY_ATTR(dump_refs);
     COPY_ATTR(malloc_stats);
 
@@ -903,7 +899,6 @@
     SET_ITEM_INT(tracemalloc);
     SET_ITEM_INT(import_time);
     SET_ITEM_INT(show_ref_count);
-    SET_ITEM_INT(show_alloc_count);
     SET_ITEM_INT(dump_refs);
     SET_ITEM_INT(malloc_stats);
     SET_ITEM_WSTR(filesystem_encoding);
@@ -1691,9 +1686,6 @@
     if (config_get_xoption(config, L"showrefcount")) {
         config->show_ref_count = 1;
     }
-    if (config_get_xoption(config, L"showalloccount")) {
-        config->show_alloc_count = 1;
-    }
 
     status = config_read_complex_options(config);
     if (_PyStatus_EXCEPTION(status)) {
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index d5d60d0..f130735 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -1173,10 +1173,6 @@
 }
 
 
-#ifdef COUNT_ALLOCS
-extern void _Py_dump_counts(FILE*);
-#endif
-
 /* Flush stdout and stderr */
 
 static int
@@ -1393,13 +1389,6 @@
      * XXX I haven't seen a real-life report of either of these.
      */
     _PyGC_CollectIfEnabled();
-#ifdef COUNT_ALLOCS
-    /* With COUNT_ALLOCS, it helps to run GC multiple times:
-       each collection might release some types from the type
-       list, so they become garbage. */
-    while (_PyGC_CollectIfEnabled() > 0)
-        /* nothing */;
-#endif
 
     /* Clear all loghooks */
     /* We want minimal exposure of this function, so define the extern
@@ -1451,10 +1440,6 @@
     /* unload faulthandler module */
     _PyFaulthandler_Fini();
 
-    /* Debugging stuff */
-#ifdef COUNT_ALLOCS
-    _Py_dump_counts(stderr);
-#endif
     /* dump hash stats */
     _PyHash_Fini();
 
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index 17e7960..1cb1030 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -1685,20 +1685,6 @@
     return _Py_GetAllocatedBlocks();
 }
 
-#ifdef COUNT_ALLOCS
-/*[clinic input]
-sys.getcounts
-[clinic start generated code]*/
-
-static PyObject *
-sys_getcounts_impl(PyObject *module)
-/*[clinic end generated code: output=20df00bc164f43cb input=ad2ec7bda5424953]*/
-{
-    extern PyObject *_Py_get_counts(void);
-
-    return _Py_get_counts();
-}
-#endif
 
 /*[clinic input]
 sys._getframe
@@ -1879,7 +1865,6 @@
     SYS_GETDEFAULTENCODING_METHODDEF
     SYS_GETDLOPENFLAGS_METHODDEF
     SYS_GETALLOCATEDBLOCKS_METHODDEF
-    SYS_GETCOUNTS_METHODDEF
 #ifdef DYNAMIC_EXECUTION_PROFILE
     {"getdxp",          _Py_GetDXProfile, METH_VARARGS},
 #endif