diff --git a/README.rst b/README.rst
index bba0fefbe..0ef3aad35 100644
--- a/README.rst
+++ b/README.rst
@@ -186,11 +186,8 @@ The `open` function also works as a context handler:
         ...
 
 .. warning::
-    The ``copy_arrays`` argument of `asdf.open()` and `AsdfFile` is deprecated,
-    and will be removed in ASDF 4.0. It is replaced by ``memmap``, which
-    is the opposite of ``copy_arrays`` (``memmap == not copy_arrays``).
-    In ASDF 4.0, ``memmap`` will default to ``False``, which means arrays
-    will no longer be memory-mapped by default.
+    The ``memmap`` argument replaces ``copy_arrays`` as of ASDF 4.0
+    (``memmap == not copy_arrays``).
 
 To get a quick overview of the data stored in the file, use the top-level
 `AsdfFile.info()` method:
diff --git a/asdf/_asdf.py b/asdf/_asdf.py
index 40b2971ac..41dac5bed 100644
--- a/asdf/_asdf.py
+++ b/asdf/_asdf.py
@@ -75,8 +75,7 @@ def __init__(
         ignore_version_mismatch=NotSet,
         ignore_unrecognized_tag=False,
         ignore_implicit_conversion=NotSet,
-        copy_arrays=NotSet,
-        memmap=NotSet,
+        memmap=True,
         lazy_load=True,
         custom_schema=None,
     ):
@@ -117,16 +116,9 @@ def __init__(
             case for this is currently ``namedtuple``, which cannot be serialized
             as-is.
 
-        copy_arrays : bool, optional
-            Deprecated; use ``memmap`` instead.
-            When `False`, when reading files, attempt to memmap underlying data
-            arrays when possible.
-
         memmap : bool, optional
             When `True`, when reading files, attempt to memmap underlying data
-            arrays when possible. When set, this argument will override
-            ``copy_arrays``. The default will change to ``False`` in an upcoming
-            ASDF version. At the moment the default is ``True``.
+            arrays when possible. Defaults to ``True``.
 
         lazy_load : bool, optional
             When `True` and the underlying file handle is seekable, data
@@ -184,16 +176,6 @@ def __init__(
         self._fd = None
         self._closed = False
         self._external_asdf_by_uri = {}
-        # if memmap is set, it overrides copy_arrays
-        if copy_arrays is not NotSet:
-            warnings.warn(
-                "copy_arrays is deprecated; use memmap instead. Note that memmap will default to False in asdf 4.0.",
-                AsdfWarning,
-            )
-            if memmap is NotSet:
-                memmap = not copy_arrays
-        elif memmap is NotSet:
-            memmap = True
         self._blocks = BlockManager(uri=uri, lazy_load=lazy_load, memmap=memmap)
         # this message is passed into find_references to only warn if
         # a reference was found
@@ -1628,8 +1610,7 @@ def open_asdf(
     ignore_version_mismatch=NotSet,
     ignore_unrecognized_tag=False,
     _force_raw_types=False,
-    copy_arrays=NotSet,
-    memmap=NotSet,
+    memmap=True,
     lazy_tree=NotSet,
     lazy_load=True,
     custom_schema=None,
@@ -1671,16 +1652,9 @@ def open_asdf(
         When `True`, do not raise warnings for unrecognized tags. Set to
         `False` by default.
 
-    copy_arrays : bool, optional
-        Deprecated; use ``memmap`` instead.
-        When `False`, when reading files, attempt to memmap underlying data
-        arrays when possible.
-
     memmap : bool, optional
         When `True`, when reading files, attempt to memmap underlying data
-        arrays when possible. When set, this argument will override
-        ``copy_arrays``. The default will change to ``False`` in an upcoming
-        ASDF version. At the moment the default is ``True``.
+        arrays when possible. Defaults to ``True``.
 
     lazy_load : bool, optional
         When `True` and the underlying file handle is seekable, data
@@ -1741,7 +1715,6 @@ def open_asdf(
     instance = AsdfFile(
         ignore_version_mismatch=ignore_version_mismatch,
         ignore_unrecognized_tag=ignore_unrecognized_tag,
-        copy_arrays=copy_arrays,
         memmap=memmap,
         lazy_load=lazy_load,
         custom_schema=custom_schema,
diff --git a/asdf/_tests/test_array_blocks.py b/asdf/_tests/test_array_blocks.py
index 32cb03491..e8d8cf3c4 100644
--- a/asdf/_tests/test_array_blocks.py
+++ b/asdf/_tests/test_array_blocks.py
@@ -814,22 +814,13 @@ def filename_with_array(tmp_path_factory):
         ({}, True),
         ({"memmap": True}, True),
         ({"memmap": False}, False),
-        ({"copy_arrays": True}, False),
-        ({"copy_arrays": False}, True),
-        ({"copy_arrays": True, "memmap": True}, True),
-        ({"copy_arrays": False, "memmap": True}, True),
-        ({"copy_arrays": True, "memmap": False}, False),
-        ({"copy_arrays": False, "memmap": False}, False),
     ],
 )
-@pytest.mark.filterwarnings("ignore:copy_arrays is deprecated")
 def test_open_no_memmap(filename_with_array, open_kwargs, should_memmap):
     """
     Test that asdf.open does not (or does) return memmaps for arrays
     depending on a number of arguments including:
         default (no kwargs)
-        copy_arrays
-        memmap (overwrites copy_arrays)
         memmap
     """
     with asdf.open(filename_with_array, lazy_load=False, **open_kwargs) as af:
diff --git a/asdf/_tests/test_deprecated.py b/asdf/_tests/test_deprecated.py
index 09a7a3a99..2a13151c3 100644
--- a/asdf/_tests/test_deprecated.py
+++ b/asdf/_tests/test_deprecated.py
@@ -6,7 +6,7 @@
 
 import asdf
 import asdf.testing.helpers
-from asdf.exceptions import AsdfDeprecationWarning, AsdfWarning, ValidationError
+from asdf.exceptions import AsdfDeprecationWarning, ValidationError
 
 
 def test_asdf_stream_deprecation():
@@ -148,18 +148,6 @@ def test_walk_and_modify_ignore_implicit_conversion_deprecation(value):
         asdf.treeutil.walk_and_modify({}, lambda obj: obj, ignore_implicit_conversion=value)
 
 
-@pytest.mark.parametrize("copy_arrays", [True, False])
-@pytest.mark.parametrize("memmap", [True, False, asdf._asdf.NotSet])
-def test_copy_arrays_deprecation(copy_arrays, memmap, tmp_path):
-    fn = tmp_path / "test.asdf"
-    af = asdf.AsdfFile()
-    af["a"] = 1
-    af.write_to(fn)
-    with pytest.warns(AsdfWarning, match="copy_arrays is deprecated; use memmap instead"):
-        with asdf.open(fn, copy_arrays=copy_arrays, memmap=memmap) as af:
-            pass
-
-
 @pytest.mark.parametrize("value", [True, False])
 def test_ignore_version_mismatch_deprecation(value):
     with pytest.warns(AsdfDeprecationWarning, match="ignore_version_mismatch is deprecated"):
diff --git a/changes/1800.removal.rst b/changes/1800.removal.rst
new file mode 100644
index 000000000..342b7822c
--- /dev/null
+++ b/changes/1800.removal.rst
@@ -0,0 +1 @@
+remove ``copy_arrays`` (replaced by ``memmap``)